OSDN Git Service

* config/pa/fptr.c: Update license header.
[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
189       Left_N : constant Time_Rep := Time_Rep (Left);
190       Res_N  : Time_Rep;
191
192    begin
193       --  Trivial case
194
195       if Right = Duration (0.0) then
196          return Left;
197       end if;
198
199       Res_N := Left_N + Duration_To_Time_Rep (Right);
200
201       Check_Within_Time_Bounds (Res_N);
202
203       return Time (Res_N);
204
205    exception
206       when Constraint_Error =>
207          raise Time_Error;
208    end "+";
209
210    function "+" (Left : Duration; Right : Time) return Time is
211    begin
212       return Right + Left;
213    end "+";
214
215    ---------
216    -- "-" --
217    ---------
218
219    function "-" (Left : Time; Right : Duration) return Time is
220       pragma Unsuppress (Overflow_Check);
221
222       Left_N : constant Time_Rep := Time_Rep (Left);
223       Res_N  : Time_Rep;
224
225    begin
226       --  Trivial case
227
228       if Right = Duration (0.0) then
229          return Left;
230       end if;
231
232       Res_N := Left_N - Duration_To_Time_Rep (Right);
233
234       Check_Within_Time_Bounds (Res_N);
235
236       return Time (Res_N);
237
238    exception
239       when Constraint_Error =>
240          raise Time_Error;
241    end "-";
242
243    function "-" (Left : Time; Right : Time) return Duration is
244       pragma Unsuppress (Overflow_Check);
245
246       --  The bounds of type Duration expressed as time representations
247
248       Dur_Low  : constant Time_Rep := Duration_To_Time_Rep (Duration'First);
249       Dur_High : constant Time_Rep := Duration_To_Time_Rep (Duration'Last);
250
251       Res_N : Time_Rep;
252
253    begin
254       Res_N := Time_Rep (Left) - Time_Rep (Right);
255
256       --  The result does not fit in a duration value
257
258       if Res_N < Dur_Low
259         or else Res_N > Dur_High
260       then
261          raise Time_Error;
262       end if;
263
264       return Time_Rep_To_Duration (Res_N);
265    exception
266       when Constraint_Error =>
267          raise Time_Error;
268    end "-";
269
270    ---------
271    -- "<" --
272    ---------
273
274    function "<" (Left, Right : Time) return Boolean is
275    begin
276       return Time_Rep (Left) < Time_Rep (Right);
277    end "<";
278
279    ----------
280    -- "<=" --
281    ----------
282
283    function "<=" (Left, Right : Time) return Boolean is
284    begin
285       return Time_Rep (Left) <= Time_Rep (Right);
286    end "<=";
287
288    ---------
289    -- ">" --
290    ---------
291
292    function ">" (Left, Right : Time) return Boolean is
293    begin
294       return Time_Rep (Left) > Time_Rep (Right);
295    end ">";
296
297    ----------
298    -- ">=" --
299    ----------
300
301    function ">=" (Left, Right : Time) return Boolean is
302    begin
303       return Time_Rep (Left) >= Time_Rep (Right);
304    end ">=";
305
306    ------------------------------
307    -- Check_Within_Time_Bounds --
308    ------------------------------
309
310    procedure Check_Within_Time_Bounds (T : Time_Rep) is
311    begin
312       if Leap_Support then
313          if T < Ada_Low or else T > Ada_High_And_Leaps then
314             raise Time_Error;
315          end if;
316       else
317          if T < Ada_Low or else T > Ada_High then
318             raise Time_Error;
319          end if;
320       end if;
321    end Check_Within_Time_Bounds;
322
323    -----------
324    -- Clock --
325    -----------
326
327    function Clock return Time is
328       Elapsed_Leaps : Natural;
329       Next_Leap_N   : Time_Rep;
330
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.
334
335       Res_N : Time_Rep :=
336                 Duration_To_Time_Rep (System.OS_Primitives.Clock) +
337                   Unix_Min;
338
339    begin
340       --  If the target supports leap seconds, determine the number of leap
341       --  seconds elapsed until this moment.
342
343       if Leap_Support then
344          Cumulative_Leap_Seconds
345            (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
346
347          --  The system clock may fall exactly on a leap second
348
349          if Res_N >= Next_Leap_N then
350             Elapsed_Leaps := Elapsed_Leaps + 1;
351          end if;
352
353       --  The target does not support leap seconds
354
355       else
356          Elapsed_Leaps := 0;
357       end if;
358
359       Res_N := Res_N + Time_Rep (Elapsed_Leaps) * Nano;
360
361       return Time (Res_N);
362    end Clock;
363
364    -----------------------------
365    -- Cumulative_Leap_Seconds --
366    -----------------------------
367
368    procedure Cumulative_Leap_Seconds
369      (Start_Date    : Time_Rep;
370       End_Date      : Time_Rep;
371       Elapsed_Leaps : out Natural;
372       Next_Leap     : out Time_Rep)
373    is
374       End_Index   : Positive;
375       End_T       : Time_Rep := End_Date;
376       Start_Index : Positive;
377       Start_T     : Time_Rep := Start_Date;
378
379    begin
380       --  Both input dates must be normalized to UTC
381
382       pragma Assert (Leap_Support and then End_Date >= Start_Date);
383
384       Next_Leap := End_Of_Time;
385
386       --  Make sure that the end date does not excede the upper bound
387       --  of Ada time.
388
389       if End_Date > Ada_High then
390          End_T := Ada_High;
391       end if;
392
393       --  Remove the sub seconds from both dates
394
395       Start_T := Start_T - (Start_T mod Nano);
396       End_T   := End_T   - (End_T   mod Nano);
397
398       --  Some trivial cases:
399       --                     Leap 1 . . . Leap N
400       --  ---+========+------+############+-------+========+-----
401       --     Start_T  End_T                       Start_T  End_T
402
403       if End_T < Leap_Second_Times (1) then
404          Elapsed_Leaps := 0;
405          Next_Leap     := Leap_Second_Times (1);
406          return;
407
408       elsif Start_T > Leap_Second_Times (Leap_Seconds_Count) then
409          Elapsed_Leaps := 0;
410          Next_Leap     := End_Of_Time;
411          return;
412       end if;
413
414       --  Perform the calculations only if the start date is within the leap
415       --  second occurences table.
416
417       if Start_T <= Leap_Second_Times (Leap_Seconds_Count) then
418
419          --    1    2                  N - 1   N
420          --  +----+----+--  . . .  --+-------+---+
421          --  | T1 | T2 |             | N - 1 | N |
422          --  +----+----+--  . . .  --+-------+---+
423          --         ^                   ^
424          --         | Start_Index       | End_Index
425          --         +-------------------+
426          --             Leaps_Between
427
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
431          --  seconds elapsed.
432
433          Start_Index := 1;
434          loop
435             exit when Leap_Second_Times (Start_Index) >= Start_T;
436             Start_Index := Start_Index + 1;
437          end loop;
438
439          End_Index := Start_Index;
440          loop
441             exit when End_Index > Leap_Seconds_Count
442               or else Leap_Second_Times (End_Index) >= End_T;
443             End_Index := End_Index + 1;
444          end loop;
445
446          if End_Index <= Leap_Seconds_Count then
447             Next_Leap := Leap_Second_Times (End_Index);
448          end if;
449
450          Elapsed_Leaps := End_Index - Start_Index;
451
452       else
453          Elapsed_Leaps := 0;
454       end if;
455    end Cumulative_Leap_Seconds;
456
457    ---------
458    -- Day --
459    ---------
460
461    function Day (Date : Time) return Day_Number is
462       Y : Year_Number;
463       M : Month_Number;
464       D : Day_Number;
465       S : Day_Duration;
466    begin
467       Split (Date, Y, M, D, S);
468       return D;
469    end Day;
470
471    -------------
472    -- Is_Leap --
473    -------------
474
475    function Is_Leap (Year : Year_Number) return Boolean is
476    begin
477       --  Leap centenial years
478
479       if Year mod 400 = 0 then
480          return True;
481
482       --  Non-leap centenial years
483
484       elsif Year mod 100 = 0 then
485          return False;
486
487       --  Regular years
488
489       else
490          return Year mod 4 = 0;
491       end if;
492    end Is_Leap;
493
494    -----------
495    -- Month --
496    -----------
497
498    function Month (Date : Time) return Month_Number is
499       Y : Year_Number;
500       M : Month_Number;
501       D : Day_Number;
502       S : Day_Duration;
503    begin
504       Split (Date, Y, M, D, S);
505       return M;
506    end Month;
507
508    -------------
509    -- Seconds --
510    -------------
511
512    function Seconds (Date : Time) return Day_Duration is
513       Y : Year_Number;
514       M : Month_Number;
515       D : Day_Number;
516       S : Day_Duration;
517    begin
518       Split (Date, Y, M, D, S);
519       return S;
520    end Seconds;
521
522    -----------
523    -- Split --
524    -----------
525
526    procedure Split
527      (Date    : Time;
528       Year    : out Year_Number;
529       Month   : out Month_Number;
530       Day     : out Day_Number;
531       Seconds : out Day_Duration)
532    is
533       H  : Integer;
534       M  : Integer;
535       Se : Integer;
536       Ss : Duration;
537       Le : Boolean;
538
539    begin
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.
542
543       Formatting_Operations.Split
544         (Date      => Date,
545          Year      => Year,
546          Month     => Month,
547          Day       => Day,
548          Day_Secs  => Seconds,
549          Hour      => H,
550          Minute    => M,
551          Second    => Se,
552          Sub_Sec   => Ss,
553          Leap_Sec  => Le,
554          Is_Ada_05 => False,
555          Time_Zone => 0);
556
557       --  Validity checks
558
559       if not Year'Valid
560         or else not Month'Valid
561         or else not Day'Valid
562         or else not Seconds'Valid
563       then
564          raise Time_Error;
565       end if;
566    end Split;
567
568    -------------
569    -- Time_Of --
570    -------------
571
572    function Time_Of
573      (Year    : Year_Number;
574       Month   : Month_Number;
575       Day     : Day_Number;
576       Seconds : Day_Duration := 0.0) return Time
577    is
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.
581
582       H  : constant Integer := 1;
583       M  : constant Integer := 1;
584       Se : constant Integer := 1;
585       Ss : constant Duration := 0.1;
586
587    begin
588       --  Validity checks
589
590       if not Year'Valid
591         or else not Month'Valid
592         or else not Day'Valid
593         or else not Seconds'Valid
594       then
595          raise Time_Error;
596       end if;
597
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.
600
601       return
602         Formatting_Operations.Time_Of
603           (Year         => Year,
604            Month        => Month,
605            Day          => Day,
606            Day_Secs     => Seconds,
607            Hour         => H,
608            Minute       => M,
609            Second       => Se,
610            Sub_Sec      => Ss,
611            Leap_Sec     => False,
612            Use_Day_Secs => True,
613            Is_Ada_05    => False,
614            Time_Zone    => 0);
615    end Time_Of;
616
617    ----------
618    -- Year --
619    ----------
620
621    function Year (Date : Time) return Year_Number is
622       Y : Year_Number;
623       M : Month_Number;
624       D : Day_Number;
625       S : Day_Duration;
626    begin
627       Split (Date, Y, M, D, S);
628       return Y;
629    end Year;
630
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).
634
635    ---------------------------
636    -- Arithmetic_Operations --
637    ---------------------------
638
639    package body Arithmetic_Operations is
640
641       ---------
642       -- Add --
643       ---------
644
645       function Add (Date : Time; Days : Long_Integer) return Time is
646          pragma Unsuppress (Overflow_Check);
647
648          Date_N : constant Time_Rep := Time_Rep (Date);
649          Res_N  : Time_Rep;
650
651       begin
652          --  Trivial case
653
654          if Days = 0 then
655             return Date;
656          end if;
657
658          Res_N := Date_N + Time_Rep (Days) * Nanos_In_Day;
659
660          Check_Within_Time_Bounds (Res_N);
661
662          return Time (Res_N);
663
664       exception
665          when Constraint_Error =>
666             raise Time_Error;
667       end Add;
668
669       ----------------
670       -- Difference --
671       ----------------
672
673       procedure Difference
674         (Left         : Time;
675          Right        : Time;
676          Days         : out Long_Integer;
677          Seconds      : out Duration;
678          Leap_Seconds : out Integer)
679       is
680          Res_Dur       : Time_Dur;
681          Earlier       : Time_Rep;
682          Earlier_Sub   : Time_Rep;
683          Elapsed_Leaps : Natural;
684          Later         : Time_Rep;
685          Later_Sub     : Time_Rep;
686          Negate        : Boolean := False;
687          Next_Leap_N   : Time_Rep;
688          Sub_Seconds   : Duration;
689
690       begin
691          --  Both input time values are assumed to be in UTC
692
693          if Left >= Right then
694             Later   := Time_Rep (Left);
695             Earlier := Time_Rep (Right);
696          else
697             Later   := Time_Rep (Right);
698             Earlier := Time_Rep (Left);
699             Negate  := True;
700          end if;
701
702          --  If the target supports leap seconds, process them
703
704          if Leap_Support then
705             Cumulative_Leap_Seconds
706               (Earlier, Later, Elapsed_Leaps, Next_Leap_N);
707
708             if Later >= Next_Leap_N then
709                Elapsed_Leaps := Elapsed_Leaps + 1;
710             end if;
711
712          --  The target does not support leap seconds
713
714          else
715             Elapsed_Leaps := 0;
716          end if;
717
718          --  Sub seconds
719
720          Earlier_Sub := Earlier mod Nano;
721          Later_Sub   := Later mod Nano;
722
723          if Later_Sub < Earlier_Sub then
724             Later_Sub := Later_Sub + Time_Rep (1) * Nano;
725             Later     := Later - Time_Rep (1) * Nano;
726          end if;
727
728          Sub_Seconds := Duration (Later_Sub - Earlier_Sub) / Nano_F;
729
730          Res_Dur := Time_Dur (Later / Nano - Earlier / Nano) -
731                     Time_Dur (Elapsed_Leaps);
732
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);
736
737          if Negate then
738             Days    := -Days;
739             Seconds := -Seconds;
740
741             if Leap_Seconds /= 0 then
742                Leap_Seconds := -Leap_Seconds;
743             end if;
744          end if;
745       end Difference;
746
747       --------------
748       -- Subtract --
749       --------------
750
751       function Subtract (Date : Time; Days : Long_Integer) return Time is
752          pragma Unsuppress (Overflow_Check);
753
754          Date_N : constant Time_Rep := Time_Rep (Date);
755          Res_N  : Time_Rep;
756
757       begin
758          --  Trivial case
759
760          if Days = 0 then
761             return Date;
762          end if;
763
764          Res_N := Date_N - Time_Rep (Days) * Nanos_In_Day;
765
766          Check_Within_Time_Bounds (Res_N);
767
768          return Time (Res_N);
769
770       exception
771          when Constraint_Error =>
772             raise Time_Error;
773       end Subtract;
774    end Arithmetic_Operations;
775
776    ----------------------
777    -- Delay_Operations --
778    ----------------------
779
780    package body Delays_Operations is
781
782       -----------------
783       -- To_Duration --
784       -----------------
785
786       function To_Duration (Date : Time) return Duration is
787          Elapsed_Leaps : Natural;
788          Next_Leap_N   : Time_Rep;
789          Res_N         : Time_Rep;
790
791       begin
792          Res_N := Time_Rep (Date);
793
794          --  If the target supports leap seconds, remove any leap seconds
795          --  elapsed upto the input date.
796
797          if Leap_Support then
798             Cumulative_Leap_Seconds
799               (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
800
801             --  The input time value may fall on a leap second occurence
802
803             if Res_N >= Next_Leap_N then
804                Elapsed_Leaps := Elapsed_Leaps + 1;
805             end if;
806
807          --  The target does not support leap seconds
808
809          else
810             Elapsed_Leaps := 0;
811          end if;
812
813          Res_N := Res_N - Time_Rep (Elapsed_Leaps) * Nano;
814
815          --  Perform a shift in origins, note that enforcing type Time on
816          --  both operands will invoke Ada.Calendar."-".
817
818          return Time (Res_N) - Time (Unix_Min);
819       end To_Duration;
820    end Delays_Operations;
821
822    ---------------------------
823    -- Formatting_Operations --
824    ---------------------------
825
826    package body Formatting_Operations is
827
828       -----------------
829       -- Day_Of_Week --
830       -----------------
831
832       function Day_Of_Week (Date : Time) return Integer is
833          Y  : Year_Number;
834          Mo : Month_Number;
835          D  : Day_Number;
836          Ds : Day_Duration;
837          H  : Integer;
838          Mi : Integer;
839          Se : Integer;
840          Su : Duration;
841          Le : Boolean;
842
843          Day_Count : Long_Integer;
844          Res_Dur   : Time_Dur;
845          Res_N     : Time_Rep;
846
847       begin
848          Formatting_Operations.Split
849            (Date      => Date,
850             Year      => Y,
851             Month     => Mo,
852             Day       => D,
853             Day_Secs  => Ds,
854             Hour      => H,
855             Minute    => Mi,
856             Second    => Se,
857             Sub_Sec   => Su,
858             Leap_Sec  => Le,
859             Is_Ada_05 => True,
860             Time_Zone => 0);
861
862          --  Build a time value in the middle of the same day
863
864          Res_N :=
865            Time_Rep
866              (Formatting_Operations.Time_Of
867                (Year         => Y,
868                 Month        => Mo,
869                 Day          => D,
870                 Day_Secs     => 0.0,
871                 Hour         => 12,
872                 Minute       => 0,
873                 Second       => 0,
874                 Sub_Sec      => 0.0,
875                 Leap_Sec     => False,
876                 Use_Day_Secs => False,
877                 Is_Ada_05    => True,
878                 Time_Zone    => 0));
879
880          --  Determine the elapsed seconds since the start of Ada time
881
882          Res_Dur := Time_Dur (Res_N / Nano - Ada_Low / Nano);
883
884          --  Count the number of days since the start of Ada time. 1901-1-1
885          --  GMT was a Tuesday.
886
887          Day_Count := Long_Integer (Res_Dur / Secs_In_Day) + 1;
888
889          return Integer (Day_Count mod 7);
890       end Day_Of_Week;
891
892       -----------
893       -- Split --
894       -----------
895
896       procedure Split
897         (Date      : Time;
898          Year      : out Year_Number;
899          Month     : out Month_Number;
900          Day       : out Day_Number;
901          Day_Secs  : out Day_Duration;
902          Hour      : out Integer;
903          Minute    : out Integer;
904          Second    : out Integer;
905          Sub_Sec   : out Duration;
906          Leap_Sec  : out Boolean;
907          Is_Ada_05 : Boolean;
908          Time_Zone : Long_Integer)
909       is
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.
913
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;
920
921          Date_Dur       : Time_Dur;
922          Date_N         : Time_Rep;
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;
929          Rem_Years      : Natural;
930          Sub_Sec_N      : Time_Rep;
931          Year_Day       : Natural;
932
933       begin
934          Date_N := Time_Rep (Date);
935
936          --  Step 1: Leap seconds processing in UTC
937
938          if Leap_Support then
939             Cumulative_Leap_Seconds
940               (Start_Of_Time, Date_N, Elapsed_Leaps, Next_Leap_N);
941
942             Leap_Sec := Date_N >= Next_Leap_N;
943
944             if Leap_Sec then
945                Elapsed_Leaps := Elapsed_Leaps + 1;
946             end if;
947
948          --  The target does not support leap seconds
949
950          else
951             Elapsed_Leaps := 0;
952             Leap_Sec      := False;
953          end if;
954
955          Date_N := Date_N - Time_Rep (Elapsed_Leaps) * Nano;
956
957          --  Step 2: Time zone processing. This action converts the input date
958          --  from GMT to the requested time zone.
959
960          if Is_Ada_05 then
961             if Time_Zone /= 0 then
962                Date_N := Date_N + Time_Rep (Time_Zone) * 60 * Nano;
963             end if;
964
965          --  Ada 83 and 95
966
967          else
968             declare
969                Off : constant Long_Integer :=
970                        Time_Zones_Operations.UTC_Time_Offset (Time (Date_N));
971             begin
972                Date_N := Date_N + Time_Rep (Off) * Nano;
973             end;
974          end if;
975
976          --  Step 3: Non-leap centenial year adjustment in local time zone
977
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.
981
982          if Date_N >= Year_2301 then
983             Date_N := Date_N + Time_Rep (3) * Nanos_In_Day;
984
985          elsif Date_N >= Year_2201 then
986             Date_N := Date_N + Time_Rep (2) * Nanos_In_Day;
987
988          elsif Date_N >= Year_2101 then
989             Date_N := Date_N + Time_Rep (1) * Nanos_In_Day;
990          end if;
991
992          --  Step 4: Sub second processing in local time zone
993
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;
997
998          --  Convert Date_N into a time duration value, changing the units
999          --  to seconds.
1000
1001          Date_Dur := Time_Dur (Date_N / Nano - Ada_Low / Nano);
1002
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
1005          --  date.
1006
1007          Four_Year_Segs := Natural (Date_Dur / Secs_In_Four_Years);
1008
1009          if Four_Year_Segs > 0 then
1010             Date_Dur := Date_Dur - Time_Dur (Four_Year_Segs) *
1011                                    Secs_In_Four_Years;
1012          end if;
1013
1014          --  Calculate the remaining non-leap years
1015
1016          Rem_Years := Natural (Date_Dur / Secs_In_Non_Leap_Year);
1017
1018          if Rem_Years > 3 then
1019             Rem_Years := 3;
1020          end if;
1021
1022          Date_Dur := Date_Dur - Time_Dur (Rem_Years) * Secs_In_Non_Leap_Year;
1023
1024          Year := Ada_Min_Year + Natural (4 * Four_Year_Segs + Rem_Years);
1025          Is_Leap_Year := Is_Leap (Year);
1026
1027          --  Step 6: Month and day processing in local time zone
1028
1029          Year_Day := Natural (Date_Dur / Secs_In_Day) + 1;
1030
1031          Month := 1;
1032
1033          --  Processing for months after January
1034
1035          if Year_Day > 31 then
1036             Month    := 2;
1037             Year_Day := Year_Day - 31;
1038
1039             --  Processing for a new month or a leap February
1040
1041             if Year_Day > 28
1042               and then (not Is_Leap_Year or else Year_Day > 29)
1043             then
1044                Month    := 3;
1045                Year_Day := Year_Day - 28;
1046
1047                if Is_Leap_Year then
1048                   Year_Day := Year_Day - 1;
1049                end if;
1050
1051                --  Remaining months
1052
1053                while Year_Day > Days_In_Month (Month) loop
1054                   Year_Day := Year_Day - Days_In_Month (Month);
1055                   Month    := Month + 1;
1056                end loop;
1057             end if;
1058          end if;
1059
1060          --  Step 7: Hour, minute, second and sub second processing in local
1061          --  time zone.
1062
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;
1070       end Split;
1071
1072       -------------
1073       -- Time_Of --
1074       -------------
1075
1076       function Time_Of
1077         (Year         : Year_Number;
1078          Month        : Month_Number;
1079          Day          : Day_Number;
1080          Day_Secs     : Day_Duration;
1081          Hour         : Integer;
1082          Minute       : Integer;
1083          Second       : Integer;
1084          Sub_Sec      : Duration;
1085          Leap_Sec     : Boolean;
1086          Use_Day_Secs : Boolean;
1087          Is_Ada_05    : Boolean;
1088          Time_Zone    : Long_Integer) return Time
1089       is
1090          Count         : Integer;
1091          Elapsed_Leaps : Natural;
1092          Next_Leap_N   : Time_Rep;
1093          Res_N         : Time_Rep;
1094          Rounded_Res_N : Time_Rep;
1095
1096       begin
1097          --  Step 1: Check whether the day, month and year form a valid date
1098
1099          if Day > Days_In_Month (Month)
1100            and then (Day /= 29 or else Month /= 2 or else not Is_Leap (Year))
1101          then
1102             raise Time_Error;
1103          end if;
1104
1105          --  Start accumulating nanoseconds from the low bound of Ada time
1106
1107          Res_N := Ada_Low;
1108
1109          --  Step 2: Year processing and centenial year adjustment. Determine
1110          --  the number of four year segments since the start of Ada time and
1111          --  the input date.
1112
1113          Count := (Year - Year_Number'First) / 4;
1114          Res_N := Res_N + Time_Rep (Count) * Secs_In_Four_Years * Nano;
1115
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.
1119
1120          if Year > 2300 then
1121             Res_N := Res_N - Time_Rep (3) * Nanos_In_Day;
1122
1123          elsif Year > 2200 then
1124             Res_N := Res_N - Time_Rep (2) * Nanos_In_Day;
1125
1126          elsif Year > 2100 then
1127             Res_N := Res_N - Time_Rep (1) * Nanos_In_Day;
1128          end if;
1129
1130          --  Add the remaining non-leap years
1131
1132          Count := (Year - Year_Number'First) mod 4;
1133          Res_N := Res_N + Time_Rep (Count) * Secs_In_Non_Leap_Year * Nano;
1134
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.
1138
1139          Count := Cumulative_Days_Before_Month (Month) + Day - 1;
1140
1141          --  The input year is leap and we have passed February
1142
1143          if Is_Leap (Year)
1144            and then Month > 2
1145          then
1146             Count := Count + 1;
1147          end if;
1148
1149          Res_N := Res_N + Time_Rep (Count) * Nanos_In_Day;
1150
1151          --  Step 4: Hour, minute, second and sub second processing
1152
1153          if Use_Day_Secs then
1154             Res_N := Res_N + Duration_To_Time_Rep (Day_Secs);
1155
1156          else
1157             Res_N := Res_N +
1158               Time_Rep (Hour * 3_600 + Minute * 60 + Second) * Nano;
1159
1160             if Sub_Sec = 1.0 then
1161                Res_N := Res_N + Time_Rep (1) * Nano;
1162             else
1163                Res_N := Res_N + Duration_To_Time_Rep (Sub_Sec);
1164             end if;
1165          end if;
1166
1167          --  At this point, the generated time value should be withing the
1168          --  bounds of Ada time.
1169
1170          Check_Within_Time_Bounds (Res_N);
1171
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.
1177
1178          if Is_Ada_05 then
1179             if Time_Zone /= 0 then
1180                Res_N := Res_N - Time_Rep (Time_Zone) * 60 * Nano;
1181             end if;
1182
1183          --  Ada 83 and 95
1184
1185          else
1186             declare
1187                Current_Off   : constant Long_Integer :=
1188                                  Time_Zones_Operations.UTC_Time_Offset
1189                                    (Time (Res_N));
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));
1195             begin
1196                Res_N := Res_N - Time_Rep (Off) * Nano;
1197             end;
1198          end if;
1199
1200          --  Step 5: Leap seconds processing in GMT
1201
1202          if Leap_Support then
1203             Cumulative_Leap_Seconds
1204               (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
1205
1206             Res_N := Res_N + Time_Rep (Elapsed_Leaps) * Nano;
1207
1208             --  An Ada 2005 caller requesting an explicit leap second or an
1209             --  Ada 95 caller accounting for an invisible leap second.
1210
1211             if Leap_Sec
1212               or else Res_N >= Next_Leap_N
1213             then
1214                Res_N := Res_N + Time_Rep (1) * Nano;
1215             end if;
1216
1217             --  Leap second validity check
1218
1219             Rounded_Res_N := Res_N - (Res_N mod Nano);
1220
1221             if Is_Ada_05
1222               and then Leap_Sec
1223               and then Rounded_Res_N /= Next_Leap_N
1224             then
1225                raise Time_Error;
1226             end if;
1227          end if;
1228
1229          return Time (Res_N);
1230       end Time_Of;
1231    end Formatting_Operations;
1232
1233    ---------------------------
1234    -- Time_Zones_Operations --
1235    ---------------------------
1236
1237    package body Time_Zones_Operations is
1238
1239       --  The Unix time bounds in nanoseconds: 1970/1/1 .. 2037/1/1
1240
1241       Unix_Min : constant Time_Rep := Ada_Low +
1242                    Time_Rep (17 * 366 +  52 * 365) * Nanos_In_Day;
1243
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;
1247
1248       --  The following constants denote February 28 during non-leap
1249       --  centenial years, the units are nanoseconds.
1250
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;
1254
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;
1258
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;
1262
1263       --  56 years (14 leap years + 42 non leap years) in nanoseconds:
1264
1265       Nanos_In_56_Years : constant := (14 * 366 + 42 * 365) * Nanos_In_Day;
1266
1267       --  Base C types. There is no point dragging in Interfaces.C just for
1268       --  these four types.
1269
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;
1274
1275       --  The Ada equivalent of struct tm and type time_t
1276
1277       type tm is record
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
1289       end record;
1290
1291       type tm_Pointer is access all tm;
1292
1293       subtype time_t is long;
1294       type time_t_Pointer is access all time_t;
1295
1296       procedure localtime_tzoff
1297        (C   : time_t_Pointer;
1298         res : tm_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.
1305
1306       ---------------------
1307       -- UTC_Time_Offset --
1308       ---------------------
1309
1310       function UTC_Time_Offset (Date : Time) return Long_Integer is
1311          Adj_Cent : Integer := 0;
1312          Date_N   : Time_Rep;
1313          Offset   : aliased long;
1314          Secs_T   : aliased time_t;
1315          Secs_TM  : aliased tm;
1316
1317       begin
1318          Date_N := Time_Rep (Date);
1319
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.
1323
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
1327                   Adj_Cent := 3;
1328                else
1329                   Adj_Cent := 2;
1330                end if;
1331
1332             else
1333                Adj_Cent := 1;
1334             end if;
1335          end if;
1336
1337          if Adj_Cent > 0 then
1338             Date_N := Date_N - Time_Rep (Adj_Cent) * Nanos_In_Day;
1339          end if;
1340
1341          --  Shift the date within bounds of Unix time
1342
1343          while Date_N < Unix_Min loop
1344             Date_N := Date_N + Nanos_In_56_Years;
1345          end loop;
1346
1347          while Date_N >= Unix_Max loop
1348             Date_N := Date_N - Nanos_In_56_Years;
1349          end loop;
1350
1351          --  Perform a shift in origins from Ada to Unix
1352
1353          Date_N := Date_N - Unix_Min;
1354
1355          --  Convert the date into seconds
1356
1357          Secs_T := time_t (Date_N / Nano);
1358
1359          localtime_tzoff
1360            (Secs_T'Unchecked_Access,
1361             Secs_TM'Unchecked_Access,
1362             Offset'Unchecked_Access);
1363
1364          return Offset;
1365       end UTC_Time_Offset;
1366    end Time_Zones_Operations;
1367
1368 --  Start of elaboration code for Ada.Calendar
1369
1370 begin
1371    System.OS_Primitives.Initialize;
1372
1373    --  Population of the leap seconds table
1374
1375    if Leap_Support then
1376       declare
1377          type Leap_Second_Date is record
1378             Year  : Year_Number;
1379             Month : Month_Number;
1380             Day   : Day_Number;
1381          end record;
1382
1383          Leap_Second_Dates :
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));
1391
1392          Days_In_Four_Years : constant := 365 * 3 + 366;
1393
1394          Days  : Natural;
1395          Leap  : Leap_Second_Date;
1396          Years : Natural;
1397
1398       begin
1399          for Index in 1 .. Leap_Seconds_Count loop
1400             Leap := Leap_Second_Dates (Index);
1401
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.
1406
1407             Years := Leap.Year - Ada_Min_Year;
1408             Days  := (Years / 4) * Days_In_Four_Years;
1409             Years := Years mod 4;
1410
1411             if Years = 1 then
1412                Days := Days + 365;
1413
1414             elsif Years = 2 then
1415                Days := Days + 365 * 2;
1416
1417             elsif Years = 3 then
1418                Days := Days + 365 * 3;
1419             end if;
1420
1421             Days := Days + Cumulative_Days_Before_Month (Leap.Month);
1422
1423             if Is_Leap (Leap.Year)
1424               and then Leap.Month > 2
1425             then
1426                Days := Days + 1;
1427             end if;
1428
1429             Days := Days + Leap.Day;
1430
1431             --  Index - 1 previous leap seconds are added to Time (Index) as
1432             --  well as the lower buffer for time zones.
1433
1434             Leap_Second_Times (Index) := Ada_Low +
1435               (Time_Rep (Days) * Secs_In_Day + Time_Rep (Index - 1)) * Nano;
1436          end loop;
1437       end;
1438    end if;
1439
1440 end Ada.Calendar;