File : xleaps.adb
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT SYSTEM UTILITIES --
4 -- --
5 -- X L E A P S --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 1992-2015, Free Software Foundation, Inc. --
10 -- --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
17 -- --
18 -- GNAT was originally developed by the GNAT team at New York University. --
19 -- Extensive contributions were provided by Ada Core Technologies Inc. --
20 -- --
21 ------------------------------------------------------------------------------
22
23 -- This utility is used to generate the hard time values of all existing
24 -- leap second occurrences. It uses several target dependent values taken
25 -- from a-calend.ads to perform its computations.
26
27 -- The algorithm behind this utility calculates the total number of days
28 -- up to a leap second occurrence from a target dependent origin of time.
29 -- The program then converts the resulting days into target dependent
30 -- time units used to create the aggregate.
31
32 -- Usage:
33
34 -- xleaps
35
36 -- Output files:
37
38 -- xleaps.txt
39
40 -- When a new leap second is introduced, the following steps must be carried
41 -- out:
42
43 -- 1) Increment Leap_Seconds_Count in a-calend.adb
44 -- 2) Increment LS_Count in xleaps.adb by one
45 -- 3) Add the new date to the aggregate of array LS_Dates in xleaps.adb
46 -- 4) Compile and execute xleaps for common targets
47 -- 5) Replace the values of array Leap_Second_Times in a-calend.adb with
48 -- the aggregate generated by xleaps
49 -- 6) Update the following tests under 0005-144:
50 -- cy90006.adb
51 -- cy90006_ls.adb
52 -- cy90010.adb
53 -- cy90010_ls.adb
54 -- c790016.adb
55 -- cy90016_ls.adb
56
57 with Ada.Calendar; use Ada.Calendar;
58 with Ada.Command_Line; use Ada.Command_Line;
59 with Ada.Text_IO; use Ada.Text_IO;
60
61 procedure XLeaps is
62
63 -- A general type used for calculating the hard time values of a leap
64 -- second occurrence. The type is explicitly declared to be large enough
65 -- and system independent.
66
67 type Units is range -2 ** 63 .. +2 ** 63 - 1;
68
69 -- Target specific units (currently all targets use nano-seconds)
70
71 Mili : constant := 10_000_000;
72 Milis_In_Day : constant := 864_000_000_000;
73 Nano : constant := 1_000_000_000;
74 Nanos_In_Day : constant := 86_400_000_000_000;
75
76 -- Origins of different time systems relative to a zero value. See
77 -- a-calend for more details.
78
79 Origin_Common : constant := -(61 * 366 + 188 * 365);
80
81 -- Various constants
82
83 Ada_Min_Year : constant := 1901;
84 Days_In_Four_Years : constant := 365 * 3 + 366;
85 Secs_In_Day : constant := 86_400;
86
87 Cumulative_Days_Before_Month :
88 constant array (Month_Number) of Units :=
89 (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
90
91 type LS_Date is record
92 Year : Year_Number;
93 Month : Month_Number;
94 Day : Day_Number;
95 end record;
96
97 LS_Count : constant := 25;
98 LS_Dates : constant array (1 .. LS_Count) of LS_Date :=
99 ((1972, 6, 30), (1972, 12, 31), (1973, 12, 31), (1974, 12, 31),
100 (1975, 12, 31), (1976, 12, 31), (1977, 12, 31), (1978, 12, 31),
101 (1979, 12, 31), (1981, 6, 30), (1982, 6, 30), (1983, 6, 30),
102 (1985, 6, 30), (1987, 12, 31), (1989, 12, 31), (1990, 12, 31),
103 (1992, 6, 30), (1993, 6, 30), (1994, 6, 30), (1995, 12, 31),
104 (1997, 6, 30), (1998, 12, 31), (2005, 12, 31), (2008, 12, 31),
105 (2012, 6, 30));
106
107 Days : Units;
108 Leap : LS_Date;
109 Origin : Units;
110 Result : Units;
111 Time_Units : Units;
112 Years : Natural;
113
114 -- All hard time values will be written to this file
115
116 F : File_Type;
117
118 function Is_Leap (Year : Year_Number) return Boolean;
119 -- Determine whether a given year is leap
120
121 procedure Usage;
122 -- Generate screen of usage information
123
124 -------------
125 -- Is_Leap --
126 -------------
127
128 function Is_Leap (Year : Year_Number) return Boolean is
129 begin
130 -- Leap centenial years
131
132 if Year mod 400 = 0 then
133 return True;
134
135 -- Non-leap centenial years
136
137 elsif Year mod 100 = 0 then
138 return False;
139
140 -- Regular years
141
142 else
143 return Year mod 4 = 0;
144 end if;
145 end Is_Leap;
146
147 -----------
148 -- Usage --
149 -----------
150
151 procedure Usage is
152 begin
153 Put_Line ("Usage : xleaps");
154 Put_Line ("Output: xleaps.txt");
155 end Usage;
156
157 -- Start of processing for XLeaps
158
159 begin
160 -- Determine the target and set the appropriate time origin and units
161
162 if Argument_Count = 0 then
163 Origin := Origin_Common;
164 Time_Units := Nano;
165 else
166 Usage;
167 end if;
168
169 Create (F, Out_File, "xleaps.txt");
170
171 for Index in 1 .. LS_Count loop
172 Leap := LS_Dates (Index);
173
174 -- Add the segments of four years expressed as days until the current
175 -- leap second occurrence. Note that non-leap centenial years are not
176 -- accounted for since there are no leap seconds after 2100.
177
178 Years := Leap.Year - Ada_Min_Year;
179 Days := Units (Years / 4) * Days_In_Four_Years;
180 Years := Years mod 4;
181
182 -- Add the remaining years as days
183
184 if Years = 1 then
185 Days := Days + 365;
186
187 elsif Years = 2 then
188 Days := Days + 365 * 2;
189
190 elsif Years = 3 then
191 Days := Days + 365 * 3;
192 end if;
193
194 -- Add all the days to the beginning of the current month. Handle
195 -- February 29 during leap years.
196
197 Days := Days + Cumulative_Days_Before_Month (Leap.Month);
198
199 if Is_Leap (Leap.Year)
200 and then Leap.Month > 2
201 then
202 Days := Days + 1;
203 end if;
204
205 -- Add all the days into the current month and shift the origin to the
206 -- target time system.
207
208 Days := Days + Units (Leap.Day) + Origin;
209
210 -- Finally, convert all days calculated above into seconds and add all
211 -- leap seconds that have occurred up to the current one (Index - 1).
212 -- The seconds are then converted to target-dependent units.
213
214 Result := (Days * Secs_In_Day + Units (Index - 1)) * Time_Units;
215
216 -- First line of the output file
217
218 if Index = 1 then
219 Put_Line (F, " (" & Result'Img & ",");
220
221 -- Last line of the output file
222
223 elsif Index = LS_Count then
224 Put_Line (F, " " & Result'Img & ");");
225
226 -- In between lines
227
228 else
229 Put_Line (F, " " & Result'Img & ",");
230 end if;
231 end loop;
232
233 Close (F);
234 end XLeaps;