-- --
-- B o d y --
-- --
--- --
--- Copyright (C) 1992-2001 Free Software Foundation, Inc. --
+-- Copyright (C) 1992-2005 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- --
-- TM.all cannot be represented.
-- The following constants are used in adjusting Ada dates so that they
- -- fit into the range that can be handled by Unix (1970 - 2038). The trick
- -- is that the number of days in any four year period in the Ada range of
- -- years (1901 - 2099) has a constant number of days. This is because we
- -- have the special case of 2000 which, contrary to the normal exception
- -- for centuries, is a leap year after all.
+ -- fit into a 56 year range that can be handled by Unix (1970 included -
+ -- 2026 excluded). Dates that are not in this 56 year range are shifted
+ -- by multiples of 56 years to fit in this range.
+
+ -- The trick is that the number of days in any four year period in the Ada
+ -- range of years (1901 - 2099) has a constant number of days. This is
+ -- because we have the special case of 2000 which, contrary to the normal
+ -- exception for centuries, is a leap year after all. 56 has been chosen,
+ -- because it is not only a multiple of 4, but also a multiple of 7. Thus
+ -- two dates 56 years apart fall on the same day of the week, and the
+ -- Daylight Saving Time change dates are usually the same for these two
+ -- years.
Unix_Year_Min : constant := 1970;
- Unix_Year_Max : constant := 2038;
+ Unix_Year_Max : constant := 2026;
Ada_Year_Min : constant := 1901;
Ada_Year_Max : constant := 2099;
Days_In_Month : constant array (Month_Number) of Day_Number :=
(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
- Days_In_4_Years : constant := 365 * 3 + 366;
- Seconds_In_4_Years : constant := 86_400 * Days_In_4_Years;
- Seconds_In_4_YearsD : constant Duration := Duration (Seconds_In_4_Years);
+ Days_In_4_Years : constant := 365 * 3 + 366;
+ Seconds_In_4_Years : constant := 86_400 * Days_In_4_Years;
+ Seconds_In_56_Years : constant := Seconds_In_4_Years * 14;
+ Seconds_In_56_YearsD : constant := Duration (Seconds_In_56_Years);
---------
-- "+" --
pragma Unsuppress (Overflow_Check);
begin
return (Left + Time (Right));
-
exception
when Constraint_Error =>
raise Time_Error;
pragma Unsuppress (Overflow_Check);
begin
return (Time (Left) + Right);
-
exception
when Constraint_Error =>
raise Time_Error;
pragma Unsuppress (Overflow_Check);
begin
return Left - Time (Right);
-
exception
when Constraint_Error =>
raise Time_Error;
pragma Unsuppress (Overflow_Check);
begin
return Duration (Left) - Duration (Right);
-
exception
when Constraint_Error =>
raise Time_Error;
DM : Month_Number;
DD : Day_Number;
DS : Day_Duration;
-
begin
Split (Date, DY, DM, DD, DS);
return DD;
DM : Month_Number;
DD : Day_Number;
DS : Day_Duration;
-
begin
Split (Date, DY, DM, DD, DS);
return DM;
DM : Month_Number;
DD : Day_Number;
DS : Day_Duration;
-
begin
Split (Date, DY, DM, DD, DS);
return DS;
LowD : constant Duration := Duration (Low);
HighD : constant Duration := Duration (High);
- -- The following declare the maximum duration value that can be
- -- successfully converted to a 32-bit integer suitable for passing
- -- to the localtime_r function. Note that we cannot assume that the
- -- localtime_r function expands to accept 64-bit input on a 64-bit
- -- machine, but we can count on a 32-bit range on all machines.
-
- Max_Time : constant := 2 ** 31 - 1;
- Max_TimeD : constant Duration := Duration (Max_Time);
-
-- Finally the actual variables used in the computation
D : Duration;
D := Duration (Date);
- -- First of all, filter out completely ludicrous values. Remember
- -- that we use the full stored range of duration values, which may
- -- be significantly larger than the allowed range of Ada times. Note
- -- that these checks are wider than required to make absolutely sure
- -- that there are no end effects from time zone differences.
+ -- First of all, filter out completely ludicrous values. Remember that
+ -- we use the full stored range of duration values, which may be
+ -- significantly larger than the allowed range of Ada times. Note that
+ -- these checks are wider than required to make absolutely sure that
+ -- there are no end effects from time zone differences.
if D < LowD or else D > HighD then
raise Time_Error;
-- required range of years (the guaranteed range available is only
-- EPOCH through EPOCH + N seconds). N is in practice 2 ** 31 - 1.
- -- If we have a value outside this range, then we first adjust it
- -- to be in the required range by adding multiples of four years.
- -- For the range we are interested in, the number of days in any
- -- consecutive four year period is constant. Then we do the split
- -- on the adjusted value, and readjust the years value accordingly.
+ -- If we have a value outside this range, then we first adjust it to be
+ -- in the required range by adding multiples of 56 years. For the range
+ -- we are interested in, the number of days in any consecutive 56 year
+ -- period is constant. Then we do the split on the adjusted value, and
+ -- readjust the years value accordingly.
Year_Val := 0;
while D < 0.0 loop
- D := D + Seconds_In_4_YearsD;
- Year_Val := Year_Val - 4;
+ D := D + Seconds_In_56_YearsD;
+ Year_Val := Year_Val - 56;
end loop;
- while D > Max_TimeD loop
- D := D - Seconds_In_4_YearsD;
- Year_Val := Year_Val + 4;
+ while D >= Seconds_In_56_YearsD loop
+ D := D - Seconds_In_56_YearsD;
+ Year_Val := Year_Val + 56;
end loop;
-- Now we need to take the value D, which is now non-negative, and
- -- break it down into seconds (to pass to the localtime_r function)
- -- and fractions of seconds (for the adjustment below).
+ -- break it down into seconds (to pass to the localtime_r function) and
+ -- fractions of seconds (for the adjustment below).
-- Surprisingly there is no easy way to do this in Ada, and certainly
- -- no easy way to do it and generate efficient code. Therefore we
- -- do it at a low level, knowing that it is really represented as
- -- an integer with units of Small
+ -- no easy way to do it and generate efficient code. Therefore we do it
+ -- at a low level, knowing that it is really represented as an integer
+ -- with units of Small
declare
type D_Int is range 0 .. 2 ** (Duration'Size - 1) - 1;
Day := Tm_Val.tm_mday;
-- The Seconds value is a little complex. The localtime function
- -- returns the integral number of seconds, which is what we want,
- -- but we want to retain the fractional part from the original
- -- Time value, since this is typically stored more accurately.
+ -- returns the integral number of seconds, which is what we want, but
+ -- we want to retain the fractional part from the original Time value,
+ -- since this is typically stored more accurately.
Seconds := Duration (Tm_Val.tm_hour * 3600 +
Tm_Val.tm_min * 60 +
Tm_Val.tm_sec)
+ Frac_Sec;
- -- Note: the above expression is pretty horrible, one of these days
- -- we should stop using time_of and do everything ourselves to avoid
- -- these unnecessary divides and multiplies???.
+ -- Note: the above expression is pretty horrible, one of these days we
+ -- should stop using time_of and do everything ourselves to avoid these
+ -- unnecessary divides and multiplies???.
-- The Year may still be out of range, since our entry test was
-- deliberately crude. Trying to make this entry test accurate is
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
+ -- 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
-- erroneous or bounded error cases).
if not Year 'Valid
end if;
-- Check for Day value too large (one might expect mktime to do this
- -- check, as well as the basi checks we did with 'Valid, but it seems
+ -- check, as well as the basic checks we did with 'Valid, but it seems
-- that at least on some systems, this built-in check is too weak).
if Day > Days_In_Month (Month)
TM_Val.tm_mon := Month - 1;
-- For the year, we have to adjust it to a year that Unix can handle.
- -- We do this in four year steps, since the number of days in four
- -- years is constant, so the timezone effect on the conversion from
- -- local time to GMT is unaffected.
-
- while Year_Val <= Unix_Year_Min loop
- Year_Val := Year_Val + 4;
- Duration_Adjust := Duration_Adjust - Seconds_In_4_YearsD;
+ -- We do this in 56 year steps, since the number of days in 56 years is
+ -- constant, so the timezone effect on the conversion from local time
+ -- to GMT is unaffected; also the DST change dates are usually not
+ -- modified.
+
+ while Year_Val < Unix_Year_Min loop
+ Year_Val := Year_Val + 56;
+ Duration_Adjust := Duration_Adjust - Seconds_In_56_YearsD;
end loop;
while Year_Val >= Unix_Year_Max loop
- Year_Val := Year_Val - 4;
- Duration_Adjust := Duration_Adjust + Seconds_In_4_YearsD;
+ Year_Val := Year_Val - 56;
+ Duration_Adjust := Duration_Adjust + Seconds_In_56_YearsD;
end loop;
TM_Val.tm_year := Year_Val - 1900;
- -- Since we do not have information on daylight savings,
- -- rely on the default information.
+ -- Since we do not have information on daylight savings, rely on the
+ -- default information.
TM_Val.tm_isdst := -1;
Result_Secs := mktime (TM_Val'Unchecked_Access);
-- That gives us the basic value in seconds. Two adjustments are
-- needed. First we must undo the year adjustment carried out above.
-- Second we put back the fraction seconds value since in general the
- -- Day_Duration value we received has additional precision which we
- -- do not want to lose in the constructed result.
+ -- Day_Duration value we received has additional precision which we do
+ -- not want to lose in the constructed result.
return
Time (Duration (Result_Secs) +
Duration_Adjust +
(Seconds - Duration (Int_Secs)));
-
end Time_Of;
----------
DM : Month_Number;
DD : Day_Number;
DS : Day_Duration;
-
begin
Split (Date, DY, DM, DD, DS);
return DY;