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);
189 Left_N : constant Time_Rep := Time_Rep (Left);
195 if Right = Duration (0.0) then
199 Res_N := Left_N + Duration_To_Time_Rep (Right);
201 Check_Within_Time_Bounds (Res_N);
206 when Constraint_Error =>
210 function "+" (Left : Duration; Right : Time) return Time is
219 function "-" (Left : Time; Right : Duration) return Time is
220 pragma Unsuppress (Overflow_Check);
222 Left_N : constant Time_Rep := Time_Rep (Left);
228 if Right = Duration (0.0) then
232 Res_N := Left_N - Duration_To_Time_Rep (Right);
234 Check_Within_Time_Bounds (Res_N);
239 when Constraint_Error =>
243 function "-" (Left : Time; Right : Time) return Duration is
244 pragma Unsuppress (Overflow_Check);
246 -- The bounds of type Duration expressed as time representations
248 Dur_Low : constant Time_Rep := Duration_To_Time_Rep (Duration'First);
249 Dur_High : constant Time_Rep := Duration_To_Time_Rep (Duration'Last);
254 Res_N := Time_Rep (Left) - Time_Rep (Right);
256 -- The result does not fit in a duration value
259 or else Res_N > Dur_High
264 return Time_Rep_To_Duration (Res_N);
266 when Constraint_Error =>
274 function "<" (Left, Right : Time) return Boolean is
276 return Time_Rep (Left) < Time_Rep (Right);
283 function "<=" (Left, Right : Time) return Boolean is
285 return Time_Rep (Left) <= Time_Rep (Right);
292 function ">" (Left, Right : Time) return Boolean is
294 return Time_Rep (Left) > Time_Rep (Right);
301 function ">=" (Left, Right : Time) return Boolean is
303 return Time_Rep (Left) >= Time_Rep (Right);
306 ------------------------------
307 -- Check_Within_Time_Bounds --
308 ------------------------------
310 procedure Check_Within_Time_Bounds (T : Time_Rep) is
313 if T < Ada_Low or else T > Ada_High_And_Leaps then
317 if T < Ada_Low or else T > Ada_High then
321 end Check_Within_Time_Bounds;
327 function Clock return Time is
328 Elapsed_Leaps : Natural;
329 Next_Leap_N : Time_Rep;
331 -- The system clock returns the time in UTC since the Unix Epoch of
332 -- 1970-01-01 00:00:00.0. We perform an origin shift to the Ada Epoch
333 -- by adding the number of nanoseconds between the two origins.
336 Duration_To_Time_Rep (System.OS_Primitives.Clock) +
340 -- If the target supports leap seconds, determine the number of leap
341 -- seconds elapsed until this moment.
344 Cumulative_Leap_Seconds
345 (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
347 -- The system clock may fall exactly on a leap second
349 if Res_N >= Next_Leap_N then
350 Elapsed_Leaps := Elapsed_Leaps + 1;
353 -- The target does not support leap seconds
359 Res_N := Res_N + Time_Rep (Elapsed_Leaps) * Nano;
364 -----------------------------
365 -- Cumulative_Leap_Seconds --
366 -----------------------------
368 procedure Cumulative_Leap_Seconds
369 (Start_Date : Time_Rep;
371 Elapsed_Leaps : out Natural;
372 Next_Leap : out Time_Rep)
374 End_Index : Positive;
375 End_T : Time_Rep := End_Date;
376 Start_Index : Positive;
377 Start_T : Time_Rep := Start_Date;
380 -- Both input dates must be normalized to UTC
382 pragma Assert (Leap_Support and then End_Date >= Start_Date);
384 Next_Leap := End_Of_Time;
386 -- Make sure that the end date does not excede the upper bound
389 if End_Date > Ada_High then
393 -- Remove the sub seconds from both dates
395 Start_T := Start_T - (Start_T mod Nano);
396 End_T := End_T - (End_T mod Nano);
398 -- Some trivial cases:
399 -- Leap 1 . . . Leap N
400 -- ---+========+------+############+-------+========+-----
401 -- Start_T End_T Start_T End_T
403 if End_T < Leap_Second_Times (1) then
405 Next_Leap := Leap_Second_Times (1);
408 elsif Start_T > Leap_Second_Times (Leap_Seconds_Count) then
410 Next_Leap := End_Of_Time;
414 -- Perform the calculations only if the start date is within the leap
415 -- second occurences table.
417 if Start_T <= Leap_Second_Times (Leap_Seconds_Count) then
420 -- +----+----+-- . . . --+-------+---+
421 -- | T1 | T2 | | N - 1 | N |
422 -- +----+----+-- . . . --+-------+---+
424 -- | Start_Index | End_Index
425 -- +-------------------+
428 -- The idea behind the algorithm is to iterate and find two
429 -- closest dates which are after Start_T and End_T. Their
430 -- corresponding index difference denotes the number of leap
435 exit when Leap_Second_Times (Start_Index) >= Start_T;
436 Start_Index := Start_Index + 1;
439 End_Index := Start_Index;
441 exit when End_Index > Leap_Seconds_Count
442 or else Leap_Second_Times (End_Index) >= End_T;
443 End_Index := End_Index + 1;
446 if End_Index <= Leap_Seconds_Count then
447 Next_Leap := Leap_Second_Times (End_Index);
450 Elapsed_Leaps := End_Index - Start_Index;
455 end Cumulative_Leap_Seconds;
461 function Day (Date : Time) return Day_Number is
467 Split (Date, Y, M, D, S);
475 function Is_Leap (Year : Year_Number) return Boolean is
477 -- Leap centenial years
479 if Year mod 400 = 0 then
482 -- Non-leap centenial years
484 elsif Year mod 100 = 0 then
490 return Year mod 4 = 0;
498 function Month (Date : Time) return Month_Number is
504 Split (Date, Y, M, D, S);
512 function Seconds (Date : Time) return Day_Duration is
518 Split (Date, Y, M, D, S);
528 Year : out Year_Number;
529 Month : out Month_Number;
530 Day : out Day_Number;
531 Seconds : out Day_Duration)
540 -- Even though the input time zone is UTC (0), the flag Is_Ada_05 will
541 -- ensure that Split picks up the local time zone.
543 Formatting_Operations.Split
560 or else not Month'Valid
561 or else not Day'Valid
562 or else not Seconds'Valid
574 Month : Month_Number;
576 Seconds : Day_Duration := 0.0) return Time
578 -- The values in the following constants are irrelevant, they are just
579 -- placeholders; the choice of constructing a Day_Duration value is
580 -- controlled by the Use_Day_Secs flag.
582 H : constant Integer := 1;
583 M : constant Integer := 1;
584 Se : constant Integer := 1;
585 Ss : constant Duration := 0.1;
591 or else not Month'Valid
592 or else not Day'Valid
593 or else not Seconds'Valid
598 -- Even though the input time zone is UTC (0), the flag Is_Ada_05 will
599 -- ensure that Split picks up the local time zone.
602 Formatting_Operations.Time_Of
612 Use_Day_Secs => True,
621 function Year (Date : Time) return Year_Number is
627 Split (Date, Y, M, D, S);
631 -- The following packages assume that Time is a signed 64 bit integer
632 -- type, the units are nanoseconds and the origin is the start of Ada
633 -- time (1901-01-01 00:00:00.0 UTC).
635 ---------------------------
636 -- Arithmetic_Operations --
637 ---------------------------
639 package body Arithmetic_Operations is
645 function Add (Date : Time; Days : Long_Integer) return Time is
646 pragma Unsuppress (Overflow_Check);
648 Date_N : constant Time_Rep := Time_Rep (Date);
658 Res_N := Date_N + Time_Rep (Days) * Nanos_In_Day;
660 Check_Within_Time_Bounds (Res_N);
665 when Constraint_Error =>
676 Days : out Long_Integer;
677 Seconds : out Duration;
678 Leap_Seconds : out Integer)
682 Earlier_Sub : Time_Rep;
683 Elapsed_Leaps : Natural;
685 Later_Sub : Time_Rep;
686 Negate : Boolean := False;
687 Next_Leap_N : Time_Rep;
688 Sub_Seconds : Duration;
691 -- Both input time values are assumed to be in UTC
693 if Left >= Right then
694 Later := Time_Rep (Left);
695 Earlier := Time_Rep (Right);
697 Later := Time_Rep (Right);
698 Earlier := Time_Rep (Left);
702 -- If the target supports leap seconds, process them
705 Cumulative_Leap_Seconds
706 (Earlier, Later, Elapsed_Leaps, Next_Leap_N);
708 if Later >= Next_Leap_N then
709 Elapsed_Leaps := Elapsed_Leaps + 1;
712 -- The target does not support leap seconds
720 Earlier_Sub := Earlier mod Nano;
721 Later_Sub := Later mod Nano;
723 if Later_Sub < Earlier_Sub then
724 Later_Sub := Later_Sub + Time_Rep (1) * Nano;
725 Later := Later - Time_Rep (1) * Nano;
728 Sub_Seconds := Duration (Later_Sub - Earlier_Sub) / Nano_F;
730 Res_Dur := Time_Dur (Later / Nano - Earlier / Nano) -
731 Time_Dur (Elapsed_Leaps);
733 Days := Long_Integer (Res_Dur / Secs_In_Day);
734 Seconds := Duration (Res_Dur mod Secs_In_Day) + Sub_Seconds;
735 Leap_Seconds := Integer (Elapsed_Leaps);
741 if Leap_Seconds /= 0 then
742 Leap_Seconds := -Leap_Seconds;
751 function Subtract (Date : Time; Days : Long_Integer) return Time is
752 pragma Unsuppress (Overflow_Check);
754 Date_N : constant Time_Rep := Time_Rep (Date);
764 Res_N := Date_N - Time_Rep (Days) * Nanos_In_Day;
766 Check_Within_Time_Bounds (Res_N);
771 when Constraint_Error =>
774 end Arithmetic_Operations;
776 ----------------------
777 -- Delay_Operations --
778 ----------------------
780 package body Delays_Operations is
786 function To_Duration (Date : Time) return Duration is
787 Elapsed_Leaps : Natural;
788 Next_Leap_N : Time_Rep;
792 Res_N := Time_Rep (Date);
794 -- If the target supports leap seconds, remove any leap seconds
795 -- elapsed upto the input date.
798 Cumulative_Leap_Seconds
799 (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
801 -- The input time value may fall on a leap second occurence
803 if Res_N >= Next_Leap_N then
804 Elapsed_Leaps := Elapsed_Leaps + 1;
807 -- The target does not support leap seconds
813 Res_N := Res_N - Time_Rep (Elapsed_Leaps) * Nano;
815 -- Perform a shift in origins, note that enforcing type Time on
816 -- both operands will invoke Ada.Calendar."-".
818 return Time (Res_N) - Time (Unix_Min);
820 end Delays_Operations;
822 ---------------------------
823 -- Formatting_Operations --
824 ---------------------------
826 package body Formatting_Operations is
832 function Day_Of_Week (Date : Time) return Integer is
843 Day_Count : Long_Integer;
848 Formatting_Operations.Split
862 -- Build a time value in the middle of the same day
866 (Formatting_Operations.Time_Of
876 Use_Day_Secs => False,
880 -- Determine the elapsed seconds since the start of Ada time
882 Res_Dur := Time_Dur (Res_N / Nano - Ada_Low / Nano);
884 -- Count the number of days since the start of Ada time. 1901-1-1
885 -- GMT was a Tuesday.
887 Day_Count := Long_Integer (Res_Dur / Secs_In_Day) + 1;
889 return Integer (Day_Count mod 7);
898 Year : out Year_Number;
899 Month : out Month_Number;
900 Day : out Day_Number;
901 Day_Secs : out Day_Duration;
903 Minute : out Integer;
904 Second : out Integer;
905 Sub_Sec : out Duration;
906 Leap_Sec : out Boolean;
908 Time_Zone : Long_Integer)
910 -- The following constants represent the number of nanoseconds
911 -- elapsed since the start of Ada time to and including the non
912 -- leap centenial years.
914 Year_2101 : constant Time_Rep := Ada_Low +
915 Time_Rep (49 * 366 + 151 * 365) * Nanos_In_Day;
916 Year_2201 : constant Time_Rep := Ada_Low +
917 Time_Rep (73 * 366 + 227 * 365) * Nanos_In_Day;
918 Year_2301 : constant Time_Rep := Ada_Low +
919 Time_Rep (97 * 366 + 303 * 365) * Nanos_In_Day;
923 Day_Seconds : Natural;
924 Elapsed_Leaps : Natural;
925 Four_Year_Segs : Natural;
926 Hour_Seconds : Natural;
927 Is_Leap_Year : Boolean;
928 Next_Leap_N : Time_Rep;
930 Sub_Sec_N : Time_Rep;
934 Date_N := Time_Rep (Date);
936 -- Step 1: Leap seconds processing in UTC
939 Cumulative_Leap_Seconds
940 (Start_Of_Time, Date_N, Elapsed_Leaps, Next_Leap_N);
942 Leap_Sec := Date_N >= Next_Leap_N;
945 Elapsed_Leaps := Elapsed_Leaps + 1;
948 -- The target does not support leap seconds
955 Date_N := Date_N - Time_Rep (Elapsed_Leaps) * Nano;
957 -- Step 2: Time zone processing. This action converts the input date
958 -- from GMT to the requested time zone.
961 if Time_Zone /= 0 then
962 Date_N := Date_N + Time_Rep (Time_Zone) * 60 * Nano;
969 Off : constant Long_Integer :=
970 Time_Zones_Operations.UTC_Time_Offset (Time (Date_N));
972 Date_N := Date_N + Time_Rep (Off) * Nano;
976 -- Step 3: Non-leap centenial year adjustment in local time zone
978 -- In order for all divisions to work properly and to avoid more
979 -- complicated arithmetic, we add fake Febriary 29s to dates which
980 -- occur after a non-leap centenial year.
982 if Date_N >= Year_2301 then
983 Date_N := Date_N + Time_Rep (3) * Nanos_In_Day;
985 elsif Date_N >= Year_2201 then
986 Date_N := Date_N + Time_Rep (2) * Nanos_In_Day;
988 elsif Date_N >= Year_2101 then
989 Date_N := Date_N + Time_Rep (1) * Nanos_In_Day;
992 -- Step 4: Sub second processing in local time zone
994 Sub_Sec_N := Date_N mod Nano;
995 Sub_Sec := Duration (Sub_Sec_N) / Nano_F;
996 Date_N := Date_N - Sub_Sec_N;
998 -- Convert Date_N into a time duration value, changing the units
1001 Date_Dur := Time_Dur (Date_N / Nano - Ada_Low / Nano);
1003 -- Step 5: Year processing in local time zone. Determine the number
1004 -- of four year segments since the start of Ada time and the input
1007 Four_Year_Segs := Natural (Date_Dur / Secs_In_Four_Years);
1009 if Four_Year_Segs > 0 then
1010 Date_Dur := Date_Dur - Time_Dur (Four_Year_Segs) *
1014 -- Calculate the remaining non-leap years
1016 Rem_Years := Natural (Date_Dur / Secs_In_Non_Leap_Year);
1018 if Rem_Years > 3 then
1022 Date_Dur := Date_Dur - Time_Dur (Rem_Years) * Secs_In_Non_Leap_Year;
1024 Year := Ada_Min_Year + Natural (4 * Four_Year_Segs + Rem_Years);
1025 Is_Leap_Year := Is_Leap (Year);
1027 -- Step 6: Month and day processing in local time zone
1029 Year_Day := Natural (Date_Dur / Secs_In_Day) + 1;
1033 -- Processing for months after January
1035 if Year_Day > 31 then
1037 Year_Day := Year_Day - 31;
1039 -- Processing for a new month or a leap February
1042 and then (not Is_Leap_Year or else Year_Day > 29)
1045 Year_Day := Year_Day - 28;
1047 if Is_Leap_Year then
1048 Year_Day := Year_Day - 1;
1053 while Year_Day > Days_In_Month (Month) loop
1054 Year_Day := Year_Day - Days_In_Month (Month);
1060 -- Step 7: Hour, minute, second and sub second processing in local
1063 Day := Day_Number (Year_Day);
1064 Day_Seconds := Integer (Date_Dur mod Secs_In_Day);
1065 Day_Secs := Duration (Day_Seconds) + Sub_Sec;
1066 Hour := Day_Seconds / 3_600;
1067 Hour_Seconds := Day_Seconds mod 3_600;
1068 Minute := Hour_Seconds / 60;
1069 Second := Hour_Seconds mod 60;
1077 (Year : Year_Number;
1078 Month : Month_Number;
1080 Day_Secs : Day_Duration;
1086 Use_Day_Secs : Boolean;
1087 Is_Ada_05 : Boolean;
1088 Time_Zone : Long_Integer) return Time
1091 Elapsed_Leaps : Natural;
1092 Next_Leap_N : Time_Rep;
1094 Rounded_Res_N : Time_Rep;
1097 -- Step 1: Check whether the day, month and year form a valid date
1099 if Day > Days_In_Month (Month)
1100 and then (Day /= 29 or else Month /= 2 or else not Is_Leap (Year))
1105 -- Start accumulating nanoseconds from the low bound of Ada time
1109 -- Step 2: Year processing and centenial year adjustment. Determine
1110 -- the number of four year segments since the start of Ada time and
1113 Count := (Year - Year_Number'First) / 4;
1114 Res_N := Res_N + Time_Rep (Count) * Secs_In_Four_Years * Nano;
1116 -- Note that non-leap centenial years are automatically considered
1117 -- leap in the operation above. An adjustment of several days is
1118 -- required to compensate for this.
1121 Res_N := Res_N - Time_Rep (3) * Nanos_In_Day;
1123 elsif Year > 2200 then
1124 Res_N := Res_N - Time_Rep (2) * Nanos_In_Day;
1126 elsif Year > 2100 then
1127 Res_N := Res_N - Time_Rep (1) * Nanos_In_Day;
1130 -- Add the remaining non-leap years
1132 Count := (Year - Year_Number'First) mod 4;
1133 Res_N := Res_N + Time_Rep (Count) * Secs_In_Non_Leap_Year * Nano;
1135 -- Step 3: Day of month processing. Determine the number of days
1136 -- since the start of the current year. Do not add the current
1137 -- day since it has not elapsed yet.
1139 Count := Cumulative_Days_Before_Month (Month) + Day - 1;
1141 -- The input year is leap and we have passed February
1149 Res_N := Res_N + Time_Rep (Count) * Nanos_In_Day;
1151 -- Step 4: Hour, minute, second and sub second processing
1153 if Use_Day_Secs then
1154 Res_N := Res_N + Duration_To_Time_Rep (Day_Secs);
1158 Time_Rep (Hour * 3_600 + Minute * 60 + Second) * Nano;
1160 if Sub_Sec = 1.0 then
1161 Res_N := Res_N + Time_Rep (1) * Nano;
1163 Res_N := Res_N + Duration_To_Time_Rep (Sub_Sec);
1167 -- At this point, the generated time value should be withing the
1168 -- bounds of Ada time.
1170 Check_Within_Time_Bounds (Res_N);
1172 -- Step 4: Time zone processing. At this point we have built an
1173 -- arbitrary time value which is not related to any time zone.
1174 -- For simplicity, the time value is normalized to GMT, producing
1175 -- a uniform representation which can be treated by arithmetic
1176 -- operations for instance without any additional corrections.
1179 if Time_Zone /= 0 then
1180 Res_N := Res_N - Time_Rep (Time_Zone) * 60 * Nano;
1187 Current_Off : constant Long_Integer :=
1188 Time_Zones_Operations.UTC_Time_Offset
1190 Current_Res_N : constant Time_Rep :=
1191 Res_N - Time_Rep (Current_Off) * Nano;
1192 Off : constant Long_Integer :=
1193 Time_Zones_Operations.UTC_Time_Offset
1194 (Time (Current_Res_N));
1196 Res_N := Res_N - Time_Rep (Off) * Nano;
1200 -- Step 5: Leap seconds processing in GMT
1202 if Leap_Support then
1203 Cumulative_Leap_Seconds
1204 (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
1206 Res_N := Res_N + Time_Rep (Elapsed_Leaps) * Nano;
1208 -- An Ada 2005 caller requesting an explicit leap second or an
1209 -- Ada 95 caller accounting for an invisible leap second.
1212 or else Res_N >= Next_Leap_N
1214 Res_N := Res_N + Time_Rep (1) * Nano;
1217 -- Leap second validity check
1219 Rounded_Res_N := Res_N - (Res_N mod Nano);
1223 and then Rounded_Res_N /= Next_Leap_N
1229 return Time (Res_N);
1231 end Formatting_Operations;
1233 ---------------------------
1234 -- Time_Zones_Operations --
1235 ---------------------------
1237 package body Time_Zones_Operations is
1239 -- The Unix time bounds in nanoseconds: 1970/1/1 .. 2037/1/1
1241 Unix_Min : constant Time_Rep := Ada_Low +
1242 Time_Rep (17 * 366 + 52 * 365) * Nanos_In_Day;
1244 Unix_Max : constant Time_Rep := Ada_Low +
1245 Time_Rep (34 * 366 + 102 * 365) * Nanos_In_Day +
1246 Time_Rep (Leap_Seconds_Count) * Nano;
1248 -- The following constants denote February 28 during non-leap
1249 -- centenial years, the units are nanoseconds.
1251 T_2100_2_28 : constant Time_Rep := Ada_Low +
1252 (Time_Rep (49 * 366 + 150 * 365 + 59) * Secs_In_Day +
1253 Time_Rep (Leap_Seconds_Count)) * Nano;
1255 T_2200_2_28 : constant Time_Rep := Ada_Low +
1256 (Time_Rep (73 * 366 + 226 * 365 + 59) * Secs_In_Day +
1257 Time_Rep (Leap_Seconds_Count)) * Nano;
1259 T_2300_2_28 : constant Time_Rep := Ada_Low +
1260 (Time_Rep (97 * 366 + 302 * 365 + 59) * Secs_In_Day +
1261 Time_Rep (Leap_Seconds_Count)) * Nano;
1263 -- 56 years (14 leap years + 42 non leap years) in nanoseconds:
1265 Nanos_In_56_Years : constant := (14 * 366 + 42 * 365) * Nanos_In_Day;
1267 -- Base C types. There is no point dragging in Interfaces.C just for
1268 -- these four types.
1270 type char_Pointer is access Character;
1271 subtype int is Integer;
1272 subtype long is Long_Integer;
1273 type long_Pointer is access all long;
1275 -- The Ada equivalent of struct tm and type time_t
1278 tm_sec : int; -- seconds after the minute (0 .. 60)
1279 tm_min : int; -- minutes after the hour (0 .. 59)
1280 tm_hour : int; -- hours since midnight (0 .. 24)
1281 tm_mday : int; -- day of the month (1 .. 31)
1282 tm_mon : int; -- months since January (0 .. 11)
1283 tm_year : int; -- years since 1900
1284 tm_wday : int; -- days since Sunday (0 .. 6)
1285 tm_yday : int; -- days since January 1 (0 .. 365)
1286 tm_isdst : int; -- Daylight Savings Time flag (-1 .. 1)
1287 tm_gmtoff : long; -- offset from UTC in seconds
1288 tm_zone : char_Pointer; -- timezone abbreviation
1291 type tm_Pointer is access all tm;
1293 subtype time_t is long;
1294 type time_t_Pointer is access all time_t;
1296 procedure localtime_tzoff
1297 (C : time_t_Pointer;
1299 off : long_Pointer);
1300 pragma Import (C, localtime_tzoff, "__gnat_localtime_tzoff");
1301 -- This is a lightweight wrapper around the system library function
1302 -- localtime_r. Parameter 'off' captures the UTC offset which is either
1303 -- retrieved from the tm struct or calculated from the 'timezone' extern
1304 -- and the tm_isdst flag in the tm struct.
1306 ---------------------
1307 -- UTC_Time_Offset --
1308 ---------------------
1310 function UTC_Time_Offset (Date : Time) return Long_Integer is
1311 Adj_Cent : Integer := 0;
1313 Offset : aliased long;
1314 Secs_T : aliased time_t;
1315 Secs_TM : aliased tm;
1318 Date_N := Time_Rep (Date);
1320 -- Dates which are 56 years appart fall on the same day, day light
1321 -- saving and so on. Non-leap centenial years violate this rule by
1322 -- one day and as a consequence, special adjustment is needed.
1324 if Date_N > T_2100_2_28 then
1325 if Date_N > T_2200_2_28 then
1326 if Date_N > T_2300_2_28 then
1337 if Adj_Cent > 0 then
1338 Date_N := Date_N - Time_Rep (Adj_Cent) * Nanos_In_Day;
1341 -- Shift the date within bounds of Unix time
1343 while Date_N < Unix_Min loop
1344 Date_N := Date_N + Nanos_In_56_Years;
1347 while Date_N >= Unix_Max loop
1348 Date_N := Date_N - Nanos_In_56_Years;
1351 -- Perform a shift in origins from Ada to Unix
1353 Date_N := Date_N - Unix_Min;
1355 -- Convert the date into seconds
1357 Secs_T := time_t (Date_N / Nano);
1360 (Secs_T'Unchecked_Access,
1361 Secs_TM'Unchecked_Access,
1362 Offset'Unchecked_Access);
1365 end UTC_Time_Offset;
1366 end Time_Zones_Operations;
1368 -- Start of elaboration code for Ada.Calendar
1371 System.OS_Primitives.Initialize;
1373 -- Population of the leap seconds table
1375 if Leap_Support then
1377 type Leap_Second_Date is record
1379 Month : Month_Number;
1384 constant array (1 .. Leap_Seconds_Count) of Leap_Second_Date :=
1385 ((1972, 6, 30), (1972, 12, 31), (1973, 12, 31), (1974, 12, 31),
1386 (1975, 12, 31), (1976, 12, 31), (1977, 12, 31), (1978, 12, 31),
1387 (1979, 12, 31), (1981, 6, 30), (1982, 6, 30), (1983, 6, 30),
1388 (1985, 6, 30), (1987, 12, 31), (1989, 12, 31), (1990, 12, 31),
1389 (1992, 6, 30), (1993, 6, 30), (1994, 6, 30), (1995, 12, 31),
1390 (1997, 6, 30), (1998, 12, 31), (2005, 12, 31));
1392 Days_In_Four_Years : constant := 365 * 3 + 366;
1395 Leap : Leap_Second_Date;
1399 for Index in 1 .. Leap_Seconds_Count loop
1400 Leap := Leap_Second_Dates (Index);
1402 -- Calculate the number of days from the start of Ada time until
1403 -- the current leap second occurence. Non-leap centenial years
1404 -- are not accounted for in these calculations since there are
1405 -- no leap seconds after 2100 yet.
1407 Years := Leap.Year - Ada_Min_Year;
1408 Days := (Years / 4) * Days_In_Four_Years;
1409 Years := Years mod 4;
1414 elsif Years = 2 then
1415 Days := Days + 365 * 2;
1417 elsif Years = 3 then
1418 Days := Days + 365 * 3;
1421 Days := Days + Cumulative_Days_Before_Month (Leap.Month);
1423 if Is_Leap (Leap.Year)
1424 and then Leap.Month > 2
1429 Days := Days + Leap.Day;
1431 -- Index - 1 previous leap seconds are added to Time (Index) as
1432 -- well as the lower buffer for time zones.
1434 Leap_Second_Times (Index) := Ada_Low +
1435 (Time_Rep (Days) * Secs_In_Day + Time_Rep (Index - 1)) * Nano;