OSDN Git Service

Minor reformatting.
[pf3gnuchains/gcc-fork.git] / gcc / ada / a-calend.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT RUN-TIME COMPONENTS                         --
4 --                                                                          --
5 --                         A D A . C A L E N D A R                          --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --          Copyright (C) 1992-2007, 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 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.                                              --
21 --                                                                          --
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.                                      --
28 --                                                                          --
29 -- GNAT was originally developed  by the GNAT team at  New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 with Ada.Unchecked_Conversion;
35
36 with System.OS_Primitives;
37 --  used for Clock
38
39 package body Ada.Calendar is
40
41    --------------------------
42    -- Implementation Notes --
43    --------------------------
44
45    --  In complex algorithms, some variables of type Ada.Calendar.Time carry
46    --  suffix _S or _N to denote units of seconds or nanoseconds.
47    --
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.
53
54    --     package Ada.Calendar
55    --        procedure Split (5 parameters) -------+
56    --                                              | Call from local routine
57    --     private                                  |
58    --        package Formatting_Operations         |
59    --           procedure Split (11 parameters) <--+
60    --        end Formatting_Operations             |
61    --     end Ada.Calendar                         |
62    --                                              |
63    --     package Ada.Calendar.Formatting          | Call from child routine
64    --        procedure Split (9 or 10 parameters) -+
65    --     end Ada.Calendar.Formatting
66
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).
74
75    -----------------------
76    -- Local Subprograms --
77    -----------------------
78
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.
82
83    procedure Cumulative_Leap_Seconds
84      (Start_Date    : Time_Rep;
85       End_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.
94    --
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:
101    --
102    --     End_Date >= Next_Leap_Sec;
103    --
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.
106
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
110
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
114
115    -----------------
116    -- Local Types --
117    -----------------
118
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:
122
123    --            'First  Ada_Low                  Ada_High  'Last
124    --  Time_Rep: +-------+------------------------+---------+
125    --  Time_Dur:         +------------------------+---------+
126    --                    0                                  'Last
127
128    type Time_Dur is range 0 .. 2 ** 63 - 1;
129
130    ---------------------
131    -- Local Constants --
132    ---------------------
133
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.
137
138    Leap_Support       : constant Boolean := False;
139    Leap_Seconds_Count : constant Natural := 23;
140
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;
144
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.
148
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;
151
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.
154
155    Ada_High_And_Leaps : constant Time_Rep :=
156                           Ada_High + Time_Rep (Leap_Seconds_Count) * Nano;
157
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.
161
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;
166
167    --  The Unix lower time bound expressed as nanoseconds since the
168    --  start of Ada time in UTC.
169
170    Unix_Min : constant Time_Rep :=
171                 Ada_Low + Time_Rep (17 * 366 + 52 * 365) * Nanos_In_Day;
172
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);
176
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
180    --  Ada.Calendar.
181
182    ---------
183    -- "+" --
184    ---------
185
186    function "+" (Left : Time; Right : Duration) return Time is
187       pragma Unsuppress (Overflow_Check);
188       Left_N : constant Time_Rep := Time_Rep (Left);
189    begin
190       return Time (Left_N + Duration_To_Time_Rep (Right));
191    exception
192       when Constraint_Error =>
193          raise Time_Error;
194    end "+";
195
196    function "+" (Left : Duration; Right : Time) return Time is
197    begin
198       return Right + Left;
199    end "+";
200
201    ---------
202    -- "-" --
203    ---------
204
205    function "-" (Left : Time; Right : Duration) return Time is
206       pragma Unsuppress (Overflow_Check);
207       Left_N : constant Time_Rep := Time_Rep (Left);
208    begin
209       return Time (Left_N - Duration_To_Time_Rep (Right));
210    exception
211       when Constraint_Error =>
212          raise Time_Error;
213    end "-";
214
215    function "-" (Left : Time; Right : Time) return Duration is
216       pragma Unsuppress (Overflow_Check);
217
218       --  The bounds of type Duration expressed as time representations
219
220       Dur_Low  : constant Time_Rep := Duration_To_Time_Rep (Duration'First);
221       Dur_High : constant Time_Rep := Duration_To_Time_Rep (Duration'Last);
222
223       Res_N : Time_Rep;
224
225    begin
226       Res_N := Time_Rep (Left) - Time_Rep (Right);
227
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.
232
233       if Res_N < Dur_Low
234         or else Res_N > Dur_High
235       then
236          raise Time_Error;
237       end if;
238
239       return Time_Rep_To_Duration (Res_N);
240    exception
241       when Constraint_Error =>
242          raise Time_Error;
243    end "-";
244
245    ---------
246    -- "<" --
247    ---------
248
249    function "<" (Left, Right : Time) return Boolean is
250    begin
251       return Time_Rep (Left) < Time_Rep (Right);
252    end "<";
253
254    ----------
255    -- "<=" --
256    ----------
257
258    function "<=" (Left, Right : Time) return Boolean is
259    begin
260       return Time_Rep (Left) <= Time_Rep (Right);
261    end "<=";
262
263    ---------
264    -- ">" --
265    ---------
266
267    function ">" (Left, Right : Time) return Boolean is
268    begin
269       return Time_Rep (Left) > Time_Rep (Right);
270    end ">";
271
272    ----------
273    -- ">=" --
274    ----------
275
276    function ">=" (Left, Right : Time) return Boolean is
277    begin
278       return Time_Rep (Left) >= Time_Rep (Right);
279    end ">=";
280
281    ------------------------------
282    -- Check_Within_Time_Bounds --
283    ------------------------------
284
285    procedure Check_Within_Time_Bounds (T : Time_Rep) is
286    begin
287       if Leap_Support then
288          if T < Ada_Low or else T > Ada_High_And_Leaps then
289             raise Time_Error;
290          end if;
291       else
292          if T < Ada_Low or else T > Ada_High then
293             raise Time_Error;
294          end if;
295       end if;
296    end Check_Within_Time_Bounds;
297
298    -----------
299    -- Clock --
300    -----------
301
302    function Clock return Time is
303       Elapsed_Leaps : Natural;
304       Next_Leap_N   : Time_Rep;
305
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.
309
310       Res_N : Time_Rep :=
311                 Duration_To_Time_Rep (System.OS_Primitives.Clock) +
312                   Unix_Min;
313
314    begin
315       --  If the target supports leap seconds, determine the number of leap
316       --  seconds elapsed until this moment.
317
318       if Leap_Support then
319          Cumulative_Leap_Seconds
320            (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
321
322          --  The system clock may fall exactly on a leap second
323
324          if Res_N >= Next_Leap_N then
325             Elapsed_Leaps := Elapsed_Leaps + 1;
326          end if;
327
328       --  The target does not support leap seconds
329
330       else
331          Elapsed_Leaps := 0;
332       end if;
333
334       Res_N := Res_N + Time_Rep (Elapsed_Leaps) * Nano;
335
336       return Time (Res_N);
337    end Clock;
338
339    -----------------------------
340    -- Cumulative_Leap_Seconds --
341    -----------------------------
342
343    procedure Cumulative_Leap_Seconds
344      (Start_Date    : Time_Rep;
345       End_Date      : Time_Rep;
346       Elapsed_Leaps : out Natural;
347       Next_Leap     : out Time_Rep)
348    is
349       End_Index   : Positive;
350       End_T       : Time_Rep := End_Date;
351       Start_Index : Positive;
352       Start_T     : Time_Rep := Start_Date;
353
354    begin
355       --  Both input dates must be normalized to UTC
356
357       pragma Assert (Leap_Support and then End_Date >= Start_Date);
358
359       Next_Leap := End_Of_Time;
360
361       --  Make sure that the end date does not excede the upper bound
362       --  of Ada time.
363
364       if End_Date > Ada_High then
365          End_T := Ada_High;
366       end if;
367
368       --  Remove the sub seconds from both dates
369
370       Start_T := Start_T - (Start_T mod Nano);
371       End_T   := End_T   - (End_T   mod Nano);
372
373       --  Some trivial cases:
374       --                     Leap 1 . . . Leap N
375       --  ---+========+------+############+-------+========+-----
376       --     Start_T  End_T                       Start_T  End_T
377
378       if End_T < Leap_Second_Times (1) then
379          Elapsed_Leaps := 0;
380          Next_Leap     := Leap_Second_Times (1);
381          return;
382
383       elsif Start_T > Leap_Second_Times (Leap_Seconds_Count) then
384          Elapsed_Leaps := 0;
385          Next_Leap     := End_Of_Time;
386          return;
387       end if;
388
389       --  Perform the calculations only if the start date is within the leap
390       --  second occurences table.
391
392       if Start_T <= Leap_Second_Times (Leap_Seconds_Count) then
393
394          --    1    2                  N - 1   N
395          --  +----+----+--  . . .  --+-------+---+
396          --  | T1 | T2 |             | N - 1 | N |
397          --  +----+----+--  . . .  --+-------+---+
398          --         ^                   ^
399          --         | Start_Index       | End_Index
400          --         +-------------------+
401          --             Leaps_Between
402
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
406          --  seconds elapsed.
407
408          Start_Index := 1;
409          loop
410             exit when Leap_Second_Times (Start_Index) >= Start_T;
411             Start_Index := Start_Index + 1;
412          end loop;
413
414          End_Index := Start_Index;
415          loop
416             exit when End_Index > Leap_Seconds_Count
417               or else Leap_Second_Times (End_Index) >= End_T;
418             End_Index := End_Index + 1;
419          end loop;
420
421          if End_Index <= Leap_Seconds_Count then
422             Next_Leap := Leap_Second_Times (End_Index);
423          end if;
424
425          Elapsed_Leaps := End_Index - Start_Index;
426
427       else
428          Elapsed_Leaps := 0;
429       end if;
430    end Cumulative_Leap_Seconds;
431
432    ---------
433    -- Day --
434    ---------
435
436    function Day (Date : Time) return Day_Number is
437       Y : Year_Number;
438       M : Month_Number;
439       D : Day_Number;
440       S : Day_Duration;
441    begin
442       Split (Date, Y, M, D, S);
443       return D;
444    end Day;
445
446    -------------
447    -- Is_Leap --
448    -------------
449
450    function Is_Leap (Year : Year_Number) return Boolean is
451    begin
452       --  Leap centenial years
453
454       if Year mod 400 = 0 then
455          return True;
456
457       --  Non-leap centenial years
458
459       elsif Year mod 100 = 0 then
460          return False;
461
462       --  Regular years
463
464       else
465          return Year mod 4 = 0;
466       end if;
467    end Is_Leap;
468
469    -----------
470    -- Month --
471    -----------
472
473    function Month (Date : Time) return Month_Number is
474       Y : Year_Number;
475       M : Month_Number;
476       D : Day_Number;
477       S : Day_Duration;
478    begin
479       Split (Date, Y, M, D, S);
480       return M;
481    end Month;
482
483    -------------
484    -- Seconds --
485    -------------
486
487    function Seconds (Date : Time) return Day_Duration is
488       Y : Year_Number;
489       M : Month_Number;
490       D : Day_Number;
491       S : Day_Duration;
492    begin
493       Split (Date, Y, M, D, S);
494       return S;
495    end Seconds;
496
497    -----------
498    -- Split --
499    -----------
500
501    procedure Split
502      (Date    : Time;
503       Year    : out Year_Number;
504       Month   : out Month_Number;
505       Day     : out Day_Number;
506       Seconds : out Day_Duration)
507    is
508       H  : Integer;
509       M  : Integer;
510       Se : Integer;
511       Ss : Duration;
512       Le : Boolean;
513
514    begin
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.
517
518       Formatting_Operations.Split
519         (Date      => Date,
520          Year      => Year,
521          Month     => Month,
522          Day       => Day,
523          Day_Secs  => Seconds,
524          Hour      => H,
525          Minute    => M,
526          Second    => Se,
527          Sub_Sec   => Ss,
528          Leap_Sec  => Le,
529          Is_Ada_05 => False,
530          Time_Zone => 0);
531
532       --  Validity checks
533
534       if not Year'Valid
535         or else not Month'Valid
536         or else not Day'Valid
537         or else not Seconds'Valid
538       then
539          raise Time_Error;
540       end if;
541    end Split;
542
543    -------------
544    -- Time_Of --
545    -------------
546
547    function Time_Of
548      (Year    : Year_Number;
549       Month   : Month_Number;
550       Day     : Day_Number;
551       Seconds : Day_Duration := 0.0) return Time
552    is
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.
556
557       H  : constant Integer := 1;
558       M  : constant Integer := 1;
559       Se : constant Integer := 1;
560       Ss : constant Duration := 0.1;
561
562    begin
563       --  Validity checks
564
565       if not Year'Valid
566         or else not Month'Valid
567         or else not Day'Valid
568         or else not Seconds'Valid
569       then
570          raise Time_Error;
571       end if;
572
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.
575
576       return
577         Formatting_Operations.Time_Of
578           (Year         => Year,
579            Month        => Month,
580            Day          => Day,
581            Day_Secs     => Seconds,
582            Hour         => H,
583            Minute       => M,
584            Second       => Se,
585            Sub_Sec      => Ss,
586            Leap_Sec     => False,
587            Use_Day_Secs => True,
588            Is_Ada_05    => False,
589            Time_Zone    => 0);
590    end Time_Of;
591
592    ----------
593    -- Year --
594    ----------
595
596    function Year (Date : Time) return Year_Number is
597       Y : Year_Number;
598       M : Month_Number;
599       D : Day_Number;
600       S : Day_Duration;
601    begin
602       Split (Date, Y, M, D, S);
603       return Y;
604    end Year;
605
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).
609
610    ---------------------------
611    -- Arithmetic_Operations --
612    ---------------------------
613
614    package body Arithmetic_Operations is
615
616       ---------
617       -- Add --
618       ---------
619
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);
623       begin
624          return Time (Date_N + Time_Rep (Days) * Nanos_In_Day);
625       exception
626          when Constraint_Error =>
627             raise Time_Error;
628       end Add;
629
630       ----------------
631       -- Difference --
632       ----------------
633
634       procedure Difference
635         (Left         : Time;
636          Right        : Time;
637          Days         : out Long_Integer;
638          Seconds      : out Duration;
639          Leap_Seconds : out Integer)
640       is
641          Res_Dur       : Time_Dur;
642          Earlier       : Time_Rep;
643          Elapsed_Leaps : Natural;
644          Later         : Time_Rep;
645          Negate        : Boolean := False;
646          Next_Leap_N   : Time_Rep;
647          Sub_Secs      : Duration;
648          Sub_Secs_Diff : Time_Rep;
649
650       begin
651          --  Both input time values are assumed to be in UTC
652
653          if Left >= Right then
654             Later   := Time_Rep (Left);
655             Earlier := Time_Rep (Right);
656          else
657             Later   := Time_Rep (Right);
658             Earlier := Time_Rep (Left);
659             Negate  := True;
660          end if;
661
662          --  If the target supports leap seconds, process them
663
664          if Leap_Support then
665             Cumulative_Leap_Seconds
666               (Earlier, Later, Elapsed_Leaps, Next_Leap_N);
667
668             if Later >= Next_Leap_N then
669                Elapsed_Leaps := Elapsed_Leaps + 1;
670             end if;
671
672          --  The target does not support leap seconds
673
674          else
675             Elapsed_Leaps := 0;
676          end if;
677
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.
681
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;
685
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
691          --  previous step.
692
693          Res_Dur :=
694            Time_Dur (Later / Nano - Earlier / Nano) - Time_Dur (Elapsed_Leaps);
695
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);
699
700          if Negate then
701             Days    := -Days;
702             Seconds := -Seconds;
703
704             if Leap_Seconds /= 0 then
705                Leap_Seconds := -Leap_Seconds;
706             end if;
707          end if;
708       end Difference;
709
710       --------------
711       -- Subtract --
712       --------------
713
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);
717       begin
718          return Time (Date_N - Time_Rep (Days) * Nanos_In_Day);
719       exception
720          when Constraint_Error =>
721             raise Time_Error;
722       end Subtract;
723    end Arithmetic_Operations;
724
725    ----------------------
726    -- Delay_Operations --
727    ----------------------
728
729    package body Delays_Operations is
730
731       -----------------
732       -- To_Duration --
733       -----------------
734
735       function To_Duration (Date : Time) return Duration is
736          Elapsed_Leaps : Natural;
737          Next_Leap_N   : Time_Rep;
738          Res_N         : Time_Rep;
739
740       begin
741          Res_N := Time_Rep (Date);
742
743          --  If the target supports leap seconds, remove any leap seconds
744          --  elapsed upto the input date.
745
746          if Leap_Support then
747             Cumulative_Leap_Seconds
748               (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
749
750             --  The input time value may fall on a leap second occurence
751
752             if Res_N >= Next_Leap_N then
753                Elapsed_Leaps := Elapsed_Leaps + 1;
754             end if;
755
756          --  The target does not support leap seconds
757
758          else
759             Elapsed_Leaps := 0;
760          end if;
761
762          Res_N := Res_N - Time_Rep (Elapsed_Leaps) * Nano;
763
764          --  Perform a shift in origins, note that enforcing type Time on
765          --  both operands will invoke Ada.Calendar."-".
766
767          return Time (Res_N) - Time (Unix_Min);
768       end To_Duration;
769    end Delays_Operations;
770
771    ---------------------------
772    -- Formatting_Operations --
773    ---------------------------
774
775    package body Formatting_Operations is
776
777       -----------------
778       -- Day_Of_Week --
779       -----------------
780
781       function Day_Of_Week (Date : Time) return Integer is
782          Y  : Year_Number;
783          Mo : Month_Number;
784          D  : Day_Number;
785          Ds : Day_Duration;
786          H  : Integer;
787          Mi : Integer;
788          Se : Integer;
789          Su : Duration;
790          Le : Boolean;
791
792          Day_Count : Long_Integer;
793          Res_Dur   : Time_Dur;
794          Res_N     : Time_Rep;
795
796       begin
797          Formatting_Operations.Split
798            (Date      => Date,
799             Year      => Y,
800             Month     => Mo,
801             Day       => D,
802             Day_Secs  => Ds,
803             Hour      => H,
804             Minute    => Mi,
805             Second    => Se,
806             Sub_Sec   => Su,
807             Leap_Sec  => Le,
808             Is_Ada_05 => True,
809             Time_Zone => 0);
810
811          --  Build a time value in the middle of the same day
812
813          Res_N :=
814            Time_Rep
815              (Formatting_Operations.Time_Of
816                (Year         => Y,
817                 Month        => Mo,
818                 Day          => D,
819                 Day_Secs     => 0.0,
820                 Hour         => 12,
821                 Minute       => 0,
822                 Second       => 0,
823                 Sub_Sec      => 0.0,
824                 Leap_Sec     => False,
825                 Use_Day_Secs => False,
826                 Is_Ada_05    => True,
827                 Time_Zone    => 0));
828
829          --  Determine the elapsed seconds since the start of Ada time
830
831          Res_Dur := Time_Dur (Res_N / Nano - Ada_Low / Nano);
832
833          --  Count the number of days since the start of Ada time. 1901-1-1
834          --  GMT was a Tuesday.
835
836          Day_Count := Long_Integer (Res_Dur / Secs_In_Day) + 1;
837
838          return Integer (Day_Count mod 7);
839       end Day_Of_Week;
840
841       -----------
842       -- Split --
843       -----------
844
845       procedure Split
846         (Date      : Time;
847          Year      : out Year_Number;
848          Month     : out Month_Number;
849          Day       : out Day_Number;
850          Day_Secs  : out Day_Duration;
851          Hour      : out Integer;
852          Minute    : out Integer;
853          Second    : out Integer;
854          Sub_Sec   : out Duration;
855          Leap_Sec  : out Boolean;
856          Is_Ada_05 : Boolean;
857          Time_Zone : Long_Integer)
858       is
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.
862
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;
869
870          Date_Dur       : Time_Dur;
871          Date_N         : Time_Rep;
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;
878          Rem_Years      : Natural;
879          Sub_Sec_N      : Time_Rep;
880          Year_Day       : Natural;
881
882       begin
883          Date_N := Time_Rep (Date);
884
885          --  Step 1: Leap seconds processing in UTC
886
887          if Leap_Support then
888             Cumulative_Leap_Seconds
889               (Start_Of_Time, Date_N, Elapsed_Leaps, Next_Leap_N);
890
891             Leap_Sec := Date_N >= Next_Leap_N;
892
893             if Leap_Sec then
894                Elapsed_Leaps := Elapsed_Leaps + 1;
895             end if;
896
897          --  The target does not support leap seconds
898
899          else
900             Elapsed_Leaps := 0;
901             Leap_Sec      := False;
902          end if;
903
904          Date_N := Date_N - Time_Rep (Elapsed_Leaps) * Nano;
905
906          --  Step 2: Time zone processing. This action converts the input date
907          --  from GMT to the requested time zone.
908
909          if Is_Ada_05 then
910             if Time_Zone /= 0 then
911                Date_N := Date_N + Time_Rep (Time_Zone) * 60 * Nano;
912             end if;
913
914          --  Ada 83 and 95
915
916          else
917             declare
918                Off : constant Long_Integer :=
919                        Time_Zones_Operations.UTC_Time_Offset (Time (Date_N));
920             begin
921                Date_N := Date_N + Time_Rep (Off) * Nano;
922             end;
923          end if;
924
925          --  Step 3: Non-leap centenial year adjustment in local time zone
926
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.
930
931          if Date_N >= Year_2301 then
932             Date_N := Date_N + Time_Rep (3) * Nanos_In_Day;
933
934          elsif Date_N >= Year_2201 then
935             Date_N := Date_N + Time_Rep (2) * Nanos_In_Day;
936
937          elsif Date_N >= Year_2101 then
938             Date_N := Date_N + Time_Rep (1) * Nanos_In_Day;
939          end if;
940
941          --  Step 4: Sub second processing in local time zone
942
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;
946
947          --  Convert Date_N into a time duration value, changing the units
948          --  to seconds.
949
950          Date_Dur := Time_Dur (Date_N / Nano - Ada_Low / Nano);
951
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
954          --  date.
955
956          Four_Year_Segs := Natural (Date_Dur / Secs_In_Four_Years);
957
958          if Four_Year_Segs > 0 then
959             Date_Dur := Date_Dur - Time_Dur (Four_Year_Segs) *
960                                    Secs_In_Four_Years;
961          end if;
962
963          --  Calculate the remaining non-leap years
964
965          Rem_Years := Natural (Date_Dur / Secs_In_Non_Leap_Year);
966
967          if Rem_Years > 3 then
968             Rem_Years := 3;
969          end if;
970
971          Date_Dur := Date_Dur - Time_Dur (Rem_Years) * Secs_In_Non_Leap_Year;
972
973          Year := Ada_Min_Year + Natural (4 * Four_Year_Segs + Rem_Years);
974          Is_Leap_Year := Is_Leap (Year);
975
976          --  Step 6: Month and day processing in local time zone
977
978          Year_Day := Natural (Date_Dur / Secs_In_Day) + 1;
979
980          Month := 1;
981
982          --  Processing for months after January
983
984          if Year_Day > 31 then
985             Month    := 2;
986             Year_Day := Year_Day - 31;
987
988             --  Processing for a new month or a leap February
989
990             if Year_Day > 28
991               and then (not Is_Leap_Year or else Year_Day > 29)
992             then
993                Month    := 3;
994                Year_Day := Year_Day - 28;
995
996                if Is_Leap_Year then
997                   Year_Day := Year_Day - 1;
998                end if;
999
1000                --  Remaining months
1001
1002                while Year_Day > Days_In_Month (Month) loop
1003                   Year_Day := Year_Day - Days_In_Month (Month);
1004                   Month    := Month + 1;
1005                end loop;
1006             end if;
1007          end if;
1008
1009          --  Step 7: Hour, minute, second and sub second processing in local
1010          --  time zone.
1011
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;
1019       end Split;
1020
1021       -------------
1022       -- Time_Of --
1023       -------------
1024
1025       function Time_Of
1026         (Year         : Year_Number;
1027          Month        : Month_Number;
1028          Day          : Day_Number;
1029          Day_Secs     : Day_Duration;
1030          Hour         : Integer;
1031          Minute       : Integer;
1032          Second       : Integer;
1033          Sub_Sec      : Duration;
1034          Leap_Sec     : Boolean;
1035          Use_Day_Secs : Boolean;
1036          Is_Ada_05    : Boolean;
1037          Time_Zone    : Long_Integer) return Time
1038       is
1039          Count         : Integer;
1040          Elapsed_Leaps : Natural;
1041          Next_Leap_N   : Time_Rep;
1042          Res_N         : Time_Rep;
1043          Rounded_Res_N : Time_Rep;
1044
1045       begin
1046          --  Step 1: Check whether the day, month and year form a valid date
1047
1048          if Day > Days_In_Month (Month)
1049            and then (Day /= 29 or else Month /= 2 or else not Is_Leap (Year))
1050          then
1051             raise Time_Error;
1052          end if;
1053
1054          --  Start accumulating nanoseconds from the low bound of Ada time
1055
1056          Res_N := Ada_Low;
1057
1058          --  Step 2: Year processing and centenial year adjustment. Determine
1059          --  the number of four year segments since the start of Ada time and
1060          --  the input date.
1061
1062          Count := (Year - Year_Number'First) / 4;
1063          Res_N := Res_N + Time_Rep (Count) * Secs_In_Four_Years * Nano;
1064
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.
1068
1069          if Year > 2300 then
1070             Res_N := Res_N - Time_Rep (3) * Nanos_In_Day;
1071
1072          elsif Year > 2200 then
1073             Res_N := Res_N - Time_Rep (2) * Nanos_In_Day;
1074
1075          elsif Year > 2100 then
1076             Res_N := Res_N - Time_Rep (1) * Nanos_In_Day;
1077          end if;
1078
1079          --  Add the remaining non-leap years
1080
1081          Count := (Year - Year_Number'First) mod 4;
1082          Res_N := Res_N + Time_Rep (Count) * Secs_In_Non_Leap_Year * Nano;
1083
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.
1087
1088          Count := Cumulative_Days_Before_Month (Month) + Day - 1;
1089
1090          --  The input year is leap and we have passed February
1091
1092          if Is_Leap (Year)
1093            and then Month > 2
1094          then
1095             Count := Count + 1;
1096          end if;
1097
1098          Res_N := Res_N + Time_Rep (Count) * Nanos_In_Day;
1099
1100          --  Step 4: Hour, minute, second and sub second processing
1101
1102          if Use_Day_Secs then
1103             Res_N := Res_N + Duration_To_Time_Rep (Day_Secs);
1104
1105          else
1106             Res_N := Res_N +
1107               Time_Rep (Hour * 3_600 + Minute * 60 + Second) * Nano;
1108
1109             if Sub_Sec = 1.0 then
1110                Res_N := Res_N + Time_Rep (1) * Nano;
1111             else
1112                Res_N := Res_N + Duration_To_Time_Rep (Sub_Sec);
1113             end if;
1114          end if;
1115
1116          --  At this point, the generated time value should be withing the
1117          --  bounds of Ada time.
1118
1119          Check_Within_Time_Bounds (Res_N);
1120
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.
1126
1127          if Is_Ada_05 then
1128             if Time_Zone /= 0 then
1129                Res_N := Res_N - Time_Rep (Time_Zone) * 60 * Nano;
1130             end if;
1131
1132          --  Ada 83 and 95
1133
1134          else
1135             declare
1136                Current_Off   : constant Long_Integer :=
1137                                  Time_Zones_Operations.UTC_Time_Offset
1138                                    (Time (Res_N));
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));
1144             begin
1145                Res_N := Res_N - Time_Rep (Off) * Nano;
1146             end;
1147          end if;
1148
1149          --  Step 5: Leap seconds processing in GMT
1150
1151          if Leap_Support then
1152             Cumulative_Leap_Seconds
1153               (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
1154
1155             Res_N := Res_N + Time_Rep (Elapsed_Leaps) * Nano;
1156
1157             --  An Ada 2005 caller requesting an explicit leap second or an
1158             --  Ada 95 caller accounting for an invisible leap second.
1159
1160             if Leap_Sec
1161               or else Res_N >= Next_Leap_N
1162             then
1163                Res_N := Res_N + Time_Rep (1) * Nano;
1164             end if;
1165
1166             --  Leap second validity check
1167
1168             Rounded_Res_N := Res_N - (Res_N mod Nano);
1169
1170             if Is_Ada_05
1171               and then Leap_Sec
1172               and then Rounded_Res_N /= Next_Leap_N
1173             then
1174                raise Time_Error;
1175             end if;
1176          end if;
1177
1178          return Time (Res_N);
1179       end Time_Of;
1180    end Formatting_Operations;
1181
1182    ---------------------------
1183    -- Time_Zones_Operations --
1184    ---------------------------
1185
1186    package body Time_Zones_Operations is
1187
1188       --  The Unix time bounds in nanoseconds: 1970/1/1 .. 2037/1/1
1189
1190       Unix_Min : constant Time_Rep := Ada_Low +
1191                    Time_Rep (17 * 366 +  52 * 365) * Nanos_In_Day;
1192
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;
1196
1197       --  The following constants denote February 28 during non-leap
1198       --  centenial years, the units are nanoseconds.
1199
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;
1203
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;
1207
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;
1211
1212       --  56 years (14 leap years + 42 non leap years) in nanoseconds:
1213
1214       Nanos_In_56_Years : constant := (14 * 366 + 42 * 365) * Nanos_In_Day;
1215
1216       --  Base C types. There is no point dragging in Interfaces.C just for
1217       --  these four types.
1218
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;
1223
1224       --  The Ada equivalent of struct tm and type time_t
1225
1226       type tm is record
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
1238       end record;
1239
1240       type tm_Pointer is access all tm;
1241
1242       subtype time_t is long;
1243       type time_t_Pointer is access all time_t;
1244
1245       procedure localtime_tzoff
1246        (C   : time_t_Pointer;
1247         res : tm_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.
1254
1255       ---------------------
1256       -- UTC_Time_Offset --
1257       ---------------------
1258
1259       function UTC_Time_Offset (Date : Time) return Long_Integer is
1260          Adj_Cent : Integer := 0;
1261          Date_N   : Time_Rep;
1262          Offset   : aliased long;
1263          Secs_T   : aliased time_t;
1264          Secs_TM  : aliased tm;
1265
1266       begin
1267          Date_N := Time_Rep (Date);
1268
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.
1272
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
1276                   Adj_Cent := 3;
1277                else
1278                   Adj_Cent := 2;
1279                end if;
1280
1281             else
1282                Adj_Cent := 1;
1283             end if;
1284          end if;
1285
1286          if Adj_Cent > 0 then
1287             Date_N := Date_N - Time_Rep (Adj_Cent) * Nanos_In_Day;
1288          end if;
1289
1290          --  Shift the date within bounds of Unix time
1291
1292          while Date_N < Unix_Min loop
1293             Date_N := Date_N + Nanos_In_56_Years;
1294          end loop;
1295
1296          while Date_N >= Unix_Max loop
1297             Date_N := Date_N - Nanos_In_56_Years;
1298          end loop;
1299
1300          --  Perform a shift in origins from Ada to Unix
1301
1302          Date_N := Date_N - Unix_Min;
1303
1304          --  Convert the date into seconds
1305
1306          Secs_T := time_t (Date_N / Nano);
1307
1308          localtime_tzoff
1309            (Secs_T'Unchecked_Access,
1310             Secs_TM'Unchecked_Access,
1311             Offset'Unchecked_Access);
1312
1313          return Offset;
1314       end UTC_Time_Offset;
1315    end Time_Zones_Operations;
1316
1317 --  Start of elaboration code for Ada.Calendar
1318
1319 begin
1320    System.OS_Primitives.Initialize;
1321
1322    --  Population of the leap seconds table
1323
1324    if Leap_Support then
1325       declare
1326          type Leap_Second_Date is record
1327             Year  : Year_Number;
1328             Month : Month_Number;
1329             Day   : Day_Number;
1330          end record;
1331
1332          Leap_Second_Dates :
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));
1340
1341          Days_In_Four_Years : constant := 365 * 3 + 366;
1342
1343          Days  : Natural;
1344          Leap  : Leap_Second_Date;
1345          Years : Natural;
1346
1347       begin
1348          for Index in 1 .. Leap_Seconds_Count loop
1349             Leap := Leap_Second_Dates (Index);
1350
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.
1355
1356             Years := Leap.Year - Ada_Min_Year;
1357             Days  := (Years / 4) * Days_In_Four_Years;
1358             Years := Years mod 4;
1359
1360             if Years = 1 then
1361                Days := Days + 365;
1362
1363             elsif Years = 2 then
1364                Days := Days + 365 * 2;
1365
1366             elsif Years = 3 then
1367                Days := Days + 365 * 3;
1368             end if;
1369
1370             Days := Days + Cumulative_Days_Before_Month (Leap.Month);
1371
1372             if Is_Leap (Leap.Year)
1373               and then Leap.Month > 2
1374             then
1375                Days := Days + 1;
1376             end if;
1377
1378             Days := Days + Leap.Day;
1379
1380             --  Index - 1 previous leap seconds are added to Time (Index) as
1381             --  well as the lower buffer for time zones.
1382
1383             Leap_Second_Times (Index) := Ada_Low +
1384               (Time_Rep (Days) * Secs_In_Day + Time_Rep (Index - 1)) * Nano;
1385          end loop;
1386       end;
1387    end if;
1388 end Ada.Calendar;