OSDN Git Service

* doc/install.texi (Specific, i?86-*-solaris2.10): Fix grammar.
[pf3gnuchains/gcc-fork.git] / gcc / ada / g-calend.adb
index 18c74ac..46d647f 100644 (file)
@@ -6,7 +6,7 @@
 --                                                                          --
 --                                 B o d y                                  --
 --                                                                          --
---           Copyright (C) 1999-2001 Ada Core Technologies, Inc.            --
+--                     Copyright (C) 1999-2009, AdaCore                     --
 --                                                                          --
 -- 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- --
@@ -16,8 +16,8 @@
 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
 -- for  more details.  You should have  received  a copy of the GNU General --
 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
--- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
--- MA 02111-1307, USA.                                                      --
+-- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
+-- Boston, MA 02110-1301, USA.                                              --
 --                                                                          --
 -- As a special exception,  if other files  instantiate  generics from this --
 -- unit, or you link  this unit with other files  to produce an executable, --
@@ -41,14 +41,13 @@ package body GNAT.Calendar is
    -----------------
 
    function Day_In_Year (Date : Time) return Day_In_Year_Number is
-      Year  : Year_Number;
-      Month : Month_Number;
-      Day   : Day_Number;
-      Dsecs : Day_Duration;
-
+      Year     : Year_Number;
+      Month    : Month_Number;
+      Day      : Day_Number;
+      Day_Secs : Day_Duration;
+      pragma Unreferenced (Day_Secs);
    begin
-      Split (Date, Year, Month, Day, Dsecs);
-
+      Split (Date, Year, Month, Day, Day_Secs);
       return Julian_Day (Year, Month, Day) - Julian_Day (Year, 1, 1) + 1;
    end Day_In_Year;
 
@@ -57,14 +56,13 @@ package body GNAT.Calendar is
    -----------------
 
    function Day_Of_Week (Date : Time) return Day_Name is
-      Year  : Year_Number;
-      Month : Month_Number;
-      Day   : Day_Number;
-      Dsecs : Day_Duration;
-
+      Year     : Year_Number;
+      Month    : Month_Number;
+      Day      : Day_Number;
+      Day_Secs : Day_Duration;
+      pragma Unreferenced (Day_Secs);
    begin
-      Split (Date, Year, Month, Day, Dsecs);
-
+      Split (Date, Year, Month, Day, Day_Secs);
       return Day_Name'Val ((Julian_Day (Year, Month, Day)) mod 7);
    end Day_Of_Week;
 
@@ -80,7 +78,7 @@ package body GNAT.Calendar is
       Minute     : Minute_Number;
       Second     : Second_Number;
       Sub_Second : Second_Duration;
-
+      pragma Unreferenced (Year, Month, Day, Minute, Second, Sub_Second);
    begin
       Split (Date, Year, Month, Day, Hour, Minute, Second, Sub_Second);
       return Hour;
@@ -90,14 +88,13 @@ package body GNAT.Calendar is
    -- Julian_Day --
    ----------------
 
-   --  Julian_Day is used to by Day_Of_Week and Day_In_Year. Note
-   --  that this implementation is not expensive.
+   --  Julian_Day is used to by Day_Of_Week and Day_In_Year. Note that this
+   --  implementation is not expensive.
 
    function Julian_Day
      (Year  : Year_Number;
       Month : Month_Number;
-      Day   : Day_Number)
-      return  Integer
+      Day   : Day_Number) return Integer
    is
       Internal_Year  : Integer;
       Internal_Month : Integer;
@@ -141,7 +138,7 @@ package body GNAT.Calendar is
       Minute     : Minute_Number;
       Second     : Second_Number;
       Sub_Second : Second_Duration;
-
+      pragma Unreferenced (Year, Month, Day, Hour, Second, Sub_Second);
    begin
       Split (Date, Year, Month, Day, Hour, Minute, Second, Sub_Second);
       return Minute;
@@ -159,7 +156,7 @@ package body GNAT.Calendar is
       Minute     : Minute_Number;
       Second     : Second_Number;
       Sub_Second : Second_Duration;
-
+      pragma Unreferenced (Year, Month, Day, Hour, Minute, Sub_Second);
    begin
       Split (Date, Year, Month, Day, Hour, Minute, Second, Sub_Second);
       return Second;
@@ -179,21 +176,16 @@ package body GNAT.Calendar is
       Second     : out Second_Number;
       Sub_Second : out Second_Duration)
    is
-      Dsecs : Day_Duration;
-      Secs  : Natural;
+      Day_Secs : Day_Duration;
+      Secs     : Natural;
 
    begin
-      Split (Date, Year, Month, Day, Dsecs);
-
-      if Dsecs = 0.0 then
-         Secs := 0;
-      else
-         Secs := Natural (Dsecs - 0.5);
-      end if;
+      Split (Date, Year, Month, Day, Day_Secs);
 
-      Sub_Second := Second_Duration (Dsecs - Day_Duration (Secs));
-      Hour       := Hour_Number (Secs / 3600);
-      Secs       := Secs mod 3600;
+      Secs       := (if Day_Secs = 0.0 then 0 else Natural (Day_Secs - 0.5));
+      Sub_Second := Second_Duration (Day_Secs - Day_Duration (Secs));
+      Hour       := Hour_Number (Secs / 3_600);
+      Secs       := Secs mod 3_600;
       Minute     := Minute_Number (Secs / 60);
       Second     := Second_Number (Secs mod 60);
    end Split;
@@ -210,7 +202,7 @@ package body GNAT.Calendar is
       Minute     : Minute_Number;
       Second     : Second_Number;
       Sub_Second : Second_Duration;
-
+      pragma Unreferenced (Year, Month, Day, Hour, Minute, Second);
    begin
       Split (Date, Year, Month, Day, Hour, Minute, Second, Sub_Second);
       return Sub_Second;
@@ -227,33 +219,34 @@ package body GNAT.Calendar is
       Hour       : Hour_Number;
       Minute     : Minute_Number;
       Second     : Second_Number;
-      Sub_Second : Second_Duration := 0.0)
-      return Time
+      Sub_Second : Second_Duration := 0.0) return Time
    is
-      Dsecs : constant Day_Duration :=
-                Day_Duration (Hour * 3600 + Minute * 60 + Second) +
-                                                             Sub_Second;
+
+      Day_Secs : constant Day_Duration :=
+                   Day_Duration (Hour   * 3_600) +
+                   Day_Duration (Minute *    60) +
+                   Day_Duration (Second)         +
+                                 Sub_Second;
    begin
-      return Time_Of (Year, Month, Day, Dsecs);
+      return Time_Of (Year, Month, Day, Day_Secs);
    end Time_Of;
 
    -----------------
    -- To_Duration --
    -----------------
 
-   function To_Duration (T : access timeval) return Duration is
+   function To_Duration (T : not null access timeval) return Duration is
 
       procedure timeval_to_duration
-        (T    : access timeval;
-         sec  : access C.long;
-         usec : access C.long);
+        (T    : not null access timeval;
+         sec  : not null access C.long;
+         usec : not null access C.long);
       pragma Import (C, timeval_to_duration, "__gnat_timeval_to_duration");
 
       Micro : constant := 10**6;
       sec   : aliased C.long;
       usec  : aliased C.long;
 
-
    begin
       timeval_to_duration (T, sec'Access, usec'Access);
       return Duration (sec) + Duration (usec) / Micro;
@@ -263,9 +256,12 @@ package body GNAT.Calendar is
    -- To_Timeval --
    ----------------
 
-   function To_Timeval  (D : Duration) return timeval is
+   function To_Timeval (D : Duration) return timeval is
 
-      procedure duration_to_timeval (Sec, Usec : C.long; T : access timeval);
+      procedure duration_to_timeval
+        (Sec  : C.long;
+         Usec : C.long;
+         T : not null access timeval);
       pragma Import (C, duration_to_timeval, "__gnat_duration_to_timeval");
 
       Micro  : constant := 10**6;
@@ -291,27 +287,263 @@ package body GNAT.Calendar is
    -- Week_In_Year --
    ------------------
 
-   function Week_In_Year
-     (Date : Ada.Calendar.Time)
-      return Week_In_Year_Number
+   function Week_In_Year (Date : Time) return Week_In_Year_Number is
+      Year : Year_Number;
+      Week : Week_In_Year_Number;
+      pragma Unreferenced (Year);
+   begin
+      Year_Week_In_Year (Date, Year, Week);
+      return Week;
+   end Week_In_Year;
+
+   -----------------------
+   -- Year_Week_In_Year --
+   -----------------------
+
+   procedure Year_Week_In_Year
+     (Date : Time;
+      Year : out Year_Number;
+      Week : out Week_In_Year_Number)
    is
-      Year       : Year_Number;
       Month      : Month_Number;
       Day        : Day_Number;
       Hour       : Hour_Number;
       Minute     : Minute_Number;
       Second     : Second_Number;
       Sub_Second : Second_Duration;
-      Offset     : Natural;
+      Jan_1      : Day_Name;
+      Shift      : Week_In_Year_Number;
+      Start_Week : Week_In_Year_Number;
+
+      pragma Unreferenced (Hour, Minute, Second, Sub_Second);
+
+      function Is_Leap (Year : Year_Number) return Boolean;
+      --  Return True if Year denotes a leap year. Leap centennial years are
+      --  properly handled.
+
+      function Jan_1_Day_Of_Week
+        (Jan_1     : Day_Name;
+         Year      : Year_Number;
+         Last_Year : Boolean := False;
+         Next_Year : Boolean := False) return Day_Name;
+      --  Given the weekday of January 1 in Year, determine the weekday on
+      --  which January 1 fell last year or will fall next year as set by
+      --  the two flags. This routine does not call Time_Of or Split.
+
+      function Last_Year_Has_53_Weeks
+        (Jan_1 : Day_Name;
+         Year  : Year_Number) return Boolean;
+      --  Given the weekday of January 1 in Year, determine whether last year
+      --  has 53 weeks. A False value implies that the year has 52 weeks.
+
+      -------------
+      -- Is_Leap --
+      -------------
+
+      function Is_Leap (Year : Year_Number) return Boolean is
+      begin
+         if Year mod 400 = 0 then
+            return True;
+         elsif Year mod 100 = 0 then
+            return False;
+         else
+            return Year mod 4 = 0;
+         end if;
+      end Is_Leap;
+
+      -----------------------
+      -- Jan_1_Day_Of_Week --
+      -----------------------
+
+      function Jan_1_Day_Of_Week
+        (Jan_1     : Day_Name;
+         Year      : Year_Number;
+         Last_Year : Boolean := False;
+         Next_Year : Boolean := False) return Day_Name
+      is
+         Shift : Integer := 0;
+
+      begin
+         if Last_Year then
+            Shift := (if Is_Leap (Year - 1) then -2 else -1);
+         elsif Next_Year then
+            Shift := (if Is_Leap (Year) then 2 else 1);
+         end if;
+
+         return Day_Name'Val ((Day_Name'Pos (Jan_1) + Shift) mod 7);
+      end Jan_1_Day_Of_Week;
+
+      ----------------------------
+      -- Last_Year_Has_53_Weeks --
+      ----------------------------
+
+      function Last_Year_Has_53_Weeks
+        (Jan_1 : Day_Name;
+         Year  : Year_Number) return Boolean
+      is
+         Last_Jan_1 : constant Day_Name :=
+                        Jan_1_Day_Of_Week (Jan_1, Year, Last_Year => True);
+
+      begin
+         --  These two cases are illustrated in the table below
+
+         return
+           Last_Jan_1 = Thursday
+             or else (Last_Jan_1 = Wednesday and then Is_Leap (Year - 1));
+      end Last_Year_Has_53_Weeks;
+
+   --  Start of processing for Week_In_Year
 
    begin
       Split (Date, Year, Month, Day, Hour, Minute, Second, Sub_Second);
 
-      --  Day offset number for the first week of the year.
+      --  According to ISO 8601, the first week of year Y is the week that
+      --  contains the first Thursday in year Y. The following table contains
+      --  all possible combinations of years and weekdays along with examples.
+
+      --    +-------+------+-------+---------+
+      --    | Jan 1 | Leap | Weeks | Example |
+      --    +-------+------+-------+---------+
+      --    |  Mon  |  No  |  52   |  2007   |
+      --    +-------+------+-------+---------+
+      --    |  Mon  | Yes  |  52   |  1996   |
+      --    +-------+------+-------+---------+
+      --    |  Tue  |  No  |  52   |  2002   |
+      --    +-------+------+-------+---------+
+      --    |  Tue  | Yes  |  52   |  1980   |
+      --    +-------+------+-------+---------+
+      --    |  Wed  |  No  |  52   |  2003   |
+      --    +-------+------#########---------+
+      --    |  Wed  | Yes  #  53   #  1992   |
+      --    +-------+------#-------#---------+
+      --    |  Thu  |  No  #  53   #  1998   |
+      --    +-------+------#-------#---------+
+      --    |  Thu  | Yes  #  53   #  2004   |
+      --    +-------+------#########---------+
+      --    |  Fri  |  No  |  52   |  1999   |
+      --    +-------+------+-------+---------+
+      --    |  Fri  | Yes  |  52   |  1988   |
+      --    +-------+------+-------+---------+
+      --    |  Sat  |  No  |  52   |  1994   |
+      --    +-------+------+-------+---------+
+      --    |  Sat  | Yes  |  52   |  1972   |
+      --    +-------+------+-------+---------+
+      --    |  Sun  |  No  |  52   |  1995   |
+      --    +-------+------+-------+---------+
+      --    |  Sun  | Yes  |  52   |  1956   |
+      --    +-------+------+-------+---------+
+
+      --  A small optimization, the input date is January 1. Note that this
+      --  is a key day since it determines the number of weeks and is used
+      --  when special casing the first week of January and the last week of
+      --  December.
+
+      Jan_1 := Day_Of_Week (if Day = 1 and then Month = 1
+                            then Date
+                            else (Time_Of (Year, 1, 1, 0.0)));
+
+      --  Special cases for January
+
+      if Month = 1 then
+
+         --  Special case 1: January 1, 2 and 3. These three days may belong
+         --  to last year's last week which can be week number 52 or 53.
+
+         --    +-----+-----+-----+=====+-----+-----+-----+
+         --    | Mon | Tue | Wed # Thu # Fri | Sat | Sun |
+         --    +-----+-----+-----+-----+-----+-----+-----+
+         --    | 26  | 27  | 28  # 29  # 30  | 31  |  1  |
+         --    +-----+-----+-----+-----+-----+-----+-----+
+         --    | 27  | 28  | 29  # 30  # 31  |  1  |  2  |
+         --    +-----+-----+-----+-----+-----+-----+-----+
+         --    | 28  | 29  | 30  # 31  #  1  |  2  |  3  |
+         --    +-----+-----+-----+=====+-----+-----+-----+
+
+         if (Day = 1 and then Jan_1 in Friday .. Sunday)
+               or else
+            (Day = 2 and then Jan_1 in Friday .. Saturday)
+               or else
+            (Day = 3 and then Jan_1 = Friday)
+         then
+            Week := (if Last_Year_Has_53_Weeks (Jan_1, Year) then 53 else 52);
+
+            --  January 1, 2 and 3 belong to the previous year
+
+            Year := Year - 1;
+            return;
+
+         --  Special case 2: January 1, 2, 3, 4, 5, 6 and 7 of the first week
+
+         --    +-----+-----+-----+=====+-----+-----+-----+
+         --    | Mon | Tue | Wed # Thu # Fri | Sat | Sun |
+         --    +-----+-----+-----+-----+-----+-----+-----+
+         --    | 29  | 30  | 31  #  1  #  2  |  3  |  4  |
+         --    +-----+-----+-----+-----+-----+-----+-----+
+         --    | 30  | 31  |  1  #  2  #  3  |  4  |  5  |
+         --    +-----+-----+-----+-----+-----+-----+-----+
+         --    | 31  |  1  |  2  #  3  #  4  |  5  |  6  |
+         --    +-----+-----+-----+-----+-----+-----+-----+
+         --    |  1  |  2  |  3  #  4  #  5  |  6  |  7  |
+         --    +-----+-----+-----+=====+-----+-----+-----+
+
+         elsif (Day <= 4 and then Jan_1 in Monday .. Thursday)
+                  or else
+               (Day = 5  and then Jan_1 in Monday .. Wednesday)
+                  or else
+               (Day = 6  and then Jan_1 in Monday ..  Tuesday)
+                  or else
+               (Day = 7  and then Jan_1 = Monday)
+         then
+            Week := 1;
+            return;
+         end if;
+
+      --  Month other than 1
+
+      --  Special case 3: December 29, 30 and 31. These days may belong to
+      --  next year's first week.
+
+      --    +-----+-----+-----+=====+-----+-----+-----+
+      --    | Mon | Tue | Wed # Thu # Fri | Sat | Sun |
+      --    +-----+-----+-----+-----+-----+-----+-----+
+      --    | 29  | 30  | 31  #  1  #  2  |  3  |  4  |
+      --    +-----+-----+-----+-----+-----+-----+-----+
+      --    | 30  | 31  |  1  #  2  #  3  |  4  |  5  |
+      --    +-----+-----+-----+-----+-----+-----+-----+
+      --    | 31  |  1  |  2  #  3  #  4  |  5  |  6  |
+      --    +-----+-----+-----+=====+-----+-----+-----+
+
+      elsif Month = 12 and then Day > 28 then
+         declare
+            Next_Jan_1 : constant Day_Name :=
+                           Jan_1_Day_Of_Week (Jan_1, Year, Next_Year => True);
+         begin
+            if (Day = 29 and then Next_Jan_1 = Thursday)
+                  or else
+               (Day = 30 and then Next_Jan_1 in Wednesday .. Thursday)
+                  or else
+               (Day = 31 and then Next_Jan_1 in Tuesday .. Thursday)
+            then
+               Year := Year + 1;
+               Week := 1;
+               return;
+            end if;
+         end;
+      end if;
+
+      --  Determine the week from which to start counting. If January 1 does
+      --  not belong to the first week of the input year, then the next week
+      --  is the first week.
 
-      Offset := Julian_Day (Year, 1, 1) mod 7;
+      Start_Week := (if Jan_1 in Friday .. Sunday then 1 else 2);
 
-      return 1 + ((Day_In_Year (Date) - 1) + Offset) / 7;
-   end Week_In_Year;
+      --  At this point all special combinations have been accounted for and
+      --  the proper start week has been found. Since January 1 may not fall
+      --  on a Monday, shift 7 - Day_Name'Pos (Jan_1). This action ensures an
+      --  origin which falls on Monday.
+
+      Shift := 7 - Day_Name'Pos (Jan_1);
+      Week  := Start_Week + (Day_In_Year (Date) - Shift - 1) / 7;
+   end Year_Week_In_Year;
 
 end GNAT.Calendar;