diff options
author | charlet <charlet@138bc75d-0d04-0410-961f-82ee72b054a4> | 2007-06-06 10:14:46 +0000 |
---|---|---|
committer | charlet <charlet@138bc75d-0d04-0410-961f-82ee72b054a4> | 2007-06-06 10:14:46 +0000 |
commit | df60170ddc5409cd057bef02d27df3806811d967 (patch) | |
tree | 82b6ae097e3527bf05464afd6655618c004ec21e /gcc/ada/a-calend.adb | |
parent | bf941b70cecd7a42e70e911a26d479394cc64d5c (diff) | |
download | gcc-df60170ddc5409cd057bef02d27df3806811d967.tar.gz |
2007-04-20 Hristian Kirtchev <kirtchev@adacore.com>
* a-calend.ads, a-calend.adb, a-calend-vms.ads, a-calend-vms.adb ("-"
(Time, Time)): Use To_Relative_Time rather than manual calculation to
express the bounds of Duration as Time. Raise Time_Error when the
result is greater or equal to the higher bound of Duration (on the
margin case).
("+" (Time, Duration)): Reorder code. Remove the declaration of constant
Ada_High_And_Leaps.
("-" (Time, Duration)): Reorder code. Remove the declaration of constant
Ada_High_And_Leaps.
("-" (Time, Time)): Reorder code.
(All_Leap_Seconds): Removed.
(Arithmetic_Operations.Add): Remove sign related kludge.
(Arithmetic_Operations.Difference): Control the leaps seconds processing
with flag Leap_Support.
(Arithmetic_Operations.Subtract): Remove sign related kludge.
(Check_Within_Time_Bounds): New procedure.
(Clock): Control the leap seconds processing with flag Leap_Support.
(Cumulative_Leap_Seconds): Assert that the target supports leap seconds.
(Formatting_Operations.Split): Control the leap seconds processing with
flag Leap_Support.
(Formatting_Operations.Time_Of): Control the leaps seconds processing
with flag Leap_Support. Adjust the year, month and day (if applicable)
when the value of day seconds designates a new day.
(Split): Use parameter associations for better readability. Integrate
flag Is_Ada_05.
(Time_Of): Use parameter associations for better readability. Integrate
flag Is_Ada_05.
* a-calfor.adb (Split): Use parameter associations for better
readability. Integrate flag Is_Ada_05.
(Time_Of): Remove flag Leap_Checks. Use parameter associations for
better readability. Integrate flag Is_Ada_05.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@125363 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/ada/a-calend.adb')
-rw-r--r-- | gcc/ada/a-calend.adb | 1190 |
1 files changed, 544 insertions, 646 deletions
diff --git a/gcc/ada/a-calend.adb b/gcc/ada/a-calend.adb index 0af43fd7536..c2dc77c7651 100644 --- a/gcc/ada/a-calend.adb +++ b/gcc/ada/a-calend.adb @@ -6,7 +6,7 @@ -- -- -- B o d y -- -- -- --- Copyright (C) 1992-2006, Free Software Foundation, Inc. -- +-- Copyright (C) 1992-2007, 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- -- @@ -47,9 +47,9 @@ package body Ada.Calendar is -- -- Because time is measured in different units and from different origins -- on various targets, a system independent model is incorporated into - -- Ada.Calendar. The idea behing the design is to encapsulate all target + -- Ada.Calendar. The idea behind the design is to encapsulate all target -- dependent machinery in a single package, thus providing a uniform - -- interface to any existing and potential children. + -- interface to all existing and any potential children. -- package Ada.Calendar -- procedure Split (5 parameters) -------+ @@ -76,17 +76,21 @@ package body Ada.Calendar is -- Local Subprograms -- ----------------------- + procedure Check_Within_Time_Bounds (T : Time_Rep); + -- Ensure that a time representation value falls withing the bounds of Ada + -- time. Leap seconds support is taken into account. + procedure Cumulative_Leap_Seconds - (Start_Date : Time; - End_Date : Time; + (Start_Date : Time_Rep; + End_Date : Time_Rep; Elapsed_Leaps : out Natural; - Next_Leap_Sec : out Time); + Next_Leap : out Time_Rep); -- Elapsed_Leaps is the sum of the leap seconds that have occured on or -- after Start_Date and before (strictly before) End_Date. Next_Leap_Sec -- represents the next leap second occurence on or after End_Date. If - -- there are no leaps seconds after End_Date, After_Last_Leap is returned. - -- After_Last_Leap can be used as End_Date to count all the leap seconds - -- that have occured on or after Start_Date. + -- there are no leaps seconds after End_Date, End_Of_Time is returned. + -- End_Of_Time can be used as End_Date to count all the leap seconds that + -- have occured on or after Start_Date. -- -- Note: Any sub seconds of Start_Date and End_Date are discarded before -- the calculations are done. For instance: if 113 seconds is a leap @@ -100,46 +104,77 @@ package body Ada.Calendar is -- After_Last_Leap is designed so that this comparison works without -- having to first check if Next_Leap_Sec is a valid leap second. - function To_Abs_Duration (T : Time) return Duration; - -- Convert a time value into a duration value. Note that the returned - -- duration is always positive. + function Duration_To_Time_Rep is + new Ada.Unchecked_Conversion (Duration, Time_Rep); + -- Convert a duration value into a time representation value + + function Time_Rep_To_Duration is + new Ada.Unchecked_Conversion (Time_Rep, Duration); + -- Convert a time representation value into a duration value + + ----------------- + -- Local Types -- + ----------------- + + -- An integer time duration. The type is used whenever a positive elapsed + -- duration is needed, for instance when splitting a time value. Here is + -- how Time_Rep and Time_Dur are related: + + -- 'First Ada_Low Ada_High 'Last + -- Time_Rep: +-------+------------------------+---------+ + -- Time_Dur: +------------------------+---------+ + -- 0 'Last - function To_Abs_Time (D : Duration) return Time; - -- Return the time equivalent of a duration value. Since time cannot be - -- negative, the absolute value of D is used. It is upto the called to - -- decide how to handle negative durations converted into time. + type Time_Dur is range 0 .. 2 ** 63 - 1; --------------------- -- Local Constants -- --------------------- + -- Currently none of the GNAT targets support leap seconds. At some point + -- it might be necessary to query a C function to determine if the target + -- supports leap seconds, but for now this is deemed unnecessary. + + Leap_Support : constant Boolean := False; + Leap_Seconds_Count : constant Natural := 23; + Ada_Min_Year : constant Year_Number := Year_Number'First; - After_Last_Leap : constant Time := Time'Last; - Leap_Seconds_Count : constant Natural := 23; Secs_In_Four_Years : constant := (3 * 365 + 366) * Secs_In_Day; Secs_In_Non_Leap_Year : constant := 365 * Secs_In_Day; - Time_Zero : constant Time := Time'First; - -- Even though the upper bound of Ada time is 2399-12-31 86_399.999999999 - -- GMT, it must be shifted to include all leap seconds. + -- 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 + -- for the non-leap centenial years. + + Ada_Low : constant Time_Rep := -(61 * 366 + 188 * 365) * Nanos_In_Day; + Ada_High : constant Time_Rep := (60 * 366 + 190 * 365) * Nanos_In_Day; + + -- Even though the upper bound of time is 2399-12-31 23:59:59.999999999 + -- UTC, it must be increased to include all leap seconds. - Ada_High_And_Leaps : constant Time := - Ada_High + Time (Leap_Seconds_Count) * Nano; + Ada_High_And_Leaps : constant Time_Rep := + Ada_High + Time_Rep (Leap_Seconds_Count) * Nano; - Hard_Ada_High_And_Leaps : constant Time := - Hard_Ada_High + - Time (Leap_Seconds_Count) * Nano; + -- Two constants used in the calculations of elapsed leap seconds. + -- End_Of_Time is later than Ada_High in time zone -28. Start_Of_Time + -- is earlier than Ada_Low in time zone +28. + + End_Of_Time : constant Time_Rep := + Ada_High + Time_Rep (3) * Nanos_In_Day; + 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 GMT. + -- start of Ada time in UTC. - Unix_Min : constant Time := (17 * 366 + 52 * 365) * Nanos_In_Day; + Unix_Min : constant Time_Rep := + Ada_Low + Time_Rep (17 * 366 + 52 * 365) * Nanos_In_Day; Cumulative_Days_Before_Month : constant array (Month_Number) of Natural := (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334); - Leap_Second_Times : array (1 .. Leap_Seconds_Count) of Time; + Leap_Second_Times : array (1 .. Leap_Seconds_Count) of Time_Rep; -- Each value represents a time value which is one second before a leap -- second occurence. This table is populated during the elaboration of -- Ada.Calendar. @@ -151,46 +186,21 @@ package body Ada.Calendar is function "+" (Left : Time; Right : Duration) return Time is pragma Unsuppress (Overflow_Check); - begin - if Right = 0.0 then - return Left; + Left_N : constant Time_Rep := Time_Rep (Left); + Res_N : Time_Rep; - elsif Right < 0.0 then - - -- Type Duration has one additional number in its negative subrange, - -- which is Duration'First. The subsequent invocation of "-" will - -- perform among other things an Unchecked_Conversion on that - -- particular value, causing overflow. If not properly handled, - -- the erroneous value will cause an infinite recursion between "+" - -- and "-". To properly handle this boundary case, we make a small - -- adjustment of one second to Duration'First. - - if Right = Duration'First then - return Left - abs (Right + 1.0) - 1.0; - else - return Left - abs (Right); - end if; - - else - declare - -- The input time value has been normalized to GMT + begin + -- Trivial case - Result : constant Time := Left + To_Abs_Time (Right); + if Right = Duration (0.0) then + return Left; + end if; - begin - -- The end result may excede the upper bound of Ada time. Note - -- that the comparison operator is ">=" rather than ">" since - -- the smallest increment of 0.000000001 to the legal end of - -- time (2399-12-31 86_399.999999999) will render the result - -- equal to Ada_High (2400-1-1 0.0). + Res_N := Left_N + Duration_To_Time_Rep (Right); - if Result >= Ada_High_And_Leaps then - raise Time_Error; - end if; + Check_Within_Time_Bounds (Res_N); - return Result; - end; - end if; + return Time (Res_N); exception when Constraint_Error => @@ -209,38 +219,21 @@ package body Ada.Calendar is function "-" (Left : Time; Right : Duration) return Time is pragma Unsuppress (Overflow_Check); - begin - if Right = 0.0 then - return Left; - - elsif Right < 0.0 then - return Left + abs (Right); - - else - declare - Result : Time; - Right_T : constant Time := To_Abs_Time (Right); + Left_N : constant Time_Rep := Time_Rep (Left); + Res_N : Time_Rep; - begin - -- Subtracting a larger time value from a smaller time value - -- will cause a wrap around since Time is a modular type. Note - -- that the time value has been normalized to GMT. + begin + -- Trivial case - if Left < Right_T then - raise Time_Error; - end if; + if Right = Duration (0.0) then + return Left; + end if; - Result := Left - Right_T; + Res_N := Left_N - Duration_To_Time_Rep (Right); - if Result < Ada_Low - or else Result > Ada_High_And_Leaps - then - raise Time_Error; - end if; + Check_Within_Time_Bounds (Res_N); - return Result; - end; - end if; + return Time (Res_N); exception when Constraint_Error => @@ -250,54 +243,25 @@ package body Ada.Calendar is function "-" (Left : Time; Right : Time) return Duration is pragma Unsuppress (Overflow_Check); - function To_Time is new Ada.Unchecked_Conversion (Duration, Time); + -- The bounds of type Duration expressed as time representations - -- Since the absolute values of the upper and lower bound of duration - -- are denoted by the same number, it is sufficend to use Duration'Last - -- when performing out of range checks. + Dur_Low : constant Time_Rep := Duration_To_Time_Rep (Duration'First); + Dur_High : constant Time_Rep := Duration_To_Time_Rep (Duration'Last); - Duration_Bound : constant Time := To_Time (Duration'Last); - - Earlier : Time; - Later : Time; - Negate : Boolean := False; - Result : Time; - Result_D : Duration; + Res_N : Time_Rep; begin - -- This routine becomes a little tricky since time cannot be negative, - -- but the subtraction of two time values can produce a negative value. - - if Left > Right then - Later := Left; - Earlier := Right; - else - Later := Right; - Earlier := Left; - Negate := True; - end if; + Res_N := Time_Rep (Left) - Time_Rep (Right); - Result := Later - Earlier; + -- The result does not fit in a duration value - -- Check whether the resulting difference is within the range of type - -- Duration. The following two conditions are examined with the same - -- piece of code: - -- - -- positive result > positive upper bound of duration - -- - -- negative (negative result) > abs (negative bound of duration) - - if Result > Duration_Bound then + if Res_N < Dur_Low + or else Res_N > Dur_High + then raise Time_Error; end if; - Result_D := To_Abs_Duration (Result); - - if Negate then - Result_D := -Result_D; - end if; - - return Result_D; + return Time_Rep_To_Duration (Res_N); exception when Constraint_Error => raise Time_Error; @@ -339,39 +303,62 @@ package body Ada.Calendar is return Time_Rep (Left) >= Time_Rep (Right); end ">="; + ------------------------------ + -- Check_Within_Time_Bounds -- + ------------------------------ + + procedure Check_Within_Time_Bounds (T : Time_Rep) is + begin + if Leap_Support then + if T < Ada_Low or else T > Ada_High_And_Leaps then + raise Time_Error; + end if; + else + if T < Ada_Low or else T > Ada_High then + raise Time_Error; + end if; + end if; + end Check_Within_Time_Bounds; + ----------- -- Clock -- ----------- function Clock return Time is Elapsed_Leaps : Natural; - Next_Leap : Time; - - -- The system clock returns the time in GMT since the Unix Epoch of - -- 1970-1-1 0.0. We perform an origin shift to the Ada Epoch by adding - -- the number of nanoseconds between the two origins. + Next_Leap_N : Time_Rep; - Now : Time := To_Abs_Time (System.OS_Primitives.Clock) + Unix_Min; + -- The system clock returns the time in UTC since the Unix Epoch of + -- 1970-01-01 00:00:00.0. We perform an origin shift to the Ada Epoch + -- by adding the number of nanoseconds between the two origins. - Rounded_Now : constant Time := Now - (Now mod Nano); + Res_N : Time_Rep := + Duration_To_Time_Rep (System.OS_Primitives.Clock) + + Unix_Min; begin - -- Determine how many leap seconds have elapsed until this moment + -- If the target supports leap seconds, determine the number of leap + -- seconds elapsed until this moment. + + if Leap_Support then + Cumulative_Leap_Seconds + (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N); - Cumulative_Leap_Seconds (Time_Zero, Now, Elapsed_Leaps, Next_Leap); + -- The system clock may fall exactly on a leap second - Now := Now + Time (Elapsed_Leaps) * Nano; + if Res_N >= Next_Leap_N then + Elapsed_Leaps := Elapsed_Leaps + 1; + end if; - -- The system clock may fall exactly on a leap second occurence + -- The target does not support leap seconds - if Rounded_Now = Next_Leap then - Now := Now + Time (1) * Nano; + else + Elapsed_Leaps := 0; end if; - -- Add the buffer set aside for time zone processing since Split in - -- Ada.Calendar.Formatting_Operations expects it to be there. + Res_N := Res_N + Time_Rep (Elapsed_Leaps) * Nano; - return Now + Buffer_N; + return Time (Res_N); end Clock; ----------------------------- @@ -379,23 +366,22 @@ package body Ada.Calendar is ----------------------------- procedure Cumulative_Leap_Seconds - (Start_Date : Time; - End_Date : Time; + (Start_Date : Time_Rep; + End_Date : Time_Rep; Elapsed_Leaps : out Natural; - Next_Leap_Sec : out Time) + Next_Leap : out Time_Rep) is End_Index : Positive; - End_T : Time := End_Date; + End_T : Time_Rep := End_Date; Start_Index : Positive; - Start_T : Time := Start_Date; + Start_T : Time_Rep := Start_Date; begin - -- Both input dates need to be normalized to GMT in order for this - -- routine to work properly. + -- Both input dates must be normalized to UTC - pragma Assert (End_Date >= Start_Date); + pragma Assert (Leap_Support and then End_Date >= Start_Date); - Next_Leap_Sec := After_Last_Leap; + Next_Leap := End_Of_Time; -- Make sure that the end date does not excede the upper bound -- of Ada time. @@ -416,12 +402,12 @@ package body Ada.Calendar is if End_T < Leap_Second_Times (1) then Elapsed_Leaps := 0; - Next_Leap_Sec := Leap_Second_Times (1); + Next_Leap := Leap_Second_Times (1); return; elsif Start_T > Leap_Second_Times (Leap_Seconds_Count) then Elapsed_Leaps := 0; - Next_Leap_Sec := After_Last_Leap; + Next_Leap := End_Of_Time; return; end if; @@ -458,7 +444,7 @@ package body Ada.Calendar is end loop; if End_Index <= Leap_Seconds_Count then - Next_Leap_Sec := Leap_Second_Times (End_Index); + Next_Leap := Leap_Second_Times (End_Index); end if; Elapsed_Leaps := End_Index - Start_Index; @@ -549,12 +535,24 @@ package body Ada.Calendar is Se : Integer; Ss : Duration; Le : Boolean; - Tz : constant Long_Integer := - Time_Zones_Operations.UTC_Time_Offset (Date) / 60; begin + -- Even though the input time zone is UTC (0), the flag Is_Ada_05 will + -- ensure that Split picks up the local time zone. + Formatting_Operations.Split - (Date, Year, Month, Day, Seconds, H, M, Se, Ss, Le, Tz); + (Date => Date, + Year => Year, + Month => Month, + Day => Day, + Day_Secs => Seconds, + Hour => H, + Minute => M, + Second => Se, + Sub_Sec => Ss, + Leap_Sec => Le, + Is_Ada_05 => False, + Time_Zone => 0); -- Validity checks @@ -586,11 +584,9 @@ package body Ada.Calendar is Se : constant Integer := 1; Ss : constant Duration := 0.1; - Mid_Offset : Long_Integer; - Mid_Result : Time; - Offset : Long_Integer; - begin + -- Validity checks + if not Year'Valid or else not Month'Valid or else not Day'Valid @@ -599,96 +595,25 @@ package body Ada.Calendar is raise Time_Error; end if; - -- Building a time value in a local time zone is tricky since the - -- local time zone offset at the point of creation may not be the - -- same as the actual time zone offset designated by the input - -- values. The following example is relevant to New York, USA. - -- - -- Creation date: 2006-10-10 0.0 Offset -240 mins (in DST) - -- Actual date : 1901-01-01 0.0 Offset -300 mins (no DST) - - -- We first start by obtaining the current local time zone offset - -- using Ada.Calendar.Clock, then building an intermediate time - -- value using that offset. - - Mid_Offset := Time_Zones_Operations.UTC_Time_Offset (Clock) / 60; - Mid_Result := Formatting_Operations.Time_Of - (Year, Month, Day, Seconds, H, M, Se, Ss, - Leap_Sec => False, - Leap_Checks => False, - Use_Day_Secs => True, - Time_Zone => Mid_Offset); - - -- This is the true local time zone offset of the input time values - - Offset := Time_Zones_Operations.UTC_Time_Offset (Mid_Result) / 60; - - -- It is possible that at the point of invocation of Time_Of, both - -- the current local time zone offset and the one designated by the - -- input values are in the same DST mode. - - if Offset = Mid_Offset then - return Mid_Result; - - -- In this case we must calculate the new time with the new offset. It - -- is no sufficient to just take the relative difference between the - -- two offsets and adjust the intermediate result, because this does not - -- work around leap second times. - - else - declare - Result : constant Time := - Formatting_Operations.Time_Of - (Year, Month, Day, Seconds, H, M, Se, Ss, - Leap_Sec => False, - Leap_Checks => False, - Use_Day_Secs => True, - Time_Zone => Offset); - - begin - return Result; - end; - end if; + -- Even though the input time zone is UTC (0), the flag Is_Ada_05 will + -- ensure that Split picks up the local time zone. + + return + Formatting_Operations.Time_Of + (Year => Year, + Month => Month, + Day => Day, + Day_Secs => Seconds, + Hour => H, + Minute => M, + Second => Se, + Sub_Sec => Ss, + Leap_Sec => False, + Use_Day_Secs => True, + Is_Ada_05 => False, + Time_Zone => 0); end Time_Of; - --------------------- - -- To_Abs_Duration -- - --------------------- - - function To_Abs_Duration (T : Time) return Duration is - pragma Unsuppress (Overflow_Check); - function To_Duration is new Ada.Unchecked_Conversion (Time, Duration); - - begin - return To_Duration (T); - - exception - when Constraint_Error => - raise Time_Error; - end To_Abs_Duration; - - ----------------- - -- To_Abs_Time -- - ----------------- - - function To_Abs_Time (D : Duration) return Time is - pragma Unsuppress (Overflow_Check); - function To_Time is new Ada.Unchecked_Conversion (Duration, Time); - - begin - -- This operation assumes that D is positive - - if D < 0.0 then - raise Constraint_Error; - end if; - - return To_Time (D); - - exception - when Constraint_Error => - raise Time_Error; - end To_Abs_Time; - ---------- -- Year -- ---------- @@ -703,9 +628,9 @@ package body Ada.Calendar is return Y; end Year; - -- The following packages assume that Time is a modular 64 bit integer + -- The following packages assume that Time is a signed 64 bit integer -- type, the units are nanoseconds and the origin is the start of Ada - -- time (1901-1-1 0.0). + -- time (1901-01-01 00:00:00.0 UTC). --------------------------- -- Arithmetic_Operations -- @@ -718,27 +643,23 @@ package body Ada.Calendar is --------- function Add (Date : Time; Days : Long_Integer) return Time is + pragma Unsuppress (Overflow_Check); + + Date_N : constant Time_Rep := Time_Rep (Date); + Res_N : Time_Rep; + begin + -- Trivial case + if Days = 0 then return Date; + end if; - elsif Days < 0 then - return Subtract (Date, abs (Days)); - - else - declare - Result : constant Time := Date + Time (Days) * Nanos_In_Day; - - begin - -- The result excedes the upper bound of Ada time + Res_N := Date_N + Time_Rep (Days) * Nanos_In_Day; - if Result > Ada_High_And_Leaps then - raise Time_Error; - end if; + Check_Within_Time_Bounds (Res_N); - return Result; - end; - end if; + return Time (Res_N); exception when Constraint_Error => @@ -756,54 +677,70 @@ package body Ada.Calendar is Seconds : out Duration; Leap_Seconds : out Integer) is - Diff_N : Time; - Diff_S : Time; - Earlier : Time; + Res_Dur : Time_Dur; + Earlier : Time_Rep; + Earlier_Sub : Time_Rep; Elapsed_Leaps : Natural; - Later : Time; + Later : Time_Rep; + Later_Sub : Time_Rep; Negate : Boolean := False; - Next_Leap : Time; + Next_Leap_N : Time_Rep; Sub_Seconds : Duration; begin - -- Both input time values are assumed to be in GMT + -- Both input time values are assumed to be in UTC if Left >= Right then - Later := Left; - Earlier := Right; + Later := Time_Rep (Left); + Earlier := Time_Rep (Right); else - Later := Right; - Earlier := Left; + Later := Time_Rep (Right); + Earlier := Time_Rep (Left); Negate := True; end if; - -- First process the leap seconds + -- If the target supports leap seconds, process them - Cumulative_Leap_Seconds (Earlier, Later, Elapsed_Leaps, Next_Leap); + if Leap_Support then + Cumulative_Leap_Seconds + (Earlier, Later, Elapsed_Leaps, Next_Leap_N); - if Later >= Next_Leap then - Elapsed_Leaps := Elapsed_Leaps + 1; + if Later >= Next_Leap_N then + Elapsed_Leaps := Elapsed_Leaps + 1; + end if; + + -- The target does not support leap seconds + + else + Elapsed_Leaps := 0; end if; - Diff_N := Later - Earlier - Time (Elapsed_Leaps) * Nano; + -- Sub seconds - -- Sub second processing + Earlier_Sub := Earlier mod Nano; + Later_Sub := Later mod Nano; - Sub_Seconds := Duration (Diff_N mod Nano) / Nano_F; + if Later_Sub < Earlier_Sub then + Later_Sub := Later_Sub + Time_Rep (1) * Nano; + Later := Later - Time_Rep (1) * Nano; + end if; - -- Convert to seconds. Note that his action eliminates the sub - -- seconds automatically. + Sub_Seconds := Duration (Later_Sub - Earlier_Sub) / Nano_F; - Diff_S := Diff_N / Nano; + Res_Dur := Time_Dur (Later / Nano - Earlier / Nano) - + Time_Dur (Elapsed_Leaps); - Days := Long_Integer (Diff_S / Secs_In_Day); - Seconds := Duration (Diff_S mod Secs_In_Day) + Sub_Seconds; + Days := Long_Integer (Res_Dur / Secs_In_Day); + Seconds := Duration (Res_Dur mod Secs_In_Day) + Sub_Seconds; Leap_Seconds := Integer (Elapsed_Leaps); if Negate then - Days := -Days; - Seconds := -Seconds; - Leap_Seconds := -Leap_Seconds; + Days := -Days; + Seconds := -Seconds; + + if Leap_Seconds /= 0 then + Leap_Seconds := -Leap_Seconds; + end if; end if; end Difference; @@ -812,37 +749,23 @@ package body Ada.Calendar is -------------- function Subtract (Date : Time; Days : Long_Integer) return Time is - begin - if Days = 0 then - return Date; + pragma Unsuppress (Overflow_Check); - elsif Days < 0 then - return Add (Date, abs (Days)); + Date_N : constant Time_Rep := Time_Rep (Date); + Res_N : Time_Rep; - else - declare - Days_T : constant Time := Time (Days) * Nanos_In_Day; - Result : Time; - - begin - -- Subtracting a larger number of days from a smaller time - -- value will cause wrap around since time is a modular type. + begin + -- Trivial case - if Date < Days_T then - raise Time_Error; - end if; + if Days = 0 then + return Date; + end if; - Result := Date - Days_T; + Res_N := Date_N - Time_Rep (Days) * Nanos_In_Day; - if Result < Ada_Low - or else Result > Ada_High_And_Leaps - then - raise Time_Error; - end if; + Check_Within_Time_Bounds (Res_N); - return Result; - end; - end if; + return Time (Res_N); exception when Constraint_Error => @@ -860,37 +783,39 @@ package body Ada.Calendar is -- To_Duration -- ----------------- - function To_Duration (Ada_Time : Time) return Duration is + function To_Duration (Date : Time) return Duration is Elapsed_Leaps : Natural; - Modified_Time : Time; - Next_Leap : Time; - Result : Duration; - Rounded_Time : Time; + Next_Leap_N : Time_Rep; + Res_N : Time_Rep; begin - Modified_Time := Ada_Time; - Rounded_Time := Modified_Time - (Modified_Time mod Nano); + Res_N := Time_Rep (Date); - -- Remove all leap seconds + -- If the target supports leap seconds, remove any leap seconds + -- elapsed upto the input date. - Cumulative_Leap_Seconds - (Time_Zero, Modified_Time, Elapsed_Leaps, Next_Leap); + if Leap_Support then + Cumulative_Leap_Seconds + (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N); - Modified_Time := Modified_Time - Time (Elapsed_Leaps) * Nano; + -- The input time value may fall on a leap second occurence - -- The input time value may fall on a leap second occurence + if Res_N >= Next_Leap_N then + Elapsed_Leaps := Elapsed_Leaps + 1; + end if; - if Rounded_Time = Next_Leap then - Modified_Time := Modified_Time - Time (1) * Nano; - end if; + -- The target does not support leap seconds - -- Perform a shift in origins + else + Elapsed_Leaps := 0; + end if; - Result := Modified_Time - Unix_Min; + Res_N := Res_N - Time_Rep (Elapsed_Leaps) * Nano; - -- Remove the buffer period used in time zone processing + -- Perform a shift in origins, note that enforcing type Time on + -- both operands will invoke Ada.Calendar."-". - return Result - Buffer_D; + return Time (Res_N) - Time (Unix_Min); end To_Duration; end Delays_Operations; @@ -908,34 +833,58 @@ package body Ada.Calendar is Y : Year_Number; Mo : Month_Number; D : Day_Number; - Dd : Day_Duration; + Ds : Day_Duration; H : Integer; Mi : Integer; Se : Integer; Su : Duration; Le : Boolean; - Day_Count : Long_Integer; - Midday_Date_S : Time; + Day_Count : Long_Integer; + Res_Dur : Time_Dur; + Res_N : Time_Rep; begin Formatting_Operations.Split - (Date, Y, Mo, D, Dd, H, Mi, Se, Su, Le, 0); - - -- Build a time value in the middle of the same day, remove the - -- lower buffer and convert the time value to seconds. - - Midday_Date_S := (Formatting_Operations.Time_Of - (Y, Mo, D, 0.0, 12, 0, 0, 0.0, - Leap_Sec => False, - Leap_Checks => False, - Use_Day_Secs => False, - Time_Zone => 0) - Buffer_N) / Nano; + (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)); + + -- Determine the elapsed seconds since the start of Ada time + + Res_Dur := Time_Dur (Res_N / Nano - Ada_Low / Nano); -- Count the number of days since the start of Ada time. 1901-1-1 -- GMT was a Tuesday. - Day_Count := Long_Integer (Midday_Date_S / Secs_In_Day) + 1; + Day_Count := Long_Integer (Res_Dur / Secs_In_Day) + 1; return Integer (Day_Count mod 7); end Day_Of_Week; @@ -945,180 +894,139 @@ package body Ada.Calendar is ----------- procedure Split - (Date : Time; - Year : out Year_Number; - Month : out Month_Number; - Day : out Day_Number; - Day_Secs : out Day_Duration; - Hour : out Integer; - Minute : out Integer; - Second : out Integer; - Sub_Sec : out Duration; - Leap_Sec : out Boolean; - Time_Zone : Long_Integer) + (Date : Time; + Year : out Year_Number; + Month : out Month_Number; + Day : out Day_Number; + Day_Secs : out Day_Duration; + Hour : out Integer; + Minute : out Integer; + Second : out Integer; + Sub_Sec : out Duration; + Leap_Sec : out Boolean; + Is_Ada_05 : Boolean; + Time_Zone : Long_Integer) is -- The following constants represent the number of nanoseconds -- elapsed since the start of Ada time to and including the non -- leap centenial years. - Year_2101 : constant Time := (49 * 366 + 151 * 365) * Nanos_In_Day; - Year_2201 : constant Time := (73 * 366 + 227 * 365) * Nanos_In_Day; - Year_2301 : constant Time := (97 * 366 + 303 * 365) * Nanos_In_Day; - - Abs_Time_Zone : Time; - Day_Seconds : Natural; - Elapsed_Leaps : Natural; - Four_Year_Segs : Natural; - Hour_Seconds : Natural; - Is_Leap_Year : Boolean; - Modified_Date_N : Time; - Modified_Date_S : Time; - Next_Leap_N : Time; - Rem_Years : Natural; - Rounded_Date_N : Time; - Year_Day : Natural; + Year_2101 : constant Time_Rep := Ada_Low + + Time_Rep (49 * 366 + 151 * 365) * Nanos_In_Day; + Year_2201 : constant Time_Rep := Ada_Low + + Time_Rep (73 * 366 + 227 * 365) * Nanos_In_Day; + Year_2301 : constant Time_Rep := Ada_Low + + Time_Rep (97 * 366 + 303 * 365) * Nanos_In_Day; + + Date_Dur : Time_Dur; + Date_N : Time_Rep; + Day_Seconds : Natural; + Elapsed_Leaps : Natural; + Four_Year_Segs : Natural; + Hour_Seconds : Natural; + Is_Leap_Year : Boolean; + Next_Leap_N : Time_Rep; + Rem_Years : Natural; + Sub_Sec_N : Time_Rep; + Year_Day : Natural; begin - Modified_Date_N := Date; + Date_N := Time_Rep (Date); - if Modified_Date_N < Hard_Ada_Low - or else Modified_Date_N > Hard_Ada_High_And_Leaps - then - raise Time_Error; - end if; + -- Step 1: Leap seconds processing in UTC - -- Step 1: Leap seconds processing in GMT - - -- Day_Duration: 86_398 86_399 X (86_400) 0 (1) 1 (2) - -- Time : --+-------+-------+----------+------+--> - -- Seconds : 58 59 60 (Leap) 1 2 - - -- o Modified_Date_N falls between 86_399 and X (86_400) - -- Elapsed_Leaps = X - 1 leaps - -- Rounded_Date_N = 86_399 - -- Next_Leap_N = X (86_400) - -- Leap_Sec = False - - -- o Modified_Date_N falls exactly on X (86_400) - -- Elapsed_Leaps = X - 1 leaps - -- Rounded_Date_N = X (86_400) - -- Next_Leap_N = X (86_400) - -- Leap_Sec = True - -- An invisible leap second will be added. - - -- o Modified_Date_N falls between X (86_400) and 0 (1) - -- Elapsed_Leaps = X - 1 leaps - -- Rounded_Date_N = X (86_400) - -- Next_Leap_N = X (86_400) - -- Leap_Sec = True - -- An invisible leap second will be added. - - -- o Modified_Date_N falls on 0 (1) - -- Elapsed_Leaps = X - -- Rounded_Date_N = 0 (1) - -- Next_Leap_N = X + 1 - -- Leap_Sec = False - -- The invisible leap second has already been accounted for in - -- Elapsed_Leaps. + if Leap_Support then + Cumulative_Leap_Seconds + (Start_Of_Time, Date_N, Elapsed_Leaps, Next_Leap_N); - Cumulative_Leap_Seconds - (Time_Zero, Modified_Date_N, Elapsed_Leaps, Next_Leap_N); + Leap_Sec := Date_N >= Next_Leap_N; - Rounded_Date_N := Modified_Date_N - (Modified_Date_N mod Nano); - Leap_Sec := Rounded_Date_N = Next_Leap_N; - Modified_Date_N := Modified_Date_N - Time (Elapsed_Leaps) * Nano; + if Leap_Sec then + Elapsed_Leaps := Elapsed_Leaps + 1; + end if; + + -- The target does not support leap seconds - if Leap_Sec then - Modified_Date_N := Modified_Date_N - Time (1) * Nano; + else + Elapsed_Leaps := 0; + Leap_Sec := False; end if; + 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. - if Time_Zone /= 0 then - Abs_Time_Zone := Time (abs (Time_Zone)) * 60 * Nano; - - if Time_Zone < 0 then - -- The following test is obsolete since the date already - -- contains the dedicated buffer for time zones, thus no - -- error will be raised. However it is a good idea to keep - -- it should the representation of time change. - - Modified_Date_N := Modified_Date_N - Abs_Time_Zone; - else - Modified_Date_N := Modified_Date_N + Abs_Time_Zone; + if Is_Ada_05 then + if Time_Zone /= 0 then + Date_N := Date_N + Time_Rep (Time_Zone) * 60 * Nano; end if; - end if; - -- After the elapsed leap seconds have been removed and the date - -- has been normalized, it should fall withing the soft bounds of - -- Ada time. + -- Ada 83 and 95 - if Modified_Date_N < Ada_Low - or else Modified_Date_N > Ada_High - then - raise Time_Error; + else + declare + Off : constant Long_Integer := + Time_Zones_Operations.UTC_Time_Offset (Time (Date_N)); + begin + Date_N := Date_N + Time_Rep (Off) * Nano; + end; end if; - -- Before any additional arithmetic is performed we must remove the - -- lower buffer period since it will be accounted as few additional - -- days. - - Modified_Date_N := Modified_Date_N - Buffer_N; - -- Step 3: Non-leap centenial year adjustment in local time zone -- In order for all divisions to work properly and to avoid more -- complicated arithmetic, we add fake Febriary 29s to dates which -- occur after a non-leap centenial year. - if Modified_Date_N >= Year_2301 then - Modified_Date_N := Modified_Date_N + Time (3) * Nanos_In_Day; + if Date_N >= Year_2301 then + Date_N := Date_N + Time_Rep (3) * Nanos_In_Day; - elsif Modified_Date_N >= Year_2201 then - Modified_Date_N := Modified_Date_N + Time (2) * Nanos_In_Day; + elsif Date_N >= Year_2201 then + Date_N := Date_N + Time_Rep (2) * Nanos_In_Day; - elsif Modified_Date_N >= Year_2101 then - Modified_Date_N := Modified_Date_N + Time (1) * Nanos_In_Day; + elsif Date_N >= Year_2101 then + Date_N := Date_N + Time_Rep (1) * Nanos_In_Day; end if; -- Step 4: Sub second processing in local time zone - Sub_Sec := Duration (Modified_Date_N mod Nano) / Nano_F; + Sub_Sec_N := Date_N mod Nano; + Sub_Sec := Duration (Sub_Sec_N) / Nano_F; + Date_N := Date_N - Sub_Sec_N; - -- Convert the date into seconds, the sub seconds are automatically - -- dropped. + -- Convert Date_N into a time duration value, changing the units + -- to seconds. - Modified_Date_S := Modified_Date_N / Nano; + Date_Dur := Time_Dur (Date_N / Nano - Ada_Low / Nano); -- Step 5: Year processing in local time zone. Determine the number -- of four year segments since the start of Ada time and the input -- date. - Four_Year_Segs := Natural (Modified_Date_S / Secs_In_Four_Years); + Four_Year_Segs := Natural (Date_Dur / Secs_In_Four_Years); if Four_Year_Segs > 0 then - Modified_Date_S := Modified_Date_S - Time (Four_Year_Segs) * - Secs_In_Four_Years; + Date_Dur := Date_Dur - Time_Dur (Four_Year_Segs) * + Secs_In_Four_Years; end if; -- Calculate the remaining non-leap years - Rem_Years := Natural (Modified_Date_S / Secs_In_Non_Leap_Year); + Rem_Years := Natural (Date_Dur / Secs_In_Non_Leap_Year); if Rem_Years > 3 then Rem_Years := 3; end if; - Modified_Date_S := Modified_Date_S - Time (Rem_Years) * - Secs_In_Non_Leap_Year; + Date_Dur := Date_Dur - Time_Dur (Rem_Years) * Secs_In_Non_Leap_Year; Year := Ada_Min_Year + Natural (4 * Four_Year_Segs + Rem_Years); Is_Leap_Year := Is_Leap (Year); -- Step 6: Month and day processing in local time zone - Year_Day := Natural (Modified_Date_S / Secs_In_Day) + 1; + Year_Day := Natural (Date_Dur / Secs_In_Day) + 1; Month := 1; @@ -1131,8 +1039,7 @@ package body Ada.Calendar is -- Processing for a new month or a leap February if Year_Day > 28 - and then (not Is_Leap_Year - or else Year_Day > 29) + and then (not Is_Leap_Year or else Year_Day > 29) then Month := 3; Year_Day := Year_Day - 28; @@ -1154,7 +1061,7 @@ package body Ada.Calendar is -- time zone. Day := Day_Number (Year_Day); - Day_Seconds := Integer (Modified_Date_S mod Secs_In_Day); + Day_Seconds := Integer (Date_Dur mod Secs_In_Day); Day_Secs := Duration (Day_Seconds) + Sub_Sec; Hour := Day_Seconds / 3_600; Hour_Seconds := Day_Seconds mod 3_600; @@ -1176,16 +1083,15 @@ package body Ada.Calendar is Second : Integer; Sub_Sec : Duration; Leap_Sec : Boolean; - Leap_Checks : Boolean; Use_Day_Secs : Boolean; + Is_Ada_05 : Boolean; Time_Zone : Long_Integer) return Time is - Abs_Time_Zone : Time; - Count : Integer; - Elapsed_Leaps : Natural; - Next_Leap_N : Time; - Result_N : Time; - Rounded_Result_N : Time; + Count : Integer; + Elapsed_Leaps : Natural; + Next_Leap_N : Time_Rep; + Res_N : Time_Rep; + Rounded_Res_N : Time_Rep; begin -- Step 1: Check whether the day, month and year form a valid date @@ -1196,37 +1102,35 @@ package body Ada.Calendar is raise Time_Error; end if; - -- Start accumulating nanoseconds from the low bound of Ada time. - -- Note: This starting point includes the lower buffer dedicated - -- to time zones. + -- Start accumulating nanoseconds from the low bound of Ada time - Result_N := Ada_Low; + Res_N := Ada_Low; -- Step 2: Year processing and centenial year adjustment. Determine -- the number of four year segments since the start of Ada time and -- the input date. - Count := (Year - Year_Number'First) / 4; - Result_N := Result_N + Time (Count) * Secs_In_Four_Years * Nano; + Count := (Year - Year_Number'First) / 4; + Res_N := Res_N + Time_Rep (Count) * Secs_In_Four_Years * Nano; -- Note that non-leap centenial years are automatically considered -- leap in the operation above. An adjustment of several days is -- required to compensate for this. if Year > 2300 then - Result_N := Result_N - Time (3) * Nanos_In_Day; + Res_N := Res_N - Time_Rep (3) * Nanos_In_Day; elsif Year > 2200 then - Result_N := Result_N - Time (2) * Nanos_In_Day; + Res_N := Res_N - Time_Rep (2) * Nanos_In_Day; elsif Year > 2100 then - Result_N := Result_N - Time (1) * Nanos_In_Day; + Res_N := Res_N - Time_Rep (1) * Nanos_In_Day; end if; -- Add the remaining non-leap years - Count := (Year - Year_Number'First) mod 4; - Result_N := Result_N + Time (Count) * Secs_In_Non_Leap_Year * Nano; + Count := (Year - Year_Number'First) mod 4; + Res_N := Res_N + Time_Rep (Count) * Secs_In_Non_Leap_Year * Nano; -- Step 3: Day of month processing. Determine the number of days -- since the start of the current year. Do not add the current @@ -1242,92 +1146,87 @@ package body Ada.Calendar is Count := Count + 1; end if; - Result_N := Result_N + Time (Count) * Nanos_In_Day; + Res_N := Res_N + Time_Rep (Count) * Nanos_In_Day; -- Step 4: Hour, minute, second and sub second processing if Use_Day_Secs then - Result_N := Result_N + To_Abs_Time (Day_Secs); + Res_N := Res_N + Duration_To_Time_Rep (Day_Secs); else - Result_N := Result_N + - Time (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 - Result_N := Result_N + Time (1) * Nano; + Res_N := Res_N + Time_Rep (1) * Nano; else - Result_N := Result_N + To_Abs_Time (Sub_Sec); + Res_N := Res_N + Duration_To_Time_Rep (Sub_Sec); end if; end if; + -- At this point, the generated time value should be withing the + -- bounds of Ada time. + + Check_Within_Time_Bounds (Res_N); + -- Step 4: Time zone processing. At this point we have built an -- arbitrary time value which is not related to any time zone. -- For simplicity, the time value is normalized to GMT, producing -- a uniform representation which can be treated by arithmetic -- operations for instance without any additional corrections. - if Result_N < Ada_Low - or else Result_N > Ada_High - then - raise Time_Error; - end if; - - if Time_Zone /= 0 then - Abs_Time_Zone := Time (abs (Time_Zone)) * 60 * Nano; - - if Time_Zone < 0 then - Result_N := Result_N + Abs_Time_Zone; - else - -- The following test is obsolete since the result already - -- contains the dedicated buffer for time zones, thus no - -- error will be raised. However it is a good idea to keep - -- this comparison should the representation of time change. + if Is_Ada_05 then + if Time_Zone /= 0 then + Res_N := Res_N - Time_Rep (Time_Zone) * 60 * Nano; + end if; - if Result_N < Abs_Time_Zone then - raise Time_Error; - end if; + -- Ada 83 and 95 - Result_N := Result_N - Abs_Time_Zone; - end if; + else + declare + Current_Off : constant Long_Integer := + Time_Zones_Operations.UTC_Time_Offset + (Time (Res_N)); + 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)); + begin + Res_N := Res_N - Time_Rep (Off) * Nano; + end; end if; -- Step 5: Leap seconds processing in GMT - Cumulative_Leap_Seconds - (Time_Zero, Result_N, Elapsed_Leaps, Next_Leap_N); - - Result_N := Result_N + Time (Elapsed_Leaps) * Nano; + if Leap_Support then + Cumulative_Leap_Seconds + (Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N); - -- An Ada 2005 caller requesting an explicit leap second or an Ada - -- 95 caller accounting for an invisible leap second. + Res_N := Res_N + Time_Rep (Elapsed_Leaps) * Nano; - Rounded_Result_N := Result_N - (Result_N mod Nano); + -- 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 Rounded_Result_N = Next_Leap_N - then - Result_N := Result_N + Time (1) * Nano; - Rounded_Result_N := Rounded_Result_N + Time (1) * Nano; - end if; + if Leap_Sec + or else Res_N >= Next_Leap_N + then + Res_N := Res_N + Time_Rep (1) * Nano; + end if; - -- Leap second validity check + -- Leap second validity check - if Leap_Checks - and then Leap_Sec - and then Rounded_Result_N /= Next_Leap_N - then - raise Time_Error; - end if; - - -- Final bounds check + Rounded_Res_N := Res_N - (Res_N mod Nano); - if Result_N < Hard_Ada_Low - or else Result_N > Hard_Ada_High_And_Leaps - then - raise Time_Error; + if Is_Ada_05 + and then Leap_Sec + and then Rounded_Res_N /= Next_Leap_N + then + raise Time_Error; + end if; end if; - return Result_N; + return Time (Res_N); end Time_Of; end Formatting_Operations; @@ -1337,35 +1236,33 @@ package body Ada.Calendar is package body Time_Zones_Operations is - -- The Unix time bounds in seconds: 1970/1/1 .. 2037/1/1 + -- The Unix time bounds in nanoseconds: 1970/1/1 .. 2037/1/1 - Unix_Min : constant Time := - Time (17 * 366 + 52 * 365 + 2) * Secs_In_Day; - -- 1970/1/1 + Unix_Min : constant Time_Rep := Ada_Low + + Time_Rep (17 * 366 + 52 * 365) * Nanos_In_Day; - Unix_Max : constant Time := - Time (34 * 366 + 102 * 365 + 2) * Secs_In_Day + - Time (Leap_Seconds_Count); - -- 2037/1/1 + 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 -- centenial years, the units are nanoseconds. - T_2100_2_28 : constant Time := - (Time (49 * 366 + 150 * 365 + 59 + 2) * Secs_In_Day + - Time (Leap_Seconds_Count)) * Nano; + 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 := - (Time (73 * 366 + 226 * 365 + 59 + 2) * Secs_In_Day + - Time (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 := - (Time (97 * 366 + 302 * 365 + 59 + 2) * Secs_In_Day + - Time (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 seconds: + -- 56 years (14 leap years + 42 non leap years) in nanoseconds: - Secs_In_56_Years : constant := (14 * 366 + 42 * 365) * Secs_In_Day; + 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. @@ -1378,17 +1275,17 @@ package body Ada.Calendar is -- 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 + 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; @@ -1411,24 +1308,22 @@ package body Ada.Calendar is --------------------- function UTC_Time_Offset (Date : Time) return Long_Integer is - - Adj_Cent : Integer := 0; - Adj_Date_N : Time; - Adj_Date_S : Time; - Offset : aliased long; - Secs_T : aliased time_t; - Secs_TM : aliased tm; + Adj_Cent : Integer := 0; + Date_N : Time_Rep; + Offset : aliased long; + Secs_T : aliased time_t; + Secs_TM : aliased tm; begin - Adj_Date_N := Date; + Date_N := Time_Rep (Date); -- Dates which are 56 years appart fall on the same day, day light -- saving and so on. Non-leap centenial years violate this rule by -- one day and as a consequence, special adjustment is needed. - if Adj_Date_N > T_2100_2_28 then - if Adj_Date_N > T_2200_2_28 then - if Adj_Date_N > T_2300_2_28 then + 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; @@ -1440,25 +1335,26 @@ package body Ada.Calendar is end if; if Adj_Cent > 0 then - Adj_Date_N := Adj_Date_N - Time (Adj_Cent) * Nanos_In_Day; + Date_N := Date_N - Time_Rep (Adj_Cent) * Nanos_In_Day; end if; - -- Convert to seconds and shift date within bounds of Unix time + -- Shift the date within bounds of Unix time - Adj_Date_S := Adj_Date_N / Nano; - while Adj_Date_S < Unix_Min loop - Adj_Date_S := Adj_Date_S + Secs_In_56_Years; + while Date_N < Unix_Min loop + Date_N := Date_N + Nanos_In_56_Years; end loop; - while Adj_Date_S >= Unix_Max loop - Adj_Date_S := Adj_Date_S - Secs_In_56_Years; + 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 - Adj_Date_S := Adj_Date_S - Unix_Min; + Date_N := Date_N - Unix_Min; + + -- Convert the date into seconds - Secs_T := time_t (Adj_Date_S); + Secs_T := time_t (Date_N / Nano); localtime_tzoff (Secs_T'Unchecked_Access, @@ -1476,67 +1372,69 @@ begin -- Population of the leap seconds table - declare - type Leap_Second_Date is record - Year : Year_Number; - Month : Month_Number; - Day : Day_Number; - end record; - - Leap_Second_Dates : - constant array (1 .. Leap_Seconds_Count) of Leap_Second_Date := - ((1972, 6, 30), (1972, 12, 31), (1973, 12, 31), (1974, 12, 31), - (1975, 12, 31), (1976, 12, 31), (1977, 12, 31), (1978, 12, 31), - (1979, 12, 31), (1981, 6, 30), (1982, 6, 30), (1983, 6, 30), - (1985, 6, 30), (1987, 12, 31), (1989, 12, 31), (1990, 12, 31), - (1992, 6, 30), (1993, 6, 30), (1994, 6, 30), (1995, 12, 31), - (1997, 6, 30), (1998, 12, 31), (2005, 12, 31)); - - Days_In_Four_Years : constant := 365 * 3 + 366; - - Days : Natural; - Leap : Leap_Second_Date; - Years : Natural; + if Leap_Support then + declare + type Leap_Second_Date is record + Year : Year_Number; + Month : Month_Number; + Day : Day_Number; + end record; + + Leap_Second_Dates : + constant array (1 .. Leap_Seconds_Count) of Leap_Second_Date := + ((1972, 6, 30), (1972, 12, 31), (1973, 12, 31), (1974, 12, 31), + (1975, 12, 31), (1976, 12, 31), (1977, 12, 31), (1978, 12, 31), + (1979, 12, 31), (1981, 6, 30), (1982, 6, 30), (1983, 6, 30), + (1985, 6, 30), (1987, 12, 31), (1989, 12, 31), (1990, 12, 31), + (1992, 6, 30), (1993, 6, 30), (1994, 6, 30), (1995, 12, 31), + (1997, 6, 30), (1998, 12, 31), (2005, 12, 31)); + + Days_In_Four_Years : constant := 365 * 3 + 366; + + Days : Natural; + Leap : Leap_Second_Date; + Years : Natural; - begin - for Index in 1 .. Leap_Seconds_Count loop - Leap := Leap_Second_Dates (Index); + begin + for Index in 1 .. Leap_Seconds_Count loop + Leap := Leap_Second_Dates (Index); - -- Calculate the number of days from the start of Ada time until - -- the current leap second occurence. Non-leap centenial years - -- are not accounted for in these calculations since there are - -- no leap seconds after 2100 yet. + -- Calculate the number of days from the start of Ada time until + -- the current leap second occurence. Non-leap centenial years + -- are not accounted for in these calculations since there are + -- no leap seconds after 2100 yet. - Years := Leap.Year - Ada_Min_Year; - Days := (Years / 4) * Days_In_Four_Years; - Years := Years mod 4; + Years := Leap.Year - Ada_Min_Year; + Days := (Years / 4) * Days_In_Four_Years; + Years := Years mod 4; - if Years = 1 then - Days := Days + 365; + if Years = 1 then + Days := Days + 365; - elsif Years = 2 then - Days := Days + 365 * 2; + elsif Years = 2 then + Days := Days + 365 * 2; - elsif Years = 3 then - Days := Days + 365 * 3; - end if; + elsif Years = 3 then + Days := Days + 365 * 3; + end if; - Days := Days + Cumulative_Days_Before_Month (Leap.Month); + Days := Days + Cumulative_Days_Before_Month (Leap.Month); - if Is_Leap (Leap.Year) - and then Leap.Month > 2 - then - Days := Days + 1; - end if; + if Is_Leap (Leap.Year) + and then Leap.Month > 2 + then + Days := Days + 1; + end if; - Days := Days + Leap.Day; + Days := Days + Leap.Day; - -- Index - 1 previous leap seconds are added to Time (Index) as - -- well as the lower buffer for time zones. + -- Index - 1 previous leap seconds are added to Time (Index) as + -- well as the lower buffer for time zones. - Leap_Second_Times (Index) := Ada_Low + - (Time (Days) * Secs_In_Day + Time (Index - 1)) * Nano; - end loop; - end; + Leap_Second_Times (Index) := Ada_Low + + (Time_Rep (Days) * Secs_In_Day + Time_Rep (Index - 1)) * Nano; + end loop; + end; + end if; end Ada.Calendar; |