home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / adav313.zip / gnat-3_13p-os2-bin-20010916.zip / emx / gnatlib / a-calend.adb < prev    next >
Text File  |  2000-07-19  |  18KB  |  514 lines

  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. --                            $Revision: 1.49 $
  10. --                                                                          --
  11. --          Copyright (C) 1992-1999 Free Software Foundation, Inc.          --
  12. --                                                                          --
  13. -- GNAT is free software;  you can  redistribute it  and/or modify it under --
  14. -- 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- --
  16. -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
  17. -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
  18. -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
  19. -- 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 --
  21. -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
  22. -- MA 02111-1307, USA.                                                      --
  23. --                                                                          --
  24. -- 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, --
  26. -- this  unit  does not  by itself cause  the resulting  executable  to  be --
  27. -- covered  by the  GNU  General  Public  License.  This exception does not --
  28. -- however invalidate  any other reasons why  the executable file  might be --
  29. -- covered by the  GNU Public License.                                      --
  30. --                                                                          --
  31. -- GNAT was originally developed  by the GNAT team at  New York University. --
  32. -- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
  33. --                                                                          --
  34. ------------------------------------------------------------------------------
  35.  
  36. with System.Soft_Links;
  37. --  used for Lock_Task, Unlock_Task, Clock
  38.  
  39. with Unchecked_Conversion;
  40.  
  41. with System.OS_Primitives;
  42. --  used for Clock
  43.  
  44. package body Ada.Calendar is
  45.  
  46.    package SSL renames System.Soft_Links;
  47.  
  48.    use type SSL.Get_Duration_Call;
  49.  
  50.    ------------------------------
  51.    -- Use of Pragma Unsuppress --
  52.    ------------------------------
  53.  
  54.    --  This implementation of Calendar takes advantage of the permission in
  55.    --  Ada 95 of using arithmetic overflow checks to check for out of bounds
  56.    --  time values. This means that we must catch the constraint error that
  57.    --  results from arithmetic overflow, so we use pragma Unsuppress to make
  58.    --  sure that overflow is enabled, using software overflow checking if
  59.    --  necessary. That way, compiling Calendar with options to suppress this
  60.    --  checking will not affect its correctness.
  61.  
  62.    ------------------------
  63.    -- Local Declarations --
  64.    ------------------------
  65.  
  66.    type Char_Pointer is access Character;
  67.    subtype int  is Integer;
  68.    subtype long is Long_Integer;
  69.    --  Synonyms for C types. We don't want to get them from Interfaces.C
  70.    --  because there is no point in loading that unit just for calendar.
  71.  
  72.    type tm is record
  73.       tm_sec    : int;           -- seconds after the minute (0 .. 60)
  74.       tm_min    : int;           -- minutes after the hour (0 .. 59)
  75.       tm_hour   : int;           -- hours since midnight (0 .. 24)
  76.       tm_mday   : int;           -- day of the month (1 .. 31)
  77.       tm_mon    : int;           -- months since January (0 .. 11)
  78.       tm_year   : int;           -- years since 1900
  79.       tm_wday   : int;           -- days since Sunday (0 .. 6)
  80.       tm_yday   : int;           -- days since January 1 (0 .. 365)
  81.       tm_isdst  : int;           -- Daylight Savings Time flag (-1 .. +1)
  82.       tm_gmtoff : long;          -- offset from CUT in seconds
  83.       tm_zone   : Char_Pointer;  -- timezone abbreviation
  84.    end record;
  85.  
  86.    type tm_Pointer is access all tm;
  87.  
  88.    subtype time_t is long;
  89.  
  90.    type time_t_Pointer is access all time_t;
  91.  
  92.    procedure localtime_r (C : time_t_Pointer; res : tm_Pointer);
  93.    pragma Import (C, localtime_r, "__gnat_localtime_r");
  94.  
  95.    function mktime (TM : tm_Pointer) return time_t;
  96.    pragma Import (C, mktime);
  97.    --  mktime returns -1 in case the calendar time given by components of
  98.    --  TM.all cannot be represented.
  99.  
  100.    --  The following constants are used in adjusting Ada dates so that they
  101.    --  fit into the range that can be handled by Unix (1970 - 2038). The trick
  102.    --  is that the number of days in any four year period in the Ada range of
  103.    --  years (1901 - 2099) has a constant number of days. This is because we
  104.    --  have the special case of 2000 which, contrary to the normal exception
  105.    --  for centuries, is a leap year after all.
  106.  
  107.    Unix_Year_Min : constant := 1970;
  108.    Unix_Year_Max : constant := 2038;
  109.  
  110.    Ada_Year_Min : constant := 1901;
  111.    Ada_Year_Max : constant := 2099;
  112.  
  113.    --  Some basic constants used throughout
  114.  
  115.    Days_In_Month : constant array (Month_Number) of Day_Number :=
  116.                      (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
  117.  
  118.    Days_In_4_Years     : constant := 365 * 3 + 366;
  119.    Seconds_In_4_Years  : constant := 86_400 * Days_In_4_Years;
  120.    Seconds_In_4_YearsD : constant Duration := Duration (Seconds_In_4_Years);
  121.  
  122.    ---------
  123.    -- "+" --
  124.    ---------
  125.  
  126.    function "+" (Left : Time; Right : Duration) return Time is
  127.       pragma Unsuppress (Overflow_Check);
  128.    begin
  129.       return (Left + Time (Right));
  130.  
  131.    exception
  132.       when Constraint_Error =>
  133.          raise Time_Error;
  134.    end "+";
  135.  
  136.    function "+" (Left : Duration; Right : Time) return Time is
  137.       pragma Unsuppress (Overflow_Check);
  138.    begin
  139.       return (Time (Left) + Right);
  140.  
  141.    exception
  142.       when Constraint_Error =>
  143.          raise Time_Error;
  144.    end "+";
  145.  
  146.    ---------
  147.    -- "-" --
  148.    ---------
  149.  
  150.    function "-" (Left : Time; Right : Duration)  return Time is
  151.       pragma Unsuppress (Overflow_Check);
  152.    begin
  153.       return Left - Time (Right);
  154.  
  155.    exception
  156.       when Constraint_Error =>
  157.          raise Time_Error;
  158.    end "-";
  159.  
  160.    function "-" (Left : Time; Right : Time) return Duration is
  161.       pragma Unsuppress (Overflow_Check);
  162.    begin
  163.       return Duration (Left) - Duration (Right);
  164.  
  165.    exception
  166.       when Constraint_Error =>
  167.          raise Time_Error;
  168.    end "-";
  169.  
  170.    ---------
  171.    -- "<" --
  172.    ---------
  173.  
  174.    function "<" (Left, Right : Time) return Boolean is
  175.    begin
  176.       return Duration (Left) < Duration (Right);
  177.    end "<";
  178.  
  179.    ----------
  180.    -- "<=" --
  181.    ----------
  182.  
  183.    function "<=" (Left, Right : Time) return Boolean is
  184.    begin
  185.       return Duration (Left) <= Duration (Right);
  186.    end "<=";
  187.  
  188.    ---------
  189.    -- ">" --
  190.    ---------
  191.  
  192.    function ">" (Left, Right : Time) return Boolean is
  193.    begin
  194.       return Duration (Left) > Duration (Right);
  195.    end ">";
  196.  
  197.    ----------
  198.    -- ">=" --
  199.    ----------
  200.  
  201.    function ">=" (Left, Right : Time) return Boolean is
  202.    begin
  203.       return Duration (Left) >= Duration (Right);
  204.    end ">=";
  205.  
  206.    -----------
  207.    -- Clock --
  208.    -----------
  209.  
  210.    --  The Ada.Calendar.Clock function gets the time from the soft links
  211.    --  interface which will call the appropriate function depending wether
  212.    --  tasking is involved or not.
  213.  
  214.    function Clock return Time is
  215.    begin
  216.       return Time (SSL.Clock.all);
  217.    end Clock;
  218.  
  219.    ---------
  220.    -- Day --
  221.    ---------
  222.  
  223.    function Day (Date : Time) return Day_Number is
  224.       DY : Year_Number;
  225.       DM : Month_Number;
  226.       DD : Day_Number;
  227.       DS : Day_Duration;
  228.  
  229.    begin
  230.       Split (Date, DY, DM, DD, DS);
  231.       return DD;
  232.    end Day;
  233.  
  234.    -----------
  235.    -- Month --
  236.    -----------
  237.  
  238.    function Month (Date : Time) return Month_Number is
  239.       DY : Year_Number;
  240.       DM : Month_Number;
  241.       DD : Day_Number;
  242.       DS : Day_Duration;
  243.  
  244.    begin
  245.       Split (Date, DY, DM, DD, DS);
  246.       return DM;
  247.    end Month;
  248.  
  249.    -------------
  250.    -- Seconds --
  251.    -------------
  252.  
  253.    function Seconds (Date : Time) return Day_Duration is
  254.       DY : Year_Number;
  255.       DM : Month_Number;
  256.       DD : Day_Number;
  257.       DS : Day_Duration;
  258.  
  259.    begin
  260.       Split (Date, DY, DM, DD, DS);
  261.       return DS;
  262.    end Seconds;
  263.  
  264.    -----------
  265.    -- Split --
  266.    -----------
  267.  
  268.    procedure Split
  269.      (Date    : Time;
  270.       Year    : out Year_Number;
  271.       Month   : out Month_Number;
  272.       Day     : out Day_Number;
  273.       Seconds : out Day_Duration)
  274.    is
  275.       --  The following declare bounds for duration that are comfortably
  276.       --  wider than the maximum allowed output result for the Ada range
  277.       --  of representable split values. These are used for a quick check
  278.       --  that the value is not wildly out of range.
  279.  
  280.       Low  : constant := (Ada_Year_Min - Unix_Year_Min - 2) * 365 * 86_400;
  281.       High : constant := (Ada_Year_Max - Unix_Year_Min + 2) * 365 * 86_400;
  282.  
  283.       LowD  : constant Duration := Duration (Low);
  284.       HighD : constant Duration := Duration (High);
  285.  
  286.       --  The following declare the maximum duration value that can be
  287.       --  successfully converted to a 32-bit integer suitable for passing
  288.       --  to the localtime_r function. Note that we cannot assume that the
  289.       --  localtime_r function expands to accept 64-bit input on a 64-bit
  290.       --  machine, but we can count on a 32-bit range on all machines.
  291.  
  292.       Max_Time  : constant := 2 ** 31 - 1;
  293.       Max_TimeD : constant Duration := Duration (Max_Time);
  294.  
  295.       --  Finally the actual variables used in the computation
  296.  
  297.       D                : Duration;
  298.       Frac_Sec         : Duration;
  299.       Year_Val         : Integer;
  300.       Adjusted_Seconds : aliased time_t;
  301.       Tm_Val           : aliased tm;
  302.  
  303.    begin
  304.       --  For us a time is simply a signed duration value, so we work with
  305.       --  this duration value directly. Note that it can be negative.
  306.  
  307.       D := Duration (Date);
  308.  
  309.       --  First of all, filter out completely ludicrous values. Remember
  310.       --  that we use the full stored range of duration values, which may
  311.       --  be significantly larger than the allowed range of Ada times. Note
  312.       --  that these checks are wider than required to make absolutely sure
  313.       --  that there are no end effects from time zone differences.
  314.  
  315.       if D < LowD or else D > HighD then
  316.          raise Time_Error;
  317.       end if;
  318.  
  319.       --  The unix localtime_r function is more or less exactly what we need
  320.       --  here. The less comes from the fact that it does not support the
  321.       --  required range of years (the guaranteed range available is only
  322.       --  EPOCH through EPOCH + N seconds). N is in practice 2 ** 31 - 1.
  323.  
  324.       --  If we have a value outside this range, then we first adjust it
  325.       --  to be in the required range by adding multiples of four years.
  326.       --  For the range we are interested in, the number of days in any
  327.       --  consecutive four year period is constant. Then we do the split
  328.       --  on the adjusted value, and readjust the years value accordingly.
  329.  
  330.       Year_Val := 0;
  331.  
  332.       while D < 0.0 loop
  333.          D := D + Seconds_In_4_YearsD;
  334.          Year_Val := Year_Val - 4;
  335.       end loop;
  336.  
  337.       while D > Max_TimeD loop
  338.          D := D - Seconds_In_4_YearsD;
  339.          Year_Val := Year_Val + 4;
  340.       end loop;
  341.  
  342.       --  Now we need to take the value D, which is now non-negative, and
  343.       --  break it down into seconds (to pass to the localtime_r function)
  344.       --  and fractions of seconds (for the adjustment below).
  345.  
  346.       --  Surprisingly there is no easy way to do this in Ada, and certainly
  347.       --  no easy way to do it and generate efficient code. Therefore we
  348.       --  do it at a low level, knowing that it is really represented as
  349.       --  an integer with units of Small
  350.  
  351.       declare
  352.          type D_Int is range 0 .. 2 ** (Duration'Size - 1) - 1;
  353.          for D_Int'Size use Duration'Size;
  354.  
  355.          Small_Div : constant D_Int := D_Int (1.0 / Duration'Small);
  356.          D_As_Int  : D_Int;
  357.  
  358.          function To_D_As_Int is new Unchecked_Conversion (Duration, D_Int);
  359.          function To_Duration is new Unchecked_Conversion (D_Int, Duration);
  360.  
  361.       begin
  362.          D_As_Int := To_D_As_Int (D);
  363.          Adjusted_Seconds := time_t (D_As_Int / Small_Div);
  364.          Frac_Sec := To_Duration (D_As_Int rem Small_Div);
  365.       end;
  366.  
  367.       localtime_r (Adjusted_Seconds'Unchecked_Access, Tm_Val'Unchecked_Access);
  368.  
  369.       Year_Val := Tm_Val.tm_year + 1900 + Year_Val;
  370.       Month    := Tm_Val.tm_mon + 1;
  371.       Day      := Tm_Val.tm_mday;
  372.  
  373.       --  The Seconds value is a little complex. The localtime function
  374.       --  returns the integral number of seconds, which is what we want,
  375.       --  but we want to retain the fractional part from the original
  376.       --  Time value, since this is typically stored more accurately.
  377.  
  378.       --  It is annoying
  379.  
  380.       Seconds := Duration (Tm_Val.tm_hour * 3600 +
  381.                            Tm_Val.tm_min  * 60 +
  382.                            Tm_Val.tm_sec)
  383.                    + Frac_Sec;
  384.  
  385.       --  Note: the above expression is pretty horrible, one of these days
  386.       --  we should stop using time_of and do everything ourselves to avoid
  387.       --  these unnecessary divides and multiplies???.
  388.  
  389.       --  The Year may still be out of range, since our entry test was
  390.       --  deliberately crude. Trying to make this entry test accurate is
  391.       --  tricky due to time zone adjustment issues affecting the exact
  392.       --  boundary. It is interesting to note that whether or not a given
  393.       --  Calendar.Time value gets Time_Error when split depends on the
  394.       --  current time zone setting.
  395.  
  396.       if Year_Val not in Ada_Year_Min .. Ada_Year_Max then
  397.          raise Time_Error;
  398.       else
  399.          Year := Year_Val;
  400.       end if;
  401.    end Split;
  402.  
  403.    -------------
  404.    -- Time_Of --
  405.    -------------
  406.  
  407.    function Time_Of
  408.      (Year    : Year_Number;
  409.       Month   : Month_Number;
  410.       Day     : Day_Number;
  411.       Seconds : Day_Duration := 0.0)
  412.       return    Time
  413.    is
  414.       Result_Secs : aliased time_t;
  415.       TM_Val      : aliased tm;
  416.       Int_Secs    : constant Integer := Integer (Seconds);
  417.  
  418.       Year_Val        : Integer := Year;
  419.       Duration_Adjust : Duration := 0.0;
  420.  
  421.    begin
  422.       --  The following checks are redundant with respect to the constraint
  423.       --  error checks that should normally be made on parameters, but we
  424.       --  decide to raise Constraint_Error in any case if bad values come
  425.       --  in (as a result of checks being off in the caller, or for other
  426.       --  erroneous or bounded error cases).
  427.  
  428.       if        not Year   'Valid
  429.         or else not Month  'Valid
  430.         or else not Day    'Valid
  431.         or else not Seconds'Valid
  432.       then
  433.          raise Constraint_Error;
  434.       end if;
  435.  
  436.       --  Check for Day value too large (one might expect mktime to do this
  437.       --  check, as well as the basi checks we did with 'Valid, but it seems
  438.       --  that at least on some systems, this built-in check is too weak.
  439.  
  440.       if Day > Days_In_Month (Month)
  441.         and then (Day /= 29 or Month /= 2 or Year mod 4 /= 0)
  442.       then
  443.          raise Time_Error;
  444.       end if;
  445.  
  446.       TM_Val.tm_sec  := Int_Secs mod 60;
  447.       TM_Val.tm_min  := (Int_Secs / 60) mod 60;
  448.       TM_Val.tm_hour := (Int_Secs / 60) / 60;
  449.       TM_Val.tm_mday := Day;
  450.       TM_Val.tm_mon  := Month - 1;
  451.  
  452.       --  For the year, we have to adjust it to a year that Unix can handle.
  453.       --  We do this in four year steps, since the number of days in four
  454.       --  years is constant, so the timezone effect on the conversion from
  455.       --  local time to GMT is unaffected.
  456.  
  457.       while Year_Val <= Unix_Year_Min loop
  458.          Year_Val := Year_Val + 4;
  459.          Duration_Adjust := Duration_Adjust - Seconds_In_4_YearsD;
  460.       end loop;
  461.  
  462.       while Year_Val >= Unix_Year_Max loop
  463.          Year_Val := Year_Val - 4;
  464.          Duration_Adjust := Duration_Adjust + Seconds_In_4_YearsD;
  465.       end loop;
  466.  
  467.       TM_Val.tm_year := Year_Val - 1900;
  468.  
  469.       --  Since we do not have information on daylight savings,
  470.       --  rely on the default information.
  471.  
  472.       TM_Val.tm_isdst := -1;
  473.       Result_Secs := mktime (TM_Val'Unchecked_Access);
  474.  
  475.       --  That gives us the basic value in seconds. Two adjustments are
  476.       --  needed. First we must undo the year adjustment carried out above.
  477.       --  Second we put back the fraction seconds value since in general the
  478.       --  Day_Duration value we received has additional precision which we
  479.       --  do not want to lose in the constructed result.
  480.  
  481.       return
  482.         Time (Duration (Result_Secs) +
  483.               Duration_Adjust +
  484.               (Seconds - Duration (Int_Secs)));
  485.  
  486.    end Time_Of;
  487.  
  488.    ----------
  489.    -- Year --
  490.    ----------
  491.  
  492.    function Year (Date : Time) return Year_Number is
  493.       DY : Year_Number;
  494.       DM : Month_Number;
  495.       DD : Day_Number;
  496.       DS : Day_Duration;
  497.  
  498.    begin
  499.       Split (Date, DY, DM, DD, DS);
  500.       return DY;
  501.    end Year;
  502.  
  503. begin
  504.    --  Set up the Clock soft link to the non tasking version if it has not
  505.    --  been already set.
  506.    --  If tasking is present, Clock has already set this soft link, or
  507.    --  this will be overriden during the elaboration of
  508.    --  System.Tasking.Initialization
  509.  
  510.    if SSL.Clock = null then
  511.       SSL.Clock := System.OS_Primitives.Clock'Access;
  512.    end if;
  513. end Ada.Calendar;
  514.