OSDN Git Service

Remove duplicate entries.
[pf3gnuchains/gcc-fork.git] / gcc / ada / a-calend.adb
index a2759b5..f79b9d1 100644 (file)
@@ -6,7 +6,7 @@
 --                                                                          --
 --                                 B o d y                                  --
 --                                                                          --
---          Copyright (C) 1992-2009, Free Software Foundation, Inc.         --
+--          Copyright (C) 1992-2012, Free Software Foundation, Inc.         --
 --                                                                          --
 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
 -- terms of the  GNU General Public License as published  by the Free Soft- --
@@ -31,6 +31,8 @@
 
 with Ada.Unchecked_Conversion;
 
+with Interfaces.C;
+
 with System.OS_Primitives;
 
 package body Ada.Calendar is
@@ -109,6 +111,22 @@ package body Ada.Calendar is
      new Ada.Unchecked_Conversion (Time_Rep, Duration);
    --  Convert a time representation value into a duration value
 
+   function UTC_Time_Offset
+     (Date        : Time;
+      Is_Historic : Boolean) return Long_Integer;
+   --  This routine acts as an Ada wrapper around __gnat_localtime_tzoff which
+   --  in turn utilizes various OS-dependent mechanisms to calculate the time
+   --  zone offset of a date. Formal parameter Date represents an arbitrary
+   --  time stamp, either in the past, now, or in the future. If the flag
+   --  Is_Historic is set, this routine would try to calculate to the best of
+   --  the OS's abilities the time zone offset that was or will be in effect
+   --  on Date. If the flag is set to False, the routine returns the current
+   --  time zone with Date effectively set to Clock.
+   --
+   --  NOTE: Targets which support localtime_r will aways return a historic
+   --  time zone even if flag Is_Historic is set to False because this is how
+   --  localtime_r operates.
+
    -----------------
    -- Local Types --
    -----------------
@@ -132,14 +150,12 @@ package body Ada.Calendar is
    pragma Import (C, Flag, "__gl_leap_seconds_support");
    --  This imported value is used to determine whether the compilation had
    --  binder flag "-y" present which enables leap seconds. A value of zero
-   --  signifies no leap seconds support while a value of one enables the
-   --  support.
+   --  signifies no leap seconds support while a value of one enables support.
 
-   Leap_Support : constant Boolean := Flag = 1;
-   --  The above flag controls the usage of leap seconds in all Ada.Calendar
-   --  routines.
+   Leap_Support : constant Boolean := (Flag = 1);
+   --  Flag to controls the usage of leap seconds in all Ada.Calendar routines
 
-   Leap_Seconds_Count : constant Natural := 23;
+   Leap_Seconds_Count : constant Natural := 25;
 
    ---------------------
    -- Local Constants --
@@ -148,6 +164,7 @@ package body Ada.Calendar is
    Ada_Min_Year          : constant Year_Number := Year_Number'First;
    Secs_In_Four_Years    : constant := (3 * 365 + 366) * Secs_In_Day;
    Secs_In_Non_Leap_Year : constant := 365 * Secs_In_Day;
+   Nanos_In_Four_Years   : constant := Secs_In_Four_Years * Nano;
 
    --  Lower and upper bound of Ada time. The zero (0) value of type Time is
    --  positioned at year 2150. Note that the lower and upper bound account
@@ -171,12 +188,19 @@ package body Ada.Calendar is
    Start_Of_Time : constant Time_Rep :=
                      Ada_Low - Time_Rep (3) * Nanos_In_Day;
 
-   --  The Unix lower time bound expressed as nanoseconds since the
-   --  start of Ada time in UTC.
+   --  The Unix lower time bound expressed as nanoseconds since the start of
+   --  Ada time in UTC.
 
    Unix_Min : constant Time_Rep :=
                 Ada_Low + Time_Rep (17 * 366 + 52 * 365) * Nanos_In_Day;
 
+   --  The Unix upper time bound expressed as nanoseconds since the start of
+   --  Ada time in UTC.
+
+   Unix_Max : constant Time_Rep :=
+                Ada_Low + Time_Rep (34 * 366 + 102 * 365) * Nanos_In_Day +
+                          Time_Rep (Leap_Seconds_Count) * Nano;
+
    Epoch_Offset : constant Time_Rep := (136 * 365 + 44 * 366) * Nanos_In_Day;
    --  The difference between 2150-1-1 UTC and 1970-1-1 UTC expressed in
    --  nanoseconds. Note that year 2100 is non-leap.
@@ -186,7 +210,8 @@ package body Ada.Calendar is
        (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
 
    --  The following table contains the hard time values of all existing leap
-   --  seconds. The values are produced by the utility program xleaps.adb.
+   --  seconds. The values are produced by the utility program xleaps.adb. This
+   --  must be updated when additional leap second times are defined.
 
    Leap_Second_Times : constant array (1 .. Leap_Seconds_Count) of Time_Rep :=
      (-5601484800000000000,
@@ -211,7 +236,9 @@ package body Ada.Calendar is
       -4859827181000000000,
       -4812566380000000000,
       -4765132779000000000,
-      -4544207978000000000);
+      -4544207978000000000,
+      -4449513577000000000,
+      -4339180776000000000);
 
    ---------
    -- "+" --
@@ -249,10 +276,9 @@ package body Ada.Calendar is
    function "-" (Left : Time; Right : Time) return Duration is
       pragma Unsuppress (Overflow_Check);
 
-      --  The bounds of type Duration expressed as time representations
-
       Dur_Low  : constant Time_Rep := Duration_To_Time_Rep (Duration'First);
       Dur_High : constant Time_Rep := Duration_To_Time_Rep (Duration'Last);
+      --  The bounds of type Duration expressed as time representations
 
       Res_N : Time_Rep;
 
@@ -264,13 +290,12 @@ package body Ada.Calendar is
       --  the generation of bogus values by the Unchecked_Conversion, we apply
       --  the following check.
 
-      if Res_N < Dur_Low
-        or else Res_N > Dur_High
-      then
+      if Res_N < Dur_Low or else Res_N > Dur_High then
          raise Time_Error;
       end if;
 
       return Time_Rep_To_Duration (Res_N);
+
    exception
       when Constraint_Error =>
          raise Time_Error;
@@ -342,8 +367,7 @@ package body Ada.Calendar is
       --  by adding the number of nanoseconds between the two origins.
 
       Res_N : Time_Rep :=
-                Duration_To_Time_Rep (System.OS_Primitives.Clock) +
-                  Unix_Min;
+                Duration_To_Time_Rep (System.OS_Primitives.Clock) + Unix_Min;
 
    begin
       --  If the target supports leap seconds, determine the number of leap
@@ -570,10 +594,10 @@ package body Ada.Calendar is
 
       --  Validity checks
 
-      if not Year'Valid
-        or else not Month'Valid
-        or else not Day'Valid
-        or else not Seconds'Valid
+      if not Year'Valid    or else
+         not Month'Valid   or else
+         not Day'Valid     or else
+         not Seconds'Valid
       then
          raise Time_Error;
       end if;
@@ -601,10 +625,10 @@ package body Ada.Calendar is
    begin
       --  Validity checks
 
-      if not Year'Valid
-        or else not Month'Valid
-        or else not Day'Valid
-        or else not Seconds'Valid
+      if not Year'Valid    or else
+         not Month'Valid   or else
+         not Day'Valid     or else
+         not Seconds'Valid
       then
          raise Time_Error;
       end if;
@@ -628,6 +652,110 @@ package body Ada.Calendar is
            Time_Zone    => 0);
    end Time_Of;
 
+   ---------------------
+   -- UTC_Time_Offset --
+   ---------------------
+
+   function UTC_Time_Offset
+     (Date        : Time;
+      Is_Historic : Boolean) return Long_Integer
+   is
+      --  The following constants denote February 28 during non-leap centennial
+      --  years, the units are nanoseconds.
+
+      T_2100_2_28 : constant Time_Rep := Ada_Low +
+                      (Time_Rep (49 * 366 + 150 * 365 + 59) * Secs_In_Day +
+                       Time_Rep (Leap_Seconds_Count)) * Nano;
+
+      T_2200_2_28 : constant Time_Rep := Ada_Low +
+                      (Time_Rep (73 * 366 + 226 * 365 + 59) * Secs_In_Day +
+                       Time_Rep (Leap_Seconds_Count)) * Nano;
+
+      T_2300_2_28 : constant Time_Rep := Ada_Low +
+                      (Time_Rep (97 * 366 + 302 * 365 + 59) * Secs_In_Day +
+                       Time_Rep (Leap_Seconds_Count)) * Nano;
+
+      --  56 years (14 leap years + 42 non-leap years) in nanoseconds:
+
+      Nanos_In_56_Years : constant := (14 * 366 + 42 * 365) * Nanos_In_Day;
+
+      type int_Pointer  is access all Interfaces.C.int;
+      type long_Pointer is access all Interfaces.C.long;
+
+      type time_t is
+        range -(2 ** (Standard'Address_Size - Integer'(1))) ..
+              +(2 ** (Standard'Address_Size - Integer'(1)) - 1);
+      type time_t_Pointer is access all time_t;
+
+      procedure localtime_tzoff
+        (timer       : time_t_Pointer;
+         is_historic : int_Pointer;
+         off         : long_Pointer);
+      pragma Import (C, localtime_tzoff, "__gnat_localtime_tzoff");
+      --  This routine is a interfacing wrapper around the library function
+      --  __gnat_localtime_tzoff. Parameter 'timer' represents a Unix-based
+      --  time equivalent of the input date. If flag 'is_historic' is set, this
+      --  routine would try to calculate to the best of the OS's abilities the
+      --  time zone offset that was or will be in effect on 'timer'. If the
+      --  flag is set to False, the routine returns the current time zone
+      --  regardless of what 'timer' designates. Parameter 'off' captures the
+      --  UTC offset of 'timer'.
+
+      Adj_Cent : Integer;
+      Date_N   : Time_Rep;
+      Flag     : aliased Interfaces.C.int;
+      Offset   : aliased Interfaces.C.long;
+      Secs_T   : aliased time_t;
+
+   --  Start of processing for UTC_Time_Offset
+
+   begin
+      Date_N := Time_Rep (Date);
+
+      --  Dates which are 56 years apart fall on the same day, day light saving
+      --  and so on. Non-leap centennial years violate this rule by one day and
+      --  as a consequence, special adjustment is needed.
+
+      Adj_Cent :=
+        (if    Date_N <= T_2100_2_28 then 0
+         elsif Date_N <= T_2200_2_28 then 1
+         elsif Date_N <= T_2300_2_28 then 2
+         else                             3);
+
+      if Adj_Cent > 0 then
+         Date_N := Date_N - Time_Rep (Adj_Cent) * Nanos_In_Day;
+      end if;
+
+      --  Shift the date within bounds of Unix time
+
+      while Date_N < Unix_Min loop
+         Date_N := Date_N + Nanos_In_56_Years;
+      end loop;
+
+      while Date_N >= Unix_Max loop
+         Date_N := Date_N - Nanos_In_56_Years;
+      end loop;
+
+      --  Perform a shift in origins from Ada to Unix
+
+      Date_N := Date_N - Unix_Min;
+
+      --  Convert the date into seconds
+
+      Secs_T := time_t (Date_N / Nano);
+
+      --  Determine whether to treat the input date as historical or not
+
+      Flag := (if Is_Historic then 1 else 0);
+
+      localtime_tzoff
+        (Secs_T'Unchecked_Access,
+         Flag'Unchecked_Access,
+         Offset'Unchecked_Access);
+
+      return Long_Integer (Offset);
+   end UTC_Time_Offset;
+
    ----------
    -- Year --
    ----------
@@ -813,12 +941,10 @@ package body Ada.Calendar is
 
          --  Step 1: Validity checks of input values
 
-         if not Year'Valid
-           or else not Month'Valid
-           or else not Day'Valid
-           or else tm_hour not in 0 .. 24
-           or else tm_min not in 0 .. 59
-           or else tm_sec not in 0 .. 60
+         if not Year'Valid or else not Month'Valid or else not Day'Valid
+           or else tm_hour  not in 0 .. 24
+           or else tm_min   not in 0 .. 59
+           or else tm_sec   not in 0 .. 60
            or else tm_isdst not in -1 .. 1
          then
             raise Time_Error;
@@ -938,11 +1064,7 @@ package body Ada.Calendar is
 
          --  Step 3: Handle leap second occurrences
 
-         if Leap_Sec then
-            tm_sec := 60;
-         else
-            tm_sec := Second;
-         end if;
+         tm_sec := (if Leap_Sec then 60 else Second);
       end To_Struct_Tm;
 
       ------------------
@@ -1012,11 +1134,8 @@ package body Ada.Calendar is
          --  the input. Guard against very large delay values such as the end
          --  of time since the computation will overflow.
 
-         if Res_N > Safe_Ada_High then
-            Res_N := Safe_Ada_High;
-         else
-            Res_N := Res_N + Epoch_Offset;
-         end if;
+         Res_N := (if Res_N > Safe_Ada_High then Safe_Ada_High
+                                            else Res_N + Epoch_Offset);
 
          return Time_Rep_To_Duration (Res_N);
       end To_Duration;
@@ -1034,63 +1153,38 @@ package body Ada.Calendar is
       -----------------
 
       function Day_Of_Week (Date : Time) return Integer is
-         Y  : Year_Number;
-         Mo : Month_Number;
-         D  : Day_Number;
-         Ds : Day_Duration;
-         H  : Integer;
-         Mi : Integer;
-         Se : Integer;
-         Su : Duration;
-         Le : Boolean;
-
-         pragma Unreferenced (Ds, H, Mi, Se, Su, Le);
-
+         Date_N    : constant Time_Rep := Time_Rep (Date);
+         Time_Zone : constant Long_Integer := UTC_Time_Offset (Date, True);
+         Ada_Low_N : Time_Rep;
          Day_Count : Long_Integer;
-         Res_Dur   : Time_Dur;
-         Res_N     : Time_Rep;
+         Day_Dur   : Time_Dur;
+         High_N    : Time_Rep;
+         Low_N     : Time_Rep;
 
       begin
-         Formatting_Operations.Split
-           (Date      => Date,
-            Year      => Y,
-            Month     => Mo,
-            Day       => D,
-            Day_Secs  => Ds,
-            Hour      => H,
-            Minute    => Mi,
-            Second    => Se,
-            Sub_Sec   => Su,
-            Leap_Sec  => Le,
-            Is_Ada_05 => True,
-            Time_Zone => 0);
-
-         --  Build a time value in the middle of the same day
-
-         Res_N :=
-           Time_Rep
-             (Formatting_Operations.Time_Of
-               (Year         => Y,
-                Month        => Mo,
-                Day          => D,
-                Day_Secs     => 0.0,
-                Hour         => 12,
-                Minute       => 0,
-                Second       => 0,
-                Sub_Sec      => 0.0,
-                Leap_Sec     => False,
-                Use_Day_Secs => False,
-                Is_Ada_05    => True,
-                Time_Zone    => 0));
+         --  As declared, the Ada Epoch is set in UTC. For this calculation to
+         --  work properly, both the Epoch and the input date must be in the
+         --  same time zone. The following places the Epoch in the input date's
+         --  time zone.
+
+         Ada_Low_N := Ada_Low - Time_Rep (Time_Zone) * Nano;
+
+         if Date_N > Ada_Low_N then
+            High_N := Date_N;
+            Low_N  := Ada_Low_N;
+         else
+            High_N := Ada_Low_N;
+            Low_N  := Date_N;
+         end if;
 
          --  Determine the elapsed seconds since the start of Ada time
 
-         Res_Dur := Time_Dur (Res_N / Nano - Ada_Low / Nano);
+         Day_Dur := Time_Dur (High_N / Nano - Low_N / Nano);
 
-         --  Count the number of days since the start of Ada time. 1901-1-1
+         --  Count the number of days since the start of Ada time. 1901-01-01
          --  GMT was a Tuesday.
 
-         Day_Count := Long_Integer (Res_Dur / Secs_In_Day) + 1;
+         Day_Count := Long_Integer (Day_Dur / Secs_In_Day) + 1;
 
          return Integer (Day_Count mod 7);
       end Day_Of_Week;
@@ -1161,7 +1255,7 @@ package body Ada.Calendar is
          Date_N := Date_N - Time_Rep (Elapsed_Leaps) * Nano;
 
          --  Step 2: Time zone processing. This action converts the input date
-         --  from GMT to the requested time zone.
+         --  from GMT to the requested time zone. Applies from Ada 2005 on.
 
          if Is_Ada_05 then
             if Time_Zone /= 0 then
@@ -1173,7 +1267,8 @@ package body Ada.Calendar is
          else
             declare
                Off : constant Long_Integer :=
-                       Time_Zones_Operations.UTC_Time_Offset (Time (Date_N));
+                       UTC_Time_Offset (Time (Date_N), False);
+
             begin
                Date_N := Date_N + Time_Rep (Off) * Nano;
             end;
@@ -1317,7 +1412,10 @@ package body Ada.Calendar is
          --  the input date.
 
          Count := (Year - Year_Number'First) / 4;
-         Res_N := Res_N + Time_Rep (Count) * Secs_In_Four_Years * Nano;
+
+         for Four_Year_Segments in 1 .. Count loop
+            Res_N := Res_N + Nanos_In_Four_Years;
+         end loop;
 
          --  Note that non-leap centennial years are automatically considered
          --  leap in the operation above. An adjustment of several days is
@@ -1360,8 +1458,8 @@ package body Ada.Calendar is
             Res_N := Res_N + Duration_To_Time_Rep (Day_Secs);
 
          else
-            Res_N := Res_N +
-              Time_Rep (Hour * 3_600 + Minute * 60 + Second) * Nano;
+            Res_N :=
+              Res_N + Time_Rep (Hour * 3_600 + Minute * 60 + Second) * Nano;
 
             if Sub_Sec = 1.0 then
                Res_N := Res_N + Time_Rep (1) * Nano;
@@ -1391,13 +1489,12 @@ package body Ada.Calendar is
          else
             declare
                Current_Off   : constant Long_Integer :=
-                                 Time_Zones_Operations.UTC_Time_Offset
-                                   (Time (Res_N));
+                                 UTC_Time_Offset (Time (Res_N), False);
                Current_Res_N : constant Time_Rep :=
                                  Res_N - Time_Rep (Current_Off) * Nano;
                Off           : constant Long_Integer :=
-                                 Time_Zones_Operations.UTC_Time_Offset
-                                   (Time (Current_Res_N));
+                                 UTC_Time_Offset (Time (Current_Res_N), False);
+
             begin
                Res_N := Res_N - Time_Rep (Off) * Nano;
             end;
@@ -1414,9 +1511,7 @@ package body Ada.Calendar is
             --  An Ada 2005 caller requesting an explicit leap second or an
             --  Ada 95 caller accounting for an invisible leap second.
 
-            if Leap_Sec
-              or else Res_N >= Next_Leap_N
-            then
+            if Leap_Sec or else Res_N >= Next_Leap_N then
                Res_N := Res_N + Time_Rep (1) * Nano;
             end if;
 
@@ -1443,132 +1538,13 @@ package body Ada.Calendar is
 
    package body Time_Zones_Operations is
 
-      --  The Unix time bounds in nanoseconds: 1970/1/1 .. 2037/1/1
-
-      Unix_Min : constant Time_Rep := Ada_Low +
-                   Time_Rep (17 * 366 +  52 * 365) * Nanos_In_Day;
-
-      Unix_Max : constant Time_Rep := Ada_Low +
-                   Time_Rep (34 * 366 + 102 * 365) * Nanos_In_Day +
-                   Time_Rep (Leap_Seconds_Count) * Nano;
-
-      --  The following constants denote February 28 during non-leap
-      --  centennial years, the units are nanoseconds.
-
-      T_2100_2_28 : constant Time_Rep := Ada_Low +
-                      (Time_Rep (49 * 366 + 150 * 365 + 59) * Secs_In_Day +
-                       Time_Rep (Leap_Seconds_Count)) * Nano;
-
-      T_2200_2_28 : constant Time_Rep := Ada_Low +
-                      (Time_Rep (73 * 366 + 226 * 365 + 59) * Secs_In_Day +
-                       Time_Rep (Leap_Seconds_Count)) * Nano;
-
-      T_2300_2_28 : constant Time_Rep := Ada_Low +
-                      (Time_Rep (97 * 366 + 302 * 365 + 59) * Secs_In_Day +
-                       Time_Rep (Leap_Seconds_Count)) * Nano;
-
-      --  56 years (14 leap years + 42 non leap years) in nanoseconds:
-
-      Nanos_In_56_Years : constant := (14 * 366 + 42 * 365) * Nanos_In_Day;
-
-      --  Base C types. There is no point dragging in Interfaces.C just for
-      --  these four types.
-
-      type char_Pointer is access Character;
-      subtype int is Integer;
-      subtype long is Long_Integer;
-      type long_Pointer is access all long;
-
-      --  The Ada equivalent of struct tm and type time_t
-
-      type tm is record
-         tm_sec    : int;           --  seconds after the minute (0 .. 60)
-         tm_min    : int;           --  minutes after the hour (0 .. 59)
-         tm_hour   : int;           --  hours since midnight (0 .. 24)
-         tm_mday   : int;           --  day of the month (1 .. 31)
-         tm_mon    : int;           --  months since January (0 .. 11)
-         tm_year   : int;           --  years since 1900
-         tm_wday   : int;           --  days since Sunday (0 .. 6)
-         tm_yday   : int;           --  days since January 1 (0 .. 365)
-         tm_isdst  : int;           --  Daylight Savings Time flag (-1 .. 1)
-         tm_gmtoff : long;          --  offset from UTC in seconds
-         tm_zone   : char_Pointer;  --  timezone abbreviation
-      end record;
-
-      type tm_Pointer is access all tm;
-
-      subtype time_t is long;
-      type time_t_Pointer is access all time_t;
-
-      procedure localtime_tzoff
-       (C   : time_t_Pointer;
-        res : tm_Pointer;
-        off : long_Pointer);
-      pragma Import (C, localtime_tzoff, "__gnat_localtime_tzoff");
-      --  This is a lightweight wrapper around the system library function
-      --  localtime_r. Parameter 'off' captures the UTC offset which is either
-      --  retrieved from the tm struct or calculated from the 'timezone' extern
-      --  and the tm_isdst flag in the tm struct.
-
       ---------------------
       -- UTC_Time_Offset --
       ---------------------
 
       function UTC_Time_Offset (Date : Time) return Long_Integer is
-         Adj_Cent : Integer := 0;
-         Date_N   : Time_Rep;
-         Offset   : aliased long;
-         Secs_T   : aliased time_t;
-         Secs_TM  : aliased tm;
-
       begin
-         Date_N := Time_Rep (Date);
-
-         --  Dates which are 56 years apart fall on the same day, day light
-         --  saving and so on. Non-leap centennial years violate this rule by
-         --  one day and as a consequence, special adjustment is needed.
-
-         if Date_N > T_2100_2_28 then
-            if Date_N > T_2200_2_28 then
-               if Date_N > T_2300_2_28 then
-                  Adj_Cent := 3;
-               else
-                  Adj_Cent := 2;
-               end if;
-
-            else
-               Adj_Cent := 1;
-            end if;
-         end if;
-
-         if Adj_Cent > 0 then
-            Date_N := Date_N - Time_Rep (Adj_Cent) * Nanos_In_Day;
-         end if;
-
-         --  Shift the date within bounds of Unix time
-
-         while Date_N < Unix_Min loop
-            Date_N := Date_N + Nanos_In_56_Years;
-         end loop;
-
-         while Date_N >= Unix_Max loop
-            Date_N := Date_N - Nanos_In_56_Years;
-         end loop;
-
-         --  Perform a shift in origins from Ada to Unix
-
-         Date_N := Date_N - Unix_Min;
-
-         --  Convert the date into seconds
-
-         Secs_T := time_t (Date_N / Nano);
-
-         localtime_tzoff
-           (Secs_T'Unchecked_Access,
-            Secs_TM'Unchecked_Access,
-            Offset'Unchecked_Access);
-
-         return Offset;
+         return UTC_Time_Offset (Date, True);
       end UTC_Time_Offset;
 
    end Time_Zones_Operations;
@@ -1577,4 +1553,5 @@ package body Ada.Calendar is
 
 begin
    System.OS_Primitives.Initialize;
+
 end Ada.Calendar;