1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- A D A . C A L E N D A R --
9 -- Copyright (C) 1992-2007, Free Software Foundation, Inc. --
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 2, 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. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
22 -- As a special exception, if other files instantiate generics from this --
23 -- unit, or you link this unit with other files to produce an executable, --
24 -- this unit does not by itself cause the resulting executable to be --
25 -- covered by the GNU General Public License. This exception does not --
26 -- however invalidate any other reasons why the executable file might be --
27 -- covered by the GNU Public License. --
29 -- GNAT was originally developed by the GNAT team at New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc. --
32 ------------------------------------------------------------------------------
34 with Ada.Unchecked_Conversion;
36 with System.OS_Primitives;
39 package body Ada.Calendar is
41 --------------------------
42 -- Implementation Notes --
43 --------------------------
45 -- In complex algorithms, some variables of type Ada.Calendar.Time carry
46 -- suffix _S or _N to denote units of seconds or nanoseconds.
48 -- Because time is measured in different units and from different origins
49 -- on various targets, a system independent model is incorporated into
50 -- Ada.Calendar. The idea behind the design is to encapsulate all target
51 -- dependent machinery in a single package, thus providing a uniform
52 -- interface to all existing and any potential children.
54 -- package Ada.Calendar
55 -- procedure Split (5 parameters) -------+
56 -- | Call from local routine
58 -- package Formatting_Operations |
59 -- procedure Split (11 parameters) <--+
60 -- end Formatting_Operations |
63 -- package Ada.Calendar.Formatting | Call from child routine
64 -- procedure Split (9 or 10 parameters) -+
65 -- end Ada.Calendar.Formatting
67 -- The behaviour of the interfacing routines is controlled via various
68 -- flags. All new Ada 2005 types from children of Ada.Calendar are
69 -- emulated by a similar type. For instance, type Day_Number is replaced
70 -- by Integer in various routines. One ramification of this model is that
71 -- the caller site must perform validity checks on returned results.
72 -- The end result of this model is the lack of target specific files per
73 -- child of Ada.Calendar (a-calfor, a-calfor-vms, a-calfor-vxwors, etc).
75 -----------------------
76 -- Local Subprograms --
77 -----------------------
79 procedure Check_Within_Time_Bounds (T : Time_Rep);
80 -- Ensure that a time representation value falls withing the bounds of Ada
81 -- time. Leap seconds support is taken into account.
83 procedure Cumulative_Leap_Seconds
84 (Start_Date : Time_Rep;
86 Elapsed_Leaps : out Natural;
87 Next_Leap : out Time_Rep);
88 -- Elapsed_Leaps is the sum of the leap seconds that have occured on or
89 -- after Start_Date and before (strictly before) End_Date. Next_Leap_Sec
90 -- represents the next leap second occurence on or after End_Date. If
91 -- there are no leaps seconds after End_Date, End_Of_Time is returned.
92 -- End_Of_Time can be used as End_Date to count all the leap seconds that
93 -- have occured on or after Start_Date.
95 -- Note: Any sub seconds of Start_Date and End_Date are discarded before
96 -- the calculations are done. For instance: if 113 seconds is a leap
97 -- second (it isn't) and 113.5 is input as an End_Date, the leap second
98 -- at 113 will not be counted in Leaps_Between, but it will be returned
99 -- as Next_Leap_Sec. Thus, if the caller wants to know if the End_Date is
100 -- a leap second, the comparison should be:
102 -- End_Date >= Next_Leap_Sec;
104 -- After_Last_Leap is designed so that this comparison works without
105 -- having to first check if Next_Leap_Sec is a valid leap second.
107 function Duration_To_Time_Rep is
108 new Ada.Unchecked_Conversion (Duration, Time_Rep);
109 -- Convert a duration value into a time representation value
111 function Time_Rep_To_Duration is
112 new Ada.Unchecked_Conversion (Time_Rep, Duration);
113 -- Convert a time representation value into a duration value
119 -- An integer time duration. The type is used whenever a positive elapsed
120 -- duration is needed, for instance when splitting a time value. Here is
121 -- how Time_Rep and Time_Dur are related:
123 -- 'First Ada_Low Ada_High 'Last
124 -- Time_Rep: +-------+------------------------+---------+
125 -- Time_Dur: +------------------------+---------+
128 type Time_Dur is range 0 .. 2 ** 63 - 1;
130 ---------------------
131 -- Local Constants --
132 ---------------------
134 -- Currently none of the GNAT targets support leap seconds. At some point
135 -- it might be necessary to query a C function to determine if the target
136 -- supports leap seconds, but for now this is deemed unnecessary.
138 Leap_Support : constant Boolean := False;
139 Leap_Seconds_Count : constant Natural := 23;
141 Ada_Min_Year : constant Year_Number := Year_Number'First;
142 Secs_In_Four_Years : constant := (3 * 365 + 366) * Secs_In_Day;
143 Secs_In_Non_Leap_Year : constant := 365 * Secs_In_Day;
145 -- Lower and upper bound of Ada time. The zero (0) value of type Time is
146 -- positioned at year 2150. Note that the lower and upper bound account
147 -- for the non-leap centenial years.
149 Ada_Low : constant Time_Rep := -(61 * 366 + 188 * 365) * Nanos_In_Day;
150 Ada_High : constant Time_Rep := (60 * 366 + 190 * 365) * Nanos_In_Day;
152 -- Even though the upper bound of time is 2399-12-31 23:59:59.999999999
153 -- UTC, it must be increased to include all leap seconds.
155 Ada_High_And_Leaps : constant Time_Rep :=
156 Ada_High + Time_Rep (Leap_Seconds_Count) * Nano;
158 -- Two constants used in the calculations of elapsed leap seconds.
159 -- End_Of_Time is later than Ada_High in time zone -28. Start_Of_Time
160 -- is earlier than Ada_Low in time zone +28.
162 End_Of_Time : constant Time_Rep :=
163 Ada_High + Time_Rep (3) * Nanos_In_Day;
164 Start_Of_Time : constant Time_Rep :=
165 Ada_Low - Time_Rep (3) * Nanos_In_Day;
167 -- The Unix lower time bound expressed as nanoseconds since the
168 -- start of Ada time in UTC.
170 Unix_Min : constant Time_Rep :=
171 Ada_Low + Time_Rep (17 * 366 + 52 * 365) * Nanos_In_Day;
173 Cumulative_Days_Before_Month :
174 constant array (Month_Number) of Natural :=
175 (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
177 Leap_Second_Times : array (1 .. Leap_Seconds_Count) of Time_Rep;
178 -- Each value represents a time value which is one second before a leap
179 -- second occurence. This table is populated during the elaboration of
186 function "+" (Left : Time; Right : Duration) return Time is
187 pragma Unsuppress (Overflow_Check);
188 Left_N : constant Time_Rep := Time_Rep (Left);
190 return Time (Left_N + Duration_To_Time_Rep (Right));
192 when Constraint_Error =>
196 function "+" (Left : Duration; Right : Time) return Time is
205 function "-" (Left : Time; Right : Duration) return Time is
206 pragma Unsuppress (Overflow_Check);
207 Left_N : constant Time_Rep := Time_Rep (Left);
209 return Time (Left_N - Duration_To_Time_Rep (Right));
211 when Constraint_Error =>
215 function "-" (Left : Time; Right : Time) return Duration is
216 pragma Unsuppress (Overflow_Check);
218 -- The bounds of type Duration expressed as time representations
220 Dur_Low : constant Time_Rep := Duration_To_Time_Rep (Duration'First);
221 Dur_High : constant Time_Rep := Duration_To_Time_Rep (Duration'Last);
226 Res_N := Time_Rep (Left) - Time_Rep (Right);
228 -- Due to the extended range of Ada time, "-" is capable of producing
229 -- results which may exceed the range of Duration. In order to prevent
230 -- the generation of bogus values by the Unchecked_Conversion, we apply
231 -- the following check.
234 or else Res_N > Dur_High
239 return Time_Rep_To_Duration (Res_N);
241 when Constraint_Error =>
249 function "<" (Left, Right : Time) return Boolean is
251 return Time_Rep (Left) < Time_Rep (Right);
258 function "<=" (Left, Right : Time) return Boolean is
260 return Time_Rep (Left) <= Time_Rep (Right);
267 function ">" (Left, Right : Time) return Boolean is
269 return Time_Rep (Left) > Time_Rep (Right);
276 function ">=" (Left, Right : Time) return Boolean is
278 return Time_Rep (Left) >= Time_Rep (Right);
281 ------------------------------
282 -- Check_Within_Time_Bounds --
283 ------------------------------
285 procedure Check_Within_Time_Bounds (T : Time_Rep) is
288 if T < Ada_Low or else T > Ada_High_And_Leaps then
292 if T < Ada_Low or else T > Ada_High then
296 end Check_Within_Time_Bounds;
302 function Clock return Time is
303 Elapsed_Leaps : Natural;
304 Next_Leap_N : Time_Rep;
306 -- The system clock returns the time in UTC since the Unix Epoch of
307 -- 1970-01-01 00:00:00.0. We perform an origin shift to the Ada Epoch
308 -- by adding the number of nanoseconds between the two origins.
311 Duration_To_Time_Rep (System.OS_Primitives.Clock) +
315 -- If the target supports leap seconds, determine the number of leap
316 -- seconds elapsed until this moment.
319 Cumulative_Leap_Seconds
320 (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
322 -- The system clock may fall exactly on a leap second
324 if Res_N >= Next_Leap_N then
325 Elapsed_Leaps := Elapsed_Leaps + 1;
328 -- The target does not support leap seconds
334 Res_N := Res_N + Time_Rep (Elapsed_Leaps) * Nano;
339 -----------------------------
340 -- Cumulative_Leap_Seconds --
341 -----------------------------
343 procedure Cumulative_Leap_Seconds
344 (Start_Date : Time_Rep;
346 Elapsed_Leaps : out Natural;
347 Next_Leap : out Time_Rep)
349 End_Index : Positive;
350 End_T : Time_Rep := End_Date;
351 Start_Index : Positive;
352 Start_T : Time_Rep := Start_Date;
355 -- Both input dates must be normalized to UTC
357 pragma Assert (Leap_Support and then End_Date >= Start_Date);
359 Next_Leap := End_Of_Time;
361 -- Make sure that the end date does not excede the upper bound
364 if End_Date > Ada_High then
368 -- Remove the sub seconds from both dates
370 Start_T := Start_T - (Start_T mod Nano);
371 End_T := End_T - (End_T mod Nano);
373 -- Some trivial cases:
374 -- Leap 1 . . . Leap N
375 -- ---+========+------+############+-------+========+-----
376 -- Start_T End_T Start_T End_T
378 if End_T < Leap_Second_Times (1) then
380 Next_Leap := Leap_Second_Times (1);
383 elsif Start_T > Leap_Second_Times (Leap_Seconds_Count) then
385 Next_Leap := End_Of_Time;
389 -- Perform the calculations only if the start date is within the leap
390 -- second occurences table.
392 if Start_T <= Leap_Second_Times (Leap_Seconds_Count) then
395 -- +----+----+-- . . . --+-------+---+
396 -- | T1 | T2 | | N - 1 | N |
397 -- +----+----+-- . . . --+-------+---+
399 -- | Start_Index | End_Index
400 -- +-------------------+
403 -- The idea behind the algorithm is to iterate and find two
404 -- closest dates which are after Start_T and End_T. Their
405 -- corresponding index difference denotes the number of leap
410 exit when Leap_Second_Times (Start_Index) >= Start_T;
411 Start_Index := Start_Index + 1;
414 End_Index := Start_Index;
416 exit when End_Index > Leap_Seconds_Count
417 or else Leap_Second_Times (End_Index) >= End_T;
418 End_Index := End_Index + 1;
421 if End_Index <= Leap_Seconds_Count then
422 Next_Leap := Leap_Second_Times (End_Index);
425 Elapsed_Leaps := End_Index - Start_Index;
430 end Cumulative_Leap_Seconds;
436 function Day (Date : Time) return Day_Number is
442 Split (Date, Y, M, D, S);
450 function Is_Leap (Year : Year_Number) return Boolean is
452 -- Leap centenial years
454 if Year mod 400 = 0 then
457 -- Non-leap centenial years
459 elsif Year mod 100 = 0 then
465 return Year mod 4 = 0;
473 function Month (Date : Time) return Month_Number is
479 Split (Date, Y, M, D, S);
487 function Seconds (Date : Time) return Day_Duration is
493 Split (Date, Y, M, D, S);
503 Year : out Year_Number;
504 Month : out Month_Number;
505 Day : out Day_Number;
506 Seconds : out Day_Duration)
515 -- Even though the input time zone is UTC (0), the flag Is_Ada_05 will
516 -- ensure that Split picks up the local time zone.
518 Formatting_Operations.Split
535 or else not Month'Valid
536 or else not Day'Valid
537 or else not Seconds'Valid
549 Month : Month_Number;
551 Seconds : Day_Duration := 0.0) return Time
553 -- The values in the following constants are irrelevant, they are just
554 -- placeholders; the choice of constructing a Day_Duration value is
555 -- controlled by the Use_Day_Secs flag.
557 H : constant Integer := 1;
558 M : constant Integer := 1;
559 Se : constant Integer := 1;
560 Ss : constant Duration := 0.1;
566 or else not Month'Valid
567 or else not Day'Valid
568 or else not Seconds'Valid
573 -- Even though the input time zone is UTC (0), the flag Is_Ada_05 will
574 -- ensure that Split picks up the local time zone.
577 Formatting_Operations.Time_Of
587 Use_Day_Secs => True,
596 function Year (Date : Time) return Year_Number is
602 Split (Date, Y, M, D, S);
606 -- The following packages assume that Time is a signed 64 bit integer
607 -- type, the units are nanoseconds and the origin is the start of Ada
608 -- time (1901-01-01 00:00:00.0 UTC).
610 ---------------------------
611 -- Arithmetic_Operations --
612 ---------------------------
614 package body Arithmetic_Operations is
620 function Add (Date : Time; Days : Long_Integer) return Time is
621 pragma Unsuppress (Overflow_Check);
622 Date_N : constant Time_Rep := Time_Rep (Date);
624 return Time (Date_N + Time_Rep (Days) * Nanos_In_Day);
626 when Constraint_Error =>
637 Days : out Long_Integer;
638 Seconds : out Duration;
639 Leap_Seconds : out Integer)
643 Elapsed_Leaps : Natural;
645 Negate : Boolean := False;
646 Next_Leap_N : Time_Rep;
648 Sub_Secs_Diff : Time_Rep;
651 -- Both input time values are assumed to be in UTC
653 if Left >= Right then
654 Later := Time_Rep (Left);
655 Earlier := Time_Rep (Right);
657 Later := Time_Rep (Right);
658 Earlier := Time_Rep (Left);
662 -- If the target supports leap seconds, process them
665 Cumulative_Leap_Seconds
666 (Earlier, Later, Elapsed_Leaps, Next_Leap_N);
668 if Later >= Next_Leap_N then
669 Elapsed_Leaps := Elapsed_Leaps + 1;
672 -- The target does not support leap seconds
678 -- Sub seconds processing. We add the resulting difference to one
679 -- of the input dates in order to account for any potential rounding
680 -- of the difference in the next step.
682 Sub_Secs_Diff := Later mod Nano - Earlier mod Nano;
683 Earlier := Earlier + Sub_Secs_Diff;
684 Sub_Secs := Duration (Sub_Secs_Diff) / Nano_F;
686 -- Difference processing. This operation should be able to calculate
687 -- the difference between opposite values which are close to the end
688 -- and start of Ada time. To accomodate the large range, we convert
689 -- to seconds. This action may potentially round the two values and
690 -- either add or drop a second. We compensate for this issue in the
694 Time_Dur (Later / Nano - Earlier / Nano) - Time_Dur (Elapsed_Leaps);
696 Days := Long_Integer (Res_Dur / Secs_In_Day);
697 Seconds := Duration (Res_Dur mod Secs_In_Day) + Sub_Secs;
698 Leap_Seconds := Integer (Elapsed_Leaps);
704 if Leap_Seconds /= 0 then
705 Leap_Seconds := -Leap_Seconds;
714 function Subtract (Date : Time; Days : Long_Integer) return Time is
715 pragma Unsuppress (Overflow_Check);
716 Date_N : constant Time_Rep := Time_Rep (Date);
718 return Time (Date_N - Time_Rep (Days) * Nanos_In_Day);
720 when Constraint_Error =>
723 end Arithmetic_Operations;
725 ----------------------
726 -- Delay_Operations --
727 ----------------------
729 package body Delays_Operations is
735 function To_Duration (Date : Time) return Duration is
736 Elapsed_Leaps : Natural;
737 Next_Leap_N : Time_Rep;
741 Res_N := Time_Rep (Date);
743 -- If the target supports leap seconds, remove any leap seconds
744 -- elapsed upto the input date.
747 Cumulative_Leap_Seconds
748 (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
750 -- The input time value may fall on a leap second occurence
752 if Res_N >= Next_Leap_N then
753 Elapsed_Leaps := Elapsed_Leaps + 1;
756 -- The target does not support leap seconds
762 Res_N := Res_N - Time_Rep (Elapsed_Leaps) * Nano;
764 -- Perform a shift in origins, note that enforcing type Time on
765 -- both operands will invoke Ada.Calendar."-".
767 return Time (Res_N) - Time (Unix_Min);
769 end Delays_Operations;
771 ---------------------------
772 -- Formatting_Operations --
773 ---------------------------
775 package body Formatting_Operations is
781 function Day_Of_Week (Date : Time) return Integer is
792 Day_Count : Long_Integer;
797 Formatting_Operations.Split
811 -- Build a time value in the middle of the same day
815 (Formatting_Operations.Time_Of
825 Use_Day_Secs => False,
829 -- Determine the elapsed seconds since the start of Ada time
831 Res_Dur := Time_Dur (Res_N / Nano - Ada_Low / Nano);
833 -- Count the number of days since the start of Ada time. 1901-1-1
834 -- GMT was a Tuesday.
836 Day_Count := Long_Integer (Res_Dur / Secs_In_Day) + 1;
838 return Integer (Day_Count mod 7);
847 Year : out Year_Number;
848 Month : out Month_Number;
849 Day : out Day_Number;
850 Day_Secs : out Day_Duration;
852 Minute : out Integer;
853 Second : out Integer;
854 Sub_Sec : out Duration;
855 Leap_Sec : out Boolean;
857 Time_Zone : Long_Integer)
859 -- The following constants represent the number of nanoseconds
860 -- elapsed since the start of Ada time to and including the non
861 -- leap centenial years.
863 Year_2101 : constant Time_Rep := Ada_Low +
864 Time_Rep (49 * 366 + 151 * 365) * Nanos_In_Day;
865 Year_2201 : constant Time_Rep := Ada_Low +
866 Time_Rep (73 * 366 + 227 * 365) * Nanos_In_Day;
867 Year_2301 : constant Time_Rep := Ada_Low +
868 Time_Rep (97 * 366 + 303 * 365) * Nanos_In_Day;
872 Day_Seconds : Natural;
873 Elapsed_Leaps : Natural;
874 Four_Year_Segs : Natural;
875 Hour_Seconds : Natural;
876 Is_Leap_Year : Boolean;
877 Next_Leap_N : Time_Rep;
879 Sub_Sec_N : Time_Rep;
883 Date_N := Time_Rep (Date);
885 -- Step 1: Leap seconds processing in UTC
888 Cumulative_Leap_Seconds
889 (Start_Of_Time, Date_N, Elapsed_Leaps, Next_Leap_N);
891 Leap_Sec := Date_N >= Next_Leap_N;
894 Elapsed_Leaps := Elapsed_Leaps + 1;
897 -- The target does not support leap seconds
904 Date_N := Date_N - Time_Rep (Elapsed_Leaps) * Nano;
906 -- Step 2: Time zone processing. This action converts the input date
907 -- from GMT to the requested time zone.
910 if Time_Zone /= 0 then
911 Date_N := Date_N + Time_Rep (Time_Zone) * 60 * Nano;
918 Off : constant Long_Integer :=
919 Time_Zones_Operations.UTC_Time_Offset (Time (Date_N));
921 Date_N := Date_N + Time_Rep (Off) * Nano;
925 -- Step 3: Non-leap centenial year adjustment in local time zone
927 -- In order for all divisions to work properly and to avoid more
928 -- complicated arithmetic, we add fake Febriary 29s to dates which
929 -- occur after a non-leap centenial year.
931 if Date_N >= Year_2301 then
932 Date_N := Date_N + Time_Rep (3) * Nanos_In_Day;
934 elsif Date_N >= Year_2201 then
935 Date_N := Date_N + Time_Rep (2) * Nanos_In_Day;
937 elsif Date_N >= Year_2101 then
938 Date_N := Date_N + Time_Rep (1) * Nanos_In_Day;
941 -- Step 4: Sub second processing in local time zone
943 Sub_Sec_N := Date_N mod Nano;
944 Sub_Sec := Duration (Sub_Sec_N) / Nano_F;
945 Date_N := Date_N - Sub_Sec_N;
947 -- Convert Date_N into a time duration value, changing the units
950 Date_Dur := Time_Dur (Date_N / Nano - Ada_Low / Nano);
952 -- Step 5: Year processing in local time zone. Determine the number
953 -- of four year segments since the start of Ada time and the input
956 Four_Year_Segs := Natural (Date_Dur / Secs_In_Four_Years);
958 if Four_Year_Segs > 0 then
959 Date_Dur := Date_Dur - Time_Dur (Four_Year_Segs) *
963 -- Calculate the remaining non-leap years
965 Rem_Years := Natural (Date_Dur / Secs_In_Non_Leap_Year);
967 if Rem_Years > 3 then
971 Date_Dur := Date_Dur - Time_Dur (Rem_Years) * Secs_In_Non_Leap_Year;
973 Year := Ada_Min_Year + Natural (4 * Four_Year_Segs + Rem_Years);
974 Is_Leap_Year := Is_Leap (Year);
976 -- Step 6: Month and day processing in local time zone
978 Year_Day := Natural (Date_Dur / Secs_In_Day) + 1;
982 -- Processing for months after January
984 if Year_Day > 31 then
986 Year_Day := Year_Day - 31;
988 -- Processing for a new month or a leap February
991 and then (not Is_Leap_Year or else Year_Day > 29)
994 Year_Day := Year_Day - 28;
997 Year_Day := Year_Day - 1;
1002 while Year_Day > Days_In_Month (Month) loop
1003 Year_Day := Year_Day - Days_In_Month (Month);
1009 -- Step 7: Hour, minute, second and sub second processing in local
1012 Day := Day_Number (Year_Day);
1013 Day_Seconds := Integer (Date_Dur mod Secs_In_Day);
1014 Day_Secs := Duration (Day_Seconds) + Sub_Sec;
1015 Hour := Day_Seconds / 3_600;
1016 Hour_Seconds := Day_Seconds mod 3_600;
1017 Minute := Hour_Seconds / 60;
1018 Second := Hour_Seconds mod 60;
1026 (Year : Year_Number;
1027 Month : Month_Number;
1029 Day_Secs : Day_Duration;
1035 Use_Day_Secs : Boolean;
1036 Is_Ada_05 : Boolean;
1037 Time_Zone : Long_Integer) return Time
1040 Elapsed_Leaps : Natural;
1041 Next_Leap_N : Time_Rep;
1043 Rounded_Res_N : Time_Rep;
1046 -- Step 1: Check whether the day, month and year form a valid date
1048 if Day > Days_In_Month (Month)
1049 and then (Day /= 29 or else Month /= 2 or else not Is_Leap (Year))
1054 -- Start accumulating nanoseconds from the low bound of Ada time
1058 -- Step 2: Year processing and centenial year adjustment. Determine
1059 -- the number of four year segments since the start of Ada time and
1062 Count := (Year - Year_Number'First) / 4;
1063 Res_N := Res_N + Time_Rep (Count) * Secs_In_Four_Years * Nano;
1065 -- Note that non-leap centenial years are automatically considered
1066 -- leap in the operation above. An adjustment of several days is
1067 -- required to compensate for this.
1070 Res_N := Res_N - Time_Rep (3) * Nanos_In_Day;
1072 elsif Year > 2200 then
1073 Res_N := Res_N - Time_Rep (2) * Nanos_In_Day;
1075 elsif Year > 2100 then
1076 Res_N := Res_N - Time_Rep (1) * Nanos_In_Day;
1079 -- Add the remaining non-leap years
1081 Count := (Year - Year_Number'First) mod 4;
1082 Res_N := Res_N + Time_Rep (Count) * Secs_In_Non_Leap_Year * Nano;
1084 -- Step 3: Day of month processing. Determine the number of days
1085 -- since the start of the current year. Do not add the current
1086 -- day since it has not elapsed yet.
1088 Count := Cumulative_Days_Before_Month (Month) + Day - 1;
1090 -- The input year is leap and we have passed February
1098 Res_N := Res_N + Time_Rep (Count) * Nanos_In_Day;
1100 -- Step 4: Hour, minute, second and sub second processing
1102 if Use_Day_Secs then
1103 Res_N := Res_N + Duration_To_Time_Rep (Day_Secs);
1107 Time_Rep (Hour * 3_600 + Minute * 60 + Second) * Nano;
1109 if Sub_Sec = 1.0 then
1110 Res_N := Res_N + Time_Rep (1) * Nano;
1112 Res_N := Res_N + Duration_To_Time_Rep (Sub_Sec);
1116 -- At this point, the generated time value should be withing the
1117 -- bounds of Ada time.
1119 Check_Within_Time_Bounds (Res_N);
1121 -- Step 4: Time zone processing. At this point we have built an
1122 -- arbitrary time value which is not related to any time zone.
1123 -- For simplicity, the time value is normalized to GMT, producing
1124 -- a uniform representation which can be treated by arithmetic
1125 -- operations for instance without any additional corrections.
1128 if Time_Zone /= 0 then
1129 Res_N := Res_N - Time_Rep (Time_Zone) * 60 * Nano;
1136 Current_Off : constant Long_Integer :=
1137 Time_Zones_Operations.UTC_Time_Offset
1139 Current_Res_N : constant Time_Rep :=
1140 Res_N - Time_Rep (Current_Off) * Nano;
1141 Off : constant Long_Integer :=
1142 Time_Zones_Operations.UTC_Time_Offset
1143 (Time (Current_Res_N));
1145 Res_N := Res_N - Time_Rep (Off) * Nano;
1149 -- Step 5: Leap seconds processing in GMT
1151 if Leap_Support then
1152 Cumulative_Leap_Seconds
1153 (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
1155 Res_N := Res_N + Time_Rep (Elapsed_Leaps) * Nano;
1157 -- An Ada 2005 caller requesting an explicit leap second or an
1158 -- Ada 95 caller accounting for an invisible leap second.
1161 or else Res_N >= Next_Leap_N
1163 Res_N := Res_N + Time_Rep (1) * Nano;
1166 -- Leap second validity check
1168 Rounded_Res_N := Res_N - (Res_N mod Nano);
1172 and then Rounded_Res_N /= Next_Leap_N
1178 return Time (Res_N);
1180 end Formatting_Operations;
1182 ---------------------------
1183 -- Time_Zones_Operations --
1184 ---------------------------
1186 package body Time_Zones_Operations is
1188 -- The Unix time bounds in nanoseconds: 1970/1/1 .. 2037/1/1
1190 Unix_Min : constant Time_Rep := Ada_Low +
1191 Time_Rep (17 * 366 + 52 * 365) * Nanos_In_Day;
1193 Unix_Max : constant Time_Rep := Ada_Low +
1194 Time_Rep (34 * 366 + 102 * 365) * Nanos_In_Day +
1195 Time_Rep (Leap_Seconds_Count) * Nano;
1197 -- The following constants denote February 28 during non-leap
1198 -- centenial years, the units are nanoseconds.
1200 T_2100_2_28 : constant Time_Rep := Ada_Low +
1201 (Time_Rep (49 * 366 + 150 * 365 + 59) * Secs_In_Day +
1202 Time_Rep (Leap_Seconds_Count)) * Nano;
1204 T_2200_2_28 : constant Time_Rep := Ada_Low +
1205 (Time_Rep (73 * 366 + 226 * 365 + 59) * Secs_In_Day +
1206 Time_Rep (Leap_Seconds_Count)) * Nano;
1208 T_2300_2_28 : constant Time_Rep := Ada_Low +
1209 (Time_Rep (97 * 366 + 302 * 365 + 59) * Secs_In_Day +
1210 Time_Rep (Leap_Seconds_Count)) * Nano;
1212 -- 56 years (14 leap years + 42 non leap years) in nanoseconds:
1214 Nanos_In_56_Years : constant := (14 * 366 + 42 * 365) * Nanos_In_Day;
1216 -- Base C types. There is no point dragging in Interfaces.C just for
1217 -- these four types.
1219 type char_Pointer is access Character;
1220 subtype int is Integer;
1221 subtype long is Long_Integer;
1222 type long_Pointer is access all long;
1224 -- The Ada equivalent of struct tm and type time_t
1227 tm_sec : int; -- seconds after the minute (0 .. 60)
1228 tm_min : int; -- minutes after the hour (0 .. 59)
1229 tm_hour : int; -- hours since midnight (0 .. 24)
1230 tm_mday : int; -- day of the month (1 .. 31)
1231 tm_mon : int; -- months since January (0 .. 11)
1232 tm_year : int; -- years since 1900
1233 tm_wday : int; -- days since Sunday (0 .. 6)
1234 tm_yday : int; -- days since January 1 (0 .. 365)
1235 tm_isdst : int; -- Daylight Savings Time flag (-1 .. 1)
1236 tm_gmtoff : long; -- offset from UTC in seconds
1237 tm_zone : char_Pointer; -- timezone abbreviation
1240 type tm_Pointer is access all tm;
1242 subtype time_t is long;
1243 type time_t_Pointer is access all time_t;
1245 procedure localtime_tzoff
1246 (C : time_t_Pointer;
1248 off : long_Pointer);
1249 pragma Import (C, localtime_tzoff, "__gnat_localtime_tzoff");
1250 -- This is a lightweight wrapper around the system library function
1251 -- localtime_r. Parameter 'off' captures the UTC offset which is either
1252 -- retrieved from the tm struct or calculated from the 'timezone' extern
1253 -- and the tm_isdst flag in the tm struct.
1255 ---------------------
1256 -- UTC_Time_Offset --
1257 ---------------------
1259 function UTC_Time_Offset (Date : Time) return Long_Integer is
1260 Adj_Cent : Integer := 0;
1262 Offset : aliased long;
1263 Secs_T : aliased time_t;
1264 Secs_TM : aliased tm;
1267 Date_N := Time_Rep (Date);
1269 -- Dates which are 56 years appart fall on the same day, day light
1270 -- saving and so on. Non-leap centenial years violate this rule by
1271 -- one day and as a consequence, special adjustment is needed.
1273 if Date_N > T_2100_2_28 then
1274 if Date_N > T_2200_2_28 then
1275 if Date_N > T_2300_2_28 then
1286 if Adj_Cent > 0 then
1287 Date_N := Date_N - Time_Rep (Adj_Cent) * Nanos_In_Day;
1290 -- Shift the date within bounds of Unix time
1292 while Date_N < Unix_Min loop
1293 Date_N := Date_N + Nanos_In_56_Years;
1296 while Date_N >= Unix_Max loop
1297 Date_N := Date_N - Nanos_In_56_Years;
1300 -- Perform a shift in origins from Ada to Unix
1302 Date_N := Date_N - Unix_Min;
1304 -- Convert the date into seconds
1306 Secs_T := time_t (Date_N / Nano);
1309 (Secs_T'Unchecked_Access,
1310 Secs_TM'Unchecked_Access,
1311 Offset'Unchecked_Access);
1314 end UTC_Time_Offset;
1315 end Time_Zones_Operations;
1317 -- Start of elaboration code for Ada.Calendar
1320 System.OS_Primitives.Initialize;
1322 -- Population of the leap seconds table
1324 if Leap_Support then
1326 type Leap_Second_Date is record
1328 Month : Month_Number;
1333 constant array (1 .. Leap_Seconds_Count) of Leap_Second_Date :=
1334 ((1972, 6, 30), (1972, 12, 31), (1973, 12, 31), (1974, 12, 31),
1335 (1975, 12, 31), (1976, 12, 31), (1977, 12, 31), (1978, 12, 31),
1336 (1979, 12, 31), (1981, 6, 30), (1982, 6, 30), (1983, 6, 30),
1337 (1985, 6, 30), (1987, 12, 31), (1989, 12, 31), (1990, 12, 31),
1338 (1992, 6, 30), (1993, 6, 30), (1994, 6, 30), (1995, 12, 31),
1339 (1997, 6, 30), (1998, 12, 31), (2005, 12, 31));
1341 Days_In_Four_Years : constant := 365 * 3 + 366;
1344 Leap : Leap_Second_Date;
1348 for Index in 1 .. Leap_Seconds_Count loop
1349 Leap := Leap_Second_Dates (Index);
1351 -- Calculate the number of days from the start of Ada time until
1352 -- the current leap second occurence. Non-leap centenial years
1353 -- are not accounted for in these calculations since there are
1354 -- no leap seconds after 2100 yet.
1356 Years := Leap.Year - Ada_Min_Year;
1357 Days := (Years / 4) * Days_In_Four_Years;
1358 Years := Years mod 4;
1363 elsif Years = 2 then
1364 Days := Days + 365 * 2;
1366 elsif Years = 3 then
1367 Days := Days + 365 * 3;
1370 Days := Days + Cumulative_Days_Before_Month (Leap.Month);
1372 if Is_Leap (Leap.Year)
1373 and then Leap.Month > 2
1378 Days := Days + Leap.Day;
1380 -- Index - 1 previous leap seconds are added to Time (Index) as
1381 -- well as the lower buffer for time zones.
1383 Leap_Second_Times (Index) := Ada_Low +
1384 (Time_Rep (Days) * Secs_In_Day + Time_Rep (Index - 1)) * Nano;