File : a-calend.adb


     ------------------------------------------------------------------------------
     --                                                                          --
     --                        GNAT RUN-TIME COMPONENTS                          --
     --                                                                          --
   5 --                         A D A . C A L E N D A R                          --
     --                                                                          --
     --                                 B o d y                                  --
     --                                                                          --
     --                            $Revision: 1.14 $
  10 --                                                                          --
     --            Copyright (C) 1997-2001 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- --
  15 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
     -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
     -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
     -- 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 --
  20 -- 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.                                                      --
     --                                                                          --
     -- As a special exception,  if other files  instantiate  generics from this --
  25 -- unit, or you link  this unit with other files  to produce an executable, --
     -- this  unit  does not  by itself cause  the resulting  executable  to  be --
     -- covered  by the  GNU  General  Public  License.  This exception does not --
     -- however invalidate  any other reasons why  the executable file  might be --
     -- covered by the  GNU Public License.                                      --
  30 --                                                                          --
     -- GNAT was originally developed  by the GNAT team at  New York University. --
     -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
     --                                                                          --
     ------------------------------------------------------------------------------
  35 
     --  This is the Windows NT/95 version.
     
     with System.OS_Primitives;
     --  used for Clock
  40 
     with System.OS_Interface;
     
     package body Ada.Calendar is
     
  45    use System.OS_Interface;
     
        ------------------------------
        -- Use of Pragma Unsuppress --
        ------------------------------
  50 
        --  This implementation of Calendar takes advantage of the permission in
        --  Ada 95 of using arithmetic overflow checks to check for out of bounds
        --  time values. This means that we must catch the constraint error that
        --  results from arithmetic overflow, so we use pragma Unsuppress to make
  55    --  sure that overflow is enabled, using software overflow checking if
        --  necessary. That way, compiling Calendar with options to suppress this
        --  checking will not affect its correctness.
     
        ------------------------
  60    -- Local Declarations --
        ------------------------
     
        Ada_Year_Min : constant := 1901;
        Ada_Year_Max : constant := 2099;
  65 
        --  Win32 time constants
     
        epoch_1970     : constant := 16#19D_B1DE_D53E_8000#; -- win32 UTC epoch
        system_time_ns : constant := 100;                    -- 100 ns per tick
  70    Sec_Unit       : constant := 10#1#E9;
     
        ---------
        -- "+" --
        ---------
  75 
        function "+" (Left : Time; Right : Duration) return Time is
           pragma Unsuppress (Overflow_Check);
        begin
           return (Left + Time (Right));
  80 
        exception
           when Constraint_Error =>
              raise Time_Error;
        end "+";
  85 
        function "+" (Left : Duration; Right : Time) return Time is
           pragma Unsuppress (Overflow_Check);
        begin
           return (Time (Left) + Right);
  90 
        exception
           when Constraint_Error =>
              raise Time_Error;
        end "+";
  95 
        ---------
        -- "-" --
        ---------
     
 100    function "-" (Left : Time; Right : Duration)  return Time is
           pragma Unsuppress (Overflow_Check);
        begin
           return Left - Time (Right);
     
 105    exception
           when Constraint_Error =>
              raise Time_Error;
        end "-";
     
 110    function "-" (Left : Time; Right : Time) return Duration is
           pragma Unsuppress (Overflow_Check);
        begin
           return Duration (Left) - Duration (Right);
     
 115    exception
           when Constraint_Error =>
              raise Time_Error;
        end "-";
     
 120    ---------
        -- "<" --
        ---------
     
        function "<" (Left, Right : Time) return Boolean is
 125    begin
           return Duration (Left) < Duration (Right);
        end "<";
     
        ----------
 130    -- "<=" --
        ----------
     
        function "<=" (Left, Right : Time) return Boolean is
        begin
 135       return Duration (Left) <= Duration (Right);
        end "<=";
     
        ---------
        -- ">" --
 140    ---------
     
        function ">" (Left, Right : Time) return Boolean is
        begin
           return Duration (Left) > Duration (Right);
 145    end ">";
     
        ----------
        -- ">=" --
        ----------
 150 
        function ">=" (Left, Right : Time) return Boolean is
        begin
           return Duration (Left) >= Duration (Right);
        end ">=";
 155 
        -----------
        -- Clock --
        -----------
     
 160    --  The Ada.Calendar.Clock function gets the time from the soft links
        --  interface which will call the appropriate function depending wether
        --  tasking is involved or not.
     
        function Clock return Time is
 165    begin
           return Time (System.OS_Primitives.Clock);
        end Clock;
     
        ---------
 170    -- Day --
        ---------
     
        function Day (Date : Time) return Day_Number is
           DY : Year_Number;
 175       DM : Month_Number;
           DD : Day_Number;
           DS : Day_Duration;
     
        begin
 180       Split (Date, DY, DM, DD, DS);
           return DD;
        end Day;
     
        -----------
 185    -- Month --
        -----------
     
        function Month (Date : Time) return Month_Number is
           DY : Year_Number;
 190       DM : Month_Number;
           DD : Day_Number;
           DS : Day_Duration;
     
        begin
 195       Split (Date, DY, DM, DD, DS);
           return DM;
        end Month;
     
        -------------
 200    -- Seconds --
        -------------
     
        function Seconds (Date : Time) return Day_Duration is
           DY : Year_Number;
 205       DM : Month_Number;
           DD : Day_Number;
           DS : Day_Duration;
     
        begin
 210       Split (Date, DY, DM, DD, DS);
           return DS;
        end Seconds;
     
        -----------
 215    -- Split --
        -----------
     
        procedure Split
          (Date    : Time;
 220       Year    : out Year_Number;
           Month   : out Month_Number;
           Day     : out Day_Number;
           Seconds : out Day_Duration)
        is
 225 
           Date_Int    : aliased Long_Long_Integer;
           Date_Loc    : aliased Long_Long_Integer;
           Timbuf      : aliased SYSTEMTIME;
           Int_Date    : Long_Long_Integer;
 230       Sub_Seconds : Duration;
     
        begin
           --  We take the sub-seconds (decimal part) of Date and this is added
           --  to compute the Seconds. This way we keep the precision of the
 235       --  high-precision clock that was lost with the Win32 API calls
           --  below.
     
           if Date < 0.0 then
     
 240          --  this is a Date before Epoch (January 1st, 1970)
     
              Sub_Seconds := Duration (Date) -
                Duration (Long_Long_Integer (Date + Duration'(0.5)));
     
 245          Int_Date := Long_Long_Integer (Date - Sub_Seconds);
     
              --  For Date = -86400.1 we are 2 days before Epoch at 0.1 seconds
              --  from day 1 before Epoch. It means that it is 23h 59m 59.9s.
              --  here we adjust for that.
 250 
              if Sub_Seconds < 0.0 then
                 Int_Date    := Int_Date - 1;
                 Sub_Seconds := 1.0 + Sub_Seconds;
              end if;
 255 
           else
     
              --  this is a Date after Epoch (January 1st, 1970)
     
 260          Sub_Seconds := Duration (Date) -
                Duration (Long_Long_Integer (Date - Duration'(0.5)));
     
              Int_Date := Long_Long_Integer (Date - Sub_Seconds);
     
 265       end if;
     
           --  Date_Int is the number of seconds from Epoch.
     
           Date_Int := Long_Long_Integer
 270         (Int_Date * Sec_Unit / system_time_ns) + epoch_1970;
     
           if not FileTimeToLocalFileTime (Date_Int'Access, Date_Loc'Access) then
              raise Time_Error;
           end if;
 275 
           if not FileTimeToSystemTime (Date_Loc'Access, Timbuf'Access) then
              raise Time_Error;
           end if;
     
 280       if Timbuf.wYear not in Ada_Year_Min .. Ada_Year_Max then
              raise Time_Error;
           end if;
     
           Seconds :=
 285         Duration (Timbuf.wHour) * 3_600.0 +
             Duration (Timbuf.wMinute) * 60.0 +
             Duration (Timbuf.wSecond) +
             Sub_Seconds;
     
 290       Day       := Integer (Timbuf.wDay);
           Month     := Integer (Timbuf.wMonth);
           Year      := Integer (Timbuf.wYear);
        end Split;
     
 295    -------------
        -- Time_Of --
        -------------
     
        function Time_Of
 300      (Year    : Year_Number;
           Month   : Month_Number;
           Day     : Day_Number;
           Seconds : Day_Duration := 0.0)
           return    Time
 305    is
     
           Timbuf      : aliased SYSTEMTIME;
           Now         : aliased Long_Long_Integer;
           Loc         : aliased Long_Long_Integer;
 310       Int_Secs    : Integer;
           Secs        : Integer;
           Add_One_Day : Boolean := False;
           Date        : Time;
     
 315    begin
           --  The following checks are redundant with respect to the constraint
           --  error checks that should normally be made on parameters, but we
           --  decide to raise Constraint_Error in any case if bad values come
           --  in (as a result of checks being off in the caller, or for other
 320       --  erroneous or bounded error cases).
     
           if        not Year   'Valid
             or else not Month  'Valid
             or else not Day    'Valid
 325         or else not Seconds'Valid
           then
              raise Constraint_Error;
           end if;
     
 330       if Seconds = 0.0 then
              Int_Secs := 0;
           else
              Int_Secs := Integer (Seconds - 0.5);
           end if;
 335 
           --  Timbuf.wMillisec is to keep the msec. We can't use that because the
           --  high-resolution clock has a precision of 1 Microsecond.
           --  Anyway the sub-seconds part is not needed to compute the number
           --  of seconds in UTC.
 340 
           if Int_Secs = 86_400 then
              Secs := 0;
              Add_One_Day := True;
           else
 345          Secs := Int_Secs;
           end if;
     
           Timbuf.wMilliseconds := 0;
           Timbuf.wSecond       := WORD (Secs mod 60);
 350       Timbuf.wMinute       := WORD ((Secs / 60) mod 60);
           Timbuf.wHour         := WORD (Secs / 3600);
           Timbuf.wDay          := WORD (Day);
           Timbuf.wMonth        := WORD (Month);
           Timbuf.wYear         := WORD (Year);
 355 
           if not SystemTimeToFileTime (Timbuf'Access, Loc'Access) then
              raise Time_Error;
           end if;
     
 360       if not LocalFileTimeToFileTime (Loc'Access, Now'Access) then
              raise Time_Error;
           end if;
     
           --  Here we have the UTC now translate UTC to Epoch time (UNIX style
 365       --  time based on 1 january 1970) and add there the sub-seconds part.
     
           declare
              Sub_Sec  : Duration := Seconds - Duration (Int_Secs);
           begin
 370          Date := Time ((Now - epoch_1970) * system_time_ns / Sec_Unit) +
                Sub_Sec;
           end;
     
           if Add_One_Day then
 375          Date := Date + Duration (86400.0);
           end if;
     
           return Date;
        end Time_Of;
 380 
        ----------
        -- Year --
        ----------
     
 385    function Year (Date : Time) return Year_Number is
           DY : Year_Number;
           DM : Month_Number;
           DD : Day_Number;
           DS : Day_Duration;
 390 
        begin
           Split (Date, DY, DM, DD, DS);
           return DY;
        end Year;
 395 
     end Ada.Calendar;