Commit 984d7dd3 by Hristian Kirtchev Committed by Arnaud Charlet

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.

From-SVN: r125363
parent f936abf3
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
-- -- -- --
-- B o d y -- -- 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 -- -- 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- -- -- terms of the GNU General Public License as published by the Free Soft- --
...@@ -46,12 +46,40 @@ package body Ada.Calendar is ...@@ -46,12 +46,40 @@ package body Ada.Calendar is
-- Variables of type Ada.Calendar.Time have suffix _S or _M to denote -- Variables of type Ada.Calendar.Time have suffix _S or _M to denote
-- units of seconds or milis. -- units of seconds or milis.
-- 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
-- dependent machinery in a single package, thus providing a uniform
-- interface to all existing and any potential children.
-- package Ada.Calendar
-- procedure Split (5 parameters) -------+
-- | Call from local routine
-- private |
-- package Formatting_Operations |
-- procedure Split (11 parameters) <--+
-- end Formatting_Operations |
-- end Ada.Calendar |
-- |
-- package Ada.Calendar.Formatting | Call from child routine
-- procedure Split (9 or 10 parameters) -+
-- end Ada.Calendar.Formatting
-- The behaviour of the interfacing routines is controlled via various
-- flags. All new Ada 2005 types from children of Ada.Calendar are
-- emulated by a similar type. For instance, type Day_Number is replaced
-- by Integer in various routines. One ramification of this model is that
-- the caller site must perform validity checks on returned results.
-- The end result of this model is the lack of target specific files per
-- child of Ada.Calendar (a-calfor, a-calfor-vms, a-calfor-vxwors, etc).
----------------------- -----------------------
-- Local Subprograms -- -- Local Subprograms --
----------------------- -----------------------
function All_Leap_Seconds return Natural; procedure Check_Within_Time_Bounds (T : Time);
-- Return the number of all leap seconds allocated so far -- Ensure that a time representation value falls withing the bounds of Ada
-- time. Leap seconds support is taken into account.
procedure Cumulative_Leap_Seconds procedure Cumulative_Leap_Seconds
(Start_Date : Time; (Start_Date : Time;
...@@ -60,10 +88,10 @@ package body Ada.Calendar is ...@@ -60,10 +88,10 @@ package body Ada.Calendar is
Next_Leap_Sec : out Time); Next_Leap_Sec : out Time);
-- Elapsed_Leaps is the sum of the leap seconds that have occured on or -- 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 -- 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 -- represents the next leap second occurence on or after End_Date. If
-- are no leaps seconds after End_Date, After_Last_Leap is returned. -- there are no leaps seconds after End_Date, End_Of_Time is returned.
-- After_Last_Leap can be used as End_Date to count all the leap seconds -- End_Of_Time can be used as End_Date to count all the leap seconds that
-- that have occured on or after Start_Date. -- have occured on or after Start_Date.
-- --
-- Note: Any sub seconds of Start_Date and End_Date are discarded before -- 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 -- the calculations are done. For instance: if 113 seconds is a leap
...@@ -88,14 +116,36 @@ package body Ada.Calendar is ...@@ -88,14 +116,36 @@ package body Ada.Calendar is
-- Local Constants -- -- Local Constants --
--------------------- ---------------------
After_Last_Leap : constant Time := Time'Last; -- Currently none of the GNAT targets support leap seconds. At some point
N_Leap_Seconds : constant Natural := 23; -- 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;
-- The range of Ada time expressed as milis since the VMS Epoch
Ada_Low : constant Time := (10 * 366 + 32 * 365 + 45) * Milis_In_Day;
Ada_High : constant Time := (131 * 366 + 410 * 365 + 45) * Milis_In_Day;
-- Even though the upper bound of time is 2399-12-31 23:59:59.9999999
-- UTC, it must be increased to include all leap seconds.
Ada_High_And_Leaps : constant Time :=
Ada_High + Time (Leap_Seconds_Count) * Mili;
-- 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 := Ada_High + Time (3) * Milis_In_Day;
Start_Of_Time : constant Time := Ada_Low - Time (3) * Milis_In_Day;
Cumulative_Days_Before_Month : Cumulative_Days_Before_Month :
constant array (Month_Number) of Natural := constant array (Month_Number) of Natural :=
(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334); (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334);
Leap_Second_Times : array (1 .. N_Leap_Seconds) of Time; Leap_Second_Times : array (1 .. Leap_Seconds_Count) of Time;
-- Each value represents a time value which is one second before a leap -- Each value represents a time value which is one second before a leap
-- second occurence. This table is populated during the elaboration of -- second occurence. This table is populated during the elaboration of
-- Ada.Calendar. -- Ada.Calendar.
...@@ -107,18 +157,21 @@ package body Ada.Calendar is ...@@ -107,18 +157,21 @@ package body Ada.Calendar is
function "+" (Left : Time; Right : Duration) return Time is function "+" (Left : Time; Right : Duration) return Time is
pragma Unsuppress (Overflow_Check); pragma Unsuppress (Overflow_Check);
Ada_High_And_Leaps : constant Time := Res_M : Time;
Ada_High + Time (All_Leap_Seconds) * Mili;
Result : constant Time := Left + To_Relative_Time (Right);
begin begin
if Result < Ada_Low -- Trivial case
or else Result >= Ada_High_And_Leaps
then if Right = Duration (0.0) then
raise Time_Error; return Left;
end if; end if;
return Result; Res_M := Left + To_Relative_Time (Right);
Check_Within_Time_Bounds (Res_M);
return Res_M;
exception exception
when Constraint_Error => when Constraint_Error =>
raise Time_Error; raise Time_Error;
...@@ -140,18 +193,20 @@ package body Ada.Calendar is ...@@ -140,18 +193,20 @@ package body Ada.Calendar is
function "-" (Left : Time; Right : Duration) return Time is function "-" (Left : Time; Right : Duration) return Time is
pragma Unsuppress (Overflow_Check); pragma Unsuppress (Overflow_Check);
Ada_High_And_Leaps : constant Time := Res_M : Time;
Ada_High + Time (All_Leap_Seconds) * Mili;
Result : constant Time := Left - To_Relative_Time (Right);
begin begin
if Result < Ada_Low -- Trivial case
or else Result >= Ada_High_And_Leaps
then if Right = Duration (0.0) then
raise Time_Error; return Left;
end if; end if;
return Result; Res_M := Left - To_Relative_Time (Right);
Check_Within_Time_Bounds (Res_M);
return Res_M;
exception exception
when Constraint_Error => when Constraint_Error =>
...@@ -161,19 +216,25 @@ package body Ada.Calendar is ...@@ -161,19 +216,25 @@ package body Ada.Calendar is
function "-" (Left : Time; Right : Time) return Duration is function "-" (Left : Time; Right : Time) return Duration is
pragma Unsuppress (Overflow_Check); pragma Unsuppress (Overflow_Check);
Diff : constant Time := Left - Right; -- The bound of type Duration expressed as time
Dur_High : constant Time := Time (Duration'Last) * 100;
Dur_Low : constant Time := Time (Duration'First) * 100; Dur_High : constant Time := To_Relative_Time (Duration'Last);
Dur_Low : constant Time := To_Relative_Time (Duration'First);
Res_M : Time;
begin begin
if Diff < Dur_Low Res_M := Left - Right;
or else Diff > Dur_High
-- The result does not fit in a duration value
if Res_M < Dur_Low
or else Res_M >= Dur_High
then then
raise Time_Error; raise Time_Error;
end if; end if;
return To_Duration (Diff); return To_Duration (Res_M);
exception exception
when Constraint_Error => when Constraint_Error =>
raise Time_Error; raise Time_Error;
...@@ -215,14 +276,22 @@ package body Ada.Calendar is ...@@ -215,14 +276,22 @@ package body Ada.Calendar is
return Long_Integer (Left) >= Long_Integer (Right); return Long_Integer (Left) >= Long_Integer (Right);
end ">="; end ">=";
---------------------- ------------------------------
-- All_Leap_Seconds -- -- Check_Within_Time_Bounds --
---------------------- ------------------------------
function All_Leap_Seconds return Natural is procedure Check_Within_Time_Bounds (T : Time) is
begin begin
return N_Leap_Seconds; if Leap_Support then
end All_Leap_Seconds; 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 -- -- Clock --
...@@ -230,9 +299,8 @@ package body Ada.Calendar is ...@@ -230,9 +299,8 @@ package body Ada.Calendar is
function Clock return Time is function Clock return Time is
Elapsed_Leaps : Natural; Elapsed_Leaps : Natural;
Next_Leap : Time; Next_Leap_M : Time;
Now : constant Time := Time (OSP.OS_Clock); Res_M : constant Time := Time (OSP.OS_Clock);
Rounded_Now : constant Time := Now - (Now mod Mili);
begin begin
-- Note that on other targets a soft-link is used to get a different -- Note that on other targets a soft-link is used to get a different
...@@ -240,17 +308,26 @@ package body Ada.Calendar is ...@@ -240,17 +308,26 @@ package body Ada.Calendar is
-- needed since all clock calls end up using SYS$GETTIM, so call the -- needed since all clock calls end up using SYS$GETTIM, so call the
-- OS_Primitives version for efficiency. -- OS_Primitives version for efficiency.
-- Determine the number of leap seconds 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_M, Elapsed_Leaps, Next_Leap_M);
-- The system clock may fall exactly on a leap second
Cumulative_Leap_Seconds (Ada_Low, Now, Elapsed_Leaps, Next_Leap); if Res_M >= Next_Leap_M then
Elapsed_Leaps := Elapsed_Leaps + 1;
end if;
-- It is possible that OS_Clock falls exactly on a leap second -- The target does not support leap seconds
if Rounded_Now = Next_Leap then
return Now + Time (Elapsed_Leaps + 1) * Mili;
else else
return Now + Time (Elapsed_Leaps) * Mili; Elapsed_Leaps := 0;
end if; end if;
return Res_M + Time (Elapsed_Leaps) * Mili;
end Clock; end Clock;
----------------------------- -----------------------------
...@@ -269,9 +346,9 @@ package body Ada.Calendar is ...@@ -269,9 +346,9 @@ package body Ada.Calendar is
Start_T : Time := Start_Date; Start_T : Time := Start_Date;
begin begin
pragma Assert (Start_Date >= End_Date); pragma Assert (Leap_Support and then End_Date >= Start_Date);
Next_Leap_Sec := After_Last_Leap; Next_Leap_Sec := End_Of_Time;
-- Make sure that the end date does not excede the upper bound -- Make sure that the end date does not excede the upper bound
-- of Ada time. -- of Ada time.
...@@ -285,23 +362,26 @@ package body Ada.Calendar is ...@@ -285,23 +362,26 @@ package body Ada.Calendar is
Start_T := Start_T - (Start_T mod Mili); Start_T := Start_T - (Start_T mod Mili);
End_T := End_T - (End_T mod Mili); End_T := End_T - (End_T mod Mili);
-- Some trivial cases -- Some trivial cases:
-- Leap 1 . . . Leap N
-- ---+========+------+############+-------+========+-----
-- Start_T End_T Start_T End_T
if End_T < Leap_Second_Times (1) then if End_T < Leap_Second_Times (1) then
Elapsed_Leaps := 0; Elapsed_Leaps := 0;
Next_Leap_Sec := Leap_Second_Times (1); Next_Leap_Sec := Leap_Second_Times (1);
return; return;
elsif Start_T > Leap_Second_Times (N_Leap_Seconds) then elsif Start_T > Leap_Second_Times (Leap_Seconds_Count) then
Elapsed_Leaps := 0; Elapsed_Leaps := 0;
Next_Leap_Sec := After_Last_Leap; Next_Leap_Sec := End_Of_Time;
return; return;
end if; end if;
-- Perform the calculations only if the start date is within the leap -- Perform the calculations only if the start date is within the leap
-- second occurences table. -- second occurences table.
if Start_T <= Leap_Second_Times (N_Leap_Seconds) then if Start_T <= Leap_Second_Times (Leap_Seconds_Count) then
-- 1 2 N - 1 N -- 1 2 N - 1 N
-- +----+----+-- . . . --+-------+---+ -- +----+----+-- . . . --+-------+---+
...@@ -313,8 +393,8 @@ package body Ada.Calendar is ...@@ -313,8 +393,8 @@ package body Ada.Calendar is
-- Leaps_Between -- Leaps_Between
-- The idea behind the algorithm is to iterate and find two closest -- The idea behind the algorithm is to iterate and find two closest
-- dates which are after Start_T and End_T. Their corresponding index -- dates which are after Start_T and End_T. Their corresponding
-- difference denotes the number of leap seconds elapsed. -- index difference denotes the number of leap seconds elapsed.
Start_Index := 1; Start_Index := 1;
loop loop
...@@ -324,12 +404,12 @@ package body Ada.Calendar is ...@@ -324,12 +404,12 @@ package body Ada.Calendar is
End_Index := Start_Index; End_Index := Start_Index;
loop loop
exit when End_Index > N_Leap_Seconds exit when End_Index > Leap_Seconds_Count
or else Leap_Second_Times (End_Index) >= End_T; or else Leap_Second_Times (End_Index) >= End_T;
End_Index := End_Index + 1; End_Index := End_Index + 1;
end loop; end loop;
if End_Index <= N_Leap_Seconds then if End_Index <= Leap_Seconds_Count then
Next_Leap_Sec := Leap_Second_Times (End_Index); Next_Leap_Sec := Leap_Second_Times (End_Index);
end if; end if;
...@@ -423,8 +503,22 @@ package body Ada.Calendar is ...@@ -423,8 +503,22 @@ package body Ada.Calendar is
Le : Boolean; Le : Boolean;
begin begin
-- Use UTC as the local time zone on VMS, the status of flag Is_Ada_05
-- is irrelevant in this case.
Formatting_Operations.Split Formatting_Operations.Split
(Date, Year, Month, Day, Seconds, H, M, Se, Ss, Le, 0); (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 -- Validity checks
...@@ -465,12 +559,22 @@ package body Ada.Calendar is ...@@ -465,12 +559,22 @@ package body Ada.Calendar is
raise Time_Error; raise Time_Error;
end if; end if;
-- Use UTC as the local time zone on VMS, the status of flag Is_Ada_05
-- is irrelevant in this case.
return return
Formatting_Operations.Time_Of Formatting_Operations.Time_Of
(Year, Month, Day, Seconds, H, M, Se, Ss, (Year => Year,
Month => Month,
Day => Day,
Day_Secs => Seconds,
Hour => H,
Minute => M,
Second => Se,
Sub_Sec => Ss,
Leap_Sec => False, Leap_Sec => False,
Leap_Checks => False,
Use_Day_Secs => True, Use_Day_Secs => True,
Is_Ada_05 => False,
Time_Zone => 0); Time_Zone => 0);
end Time_Of; end Time_Of;
...@@ -524,29 +628,22 @@ package body Ada.Calendar is ...@@ -524,29 +628,22 @@ package body Ada.Calendar is
--------- ---------
function Add (Date : Time; Days : Long_Integer) return Time is function Add (Date : Time; Days : Long_Integer) return Time is
Ada_High_And_Leaps : constant Time := pragma Unsuppress (Overflow_Check);
Ada_High + Time (All_Leap_Seconds) * Mili;
Res_M : Time;
begin begin
-- Trivial case
if Days = 0 then if Days = 0 then
return Date; return Date;
end if;
elsif Days < 0 then Res_M := Date + Time (Days) * Milis_In_Day;
return Subtract (Date, abs (Days));
else Check_Within_Time_Bounds (Res_M);
declare
Result : constant Time := Date + Time (Days) * Milis_In_Day;
begin return Res_M;
-- The result excedes the upper bound of Ada time
if Result >= Ada_High_And_Leaps then
raise Time_Error;
end if;
return Result;
end;
end if;
exception exception
when Constraint_Error => when Constraint_Error =>
...@@ -571,7 +668,7 @@ package body Ada.Calendar is ...@@ -571,7 +668,7 @@ package body Ada.Calendar is
Earlier : Time; Earlier : Time;
Elapsed_Leaps : Natural; Elapsed_Leaps : Natural;
Later : Time; Later : Time;
Negate : Boolean; Negate : Boolean := False;
Next_Leap : Time; Next_Leap : Time;
Sub_Seconds : Duration; Sub_Seconds : Duration;
...@@ -582,19 +679,26 @@ package body Ada.Calendar is ...@@ -582,19 +679,26 @@ package body Ada.Calendar is
if Left >= Right then if Left >= Right then
Later := Left; Later := Left;
Earlier := Right; Earlier := Right;
Negate := False;
else else
Later := Right; Later := Right;
Earlier := Left; Earlier := Left;
Negate := True; Negate := True;
end if; 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);
if Later >= Next_Leap then if Later >= Next_Leap then
Elapsed_Leaps := Elapsed_Leaps + 1; Elapsed_Leaps := Elapsed_Leaps + 1;
end if;
-- The target does not support leap seconds
else
Elapsed_Leaps := 0;
end if; end if;
Diff_M := Later - Earlier - Time (Elapsed_Leaps) * Mili; Diff_M := Later - Earlier - Time (Elapsed_Leaps) * Mili;
...@@ -613,9 +717,12 @@ package body Ada.Calendar is ...@@ -613,9 +717,12 @@ package body Ada.Calendar is
Leap_Seconds := Integer (Elapsed_Leaps); Leap_Seconds := Integer (Elapsed_Leaps);
if Negate then if Negate then
Days := -Days; Days := -Days;
Seconds := -Seconds; Seconds := -Seconds;
Leap_Seconds := -Leap_Seconds;
if Leap_Seconds /= 0 then
Leap_Seconds := -Leap_Seconds;
end if;
end if; end if;
end Difference; end Difference;
...@@ -624,32 +731,22 @@ package body Ada.Calendar is ...@@ -624,32 +731,22 @@ package body Ada.Calendar is
-------------- --------------
function Subtract (Date : Time; Days : Long_Integer) return Time is function Subtract (Date : Time; Days : Long_Integer) return Time is
pragma Unsuppress (Overflow_Check);
Res_M : Time;
begin begin
-- Trivial case
if Days = 0 then if Days = 0 then
return Date; return Date;
end if;
elsif Days < 0 then Res_M := Date - Time (Days) * Milis_In_Day;
return Add (Date, abs (Days));
else Check_Within_Time_Bounds (Res_M);
declare
Days_T : constant Time := Time (Days) * Milis_In_Day;
Result : constant Time := Date - Days_T;
begin
-- Subtracting a larger number of days from a smaller time
-- value will cause wrap around since time is a modular type.
-- Also the result may be lower than the start of Ada time.
if Date < Days_T
or Result < Ada_Low
then
raise Time_Error;
end if;
return Date - Days_T; return Res_M;
end;
end if;
exception exception
when Constraint_Error => when Constraint_Error =>
raise Time_Error; raise Time_Error;
...@@ -696,18 +793,23 @@ package body Ada.Calendar is ...@@ -696,18 +793,23 @@ package body Ada.Calendar is
----------- -----------
procedure Split procedure Split
(Date : Time; (Date : Time;
Year : out Year_Number; Year : out Year_Number;
Month : out Month_Number; Month : out Month_Number;
Day : out Day_Number; Day : out Day_Number;
Day_Secs : out Day_Duration; Day_Secs : out Day_Duration;
Hour : out Integer; Hour : out Integer;
Minute : out Integer; Minute : out Integer;
Second : out Integer; Second : out Integer;
Sub_Sec : out Duration; Sub_Sec : out Duration;
Leap_Sec : out Boolean; Leap_Sec : out Boolean;
Time_Zone : Long_Integer) Is_Ada_05 : Boolean;
Time_Zone : Long_Integer)
is is
-- The flag Is_Ada_05 is present for interfacing purposes
pragma Unreferenced (Is_Ada_05);
procedure Numtim procedure Numtim
(Status : out Unsigned_Longword; (Status : out Unsigned_Longword;
Timbuf : out Unsigned_Word_Array; Timbuf : out Unsigned_Word_Array;
...@@ -727,59 +829,60 @@ package body Ada.Calendar is ...@@ -727,59 +829,60 @@ package body Ada.Calendar is
Ada_Max_Year : constant := 2399; Ada_Max_Year : constant := 2399;
Mili_F : constant Duration := 10_000_000.0; Mili_F : constant Duration := 10_000_000.0;
Abs_Time_Zone : Time; Date_M : Time;
Elapsed_Leaps : Natural; Elapsed_Leaps : Natural;
Modified_Date_M : Time; Next_Leap_M : Time;
Next_Leap_M : Time;
Rounded_Date_M : Time;
begin begin
Modified_Date_M := Date; Date_M := Date;
-- Step 1: Leap seconds processing -- Step 1: Leap seconds processing
Cumulative_Leap_Seconds (Ada_Low, Date, Elapsed_Leaps, Next_Leap_M); if Leap_Support then
Cumulative_Leap_Seconds
(Start_Of_Time, Date, Elapsed_Leaps, Next_Leap_M);
Leap_Sec := Date_M >= Next_Leap_M;
if Leap_Sec then
Elapsed_Leaps := Elapsed_Leaps + 1;
end if;
Rounded_Date_M := Modified_Date_M - (Modified_Date_M mod Mili); -- The target does not support leap seconds
Leap_Sec := Rounded_Date_M = Next_Leap_M;
Modified_Date_M := Modified_Date_M - Time (Elapsed_Leaps) * Mili;
if Leap_Sec then else
Modified_Date_M := Modified_Date_M - Time (1) * Mili; Elapsed_Leaps := 0;
Leap_Sec := False;
end if; end if;
Date_M := Date_M - Time (Elapsed_Leaps) * Mili;
-- Step 2: Time zone processing -- Step 2: Time zone processing
if Time_Zone /= 0 then if Time_Zone /= 0 then
Abs_Time_Zone := Time (abs (Time_Zone)) * 60 * Mili; Date_M := Date_M + Time (Time_Zone) * 60 * Mili;
if Time_Zone < 0 then
Modified_Date_M := Modified_Date_M - Abs_Time_Zone;
else
Modified_Date_M := Modified_Date_M + Abs_Time_Zone;
end if;
end if; end if;
-- After the leap seconds and time zone have been accounted for, -- After the leap seconds and time zone have been accounted for,
-- the date should be within the bounds of Ada time. -- the date should be within the bounds of Ada time.
if Modified_Date_M < Ada_Low if Date_M < Ada_Low
or else Modified_Date_M >= Ada_High or else Date_M > Ada_High
then then
raise Time_Error; raise Time_Error;
end if; end if;
-- Step 3: Sub second processing -- Step 3: Sub second processing
Sub_Sec := Duration (Modified_Date_M mod Mili) / Mili_F; Sub_Sec := Duration (Date_M mod Mili) / Mili_F;
-- Drop the sub seconds -- Drop the sub seconds
Modified_Date_M := Modified_Date_M - (Modified_Date_M mod Mili); Date_M := Date_M - (Date_M mod Mili);
-- Step 4: VMS system call -- Step 4: VMS system call
Numtim (Status, Timbuf, Modified_Date_M); Numtim (Status, Timbuf, Date_M);
if Status mod 2 /= 1 if Status mod 2 /= 1
or else Timbuf (1) not in Ada_Min_Year .. Ada_Max_Year or else Timbuf (1) not in Ada_Min_Year .. Ada_Max_Year
...@@ -816,8 +919,8 @@ package body Ada.Calendar is ...@@ -816,8 +919,8 @@ package body Ada.Calendar is
Second : Integer; Second : Integer;
Sub_Sec : Duration; Sub_Sec : Duration;
Leap_Sec : Boolean; Leap_Sec : Boolean;
Leap_Checks : Boolean;
Use_Day_Secs : Boolean; Use_Day_Secs : Boolean;
Is_Ada_05 : Boolean;
Time_Zone : Long_Integer) return Time Time_Zone : Long_Integer) return Time
is is
procedure Cvt_Vectim procedure Cvt_Vectim
...@@ -837,21 +940,19 @@ package body Ada.Calendar is ...@@ -837,21 +940,19 @@ package body Ada.Calendar is
Mili_F : constant := 10_000_000.0; Mili_F : constant := 10_000_000.0;
Ada_High_And_Leaps : constant Time := Y : Year_Number := Year;
Ada_High + Time (All_Leap_Seconds) * Mili; Mo : Month_Number := Month;
D : Day_Number := Day;
H : Integer := Hour; H : Integer := Hour;
Mi : Integer := Minute; Mi : Integer := Minute;
Se : Integer := Second; Se : Integer := Second;
Su : Duration := Sub_Sec; Su : Duration := Sub_Sec;
Abs_Time_Zone : Time; Elapsed_Leaps : Natural;
Adjust_Day : Boolean := False; Int_Day_Secs : Integer;
Elapsed_Leaps : Natural; Next_Leap_M : Time;
Int_Day_Secs : Integer; Res_M : Time;
Next_Leap_M : Time; Rounded_Res_M : Time;
Result_M : Time;
Rounded_Result_M : Time;
begin begin
-- No validity checks are performed on the input values since it is -- No validity checks are performed on the input values since it is
...@@ -861,15 +962,47 @@ package body Ada.Calendar is ...@@ -861,15 +962,47 @@ package body Ada.Calendar is
if Use_Day_Secs then if Use_Day_Secs then
-- A day seconds value of 86_400 designates a new day. The time -- A day seconds value of 86_400 designates a new day
-- components are reset to zero, but an additional day will be
-- added after the system call.
if Day_Secs = 86_400.0 then if Day_Secs = 86_400.0 then
Adjust_Day := True; declare
H := 0; Adj_Year : Year_Number := Year;
Mi := 0; Adj_Month : Month_Number := Month;
Se := 0; Adj_Day : Day_Number := Day;
begin
if Day < Days_In_Month (Month)
or else (Month = 2
and then Is_Leap (Year))
then
Adj_Day := Day + 1;
-- The day adjustment moves the date to a new month
else
Adj_Day := 1;
if Month < 12 then
Adj_Month := Month + 1;
-- The month adjustment moves the date to a new year
else
Adj_Month := 1;
Adj_Year := Year + 1;
end if;
end if;
Y := Adj_Year;
Mo := Adj_Month;
D := Adj_Day;
H := 0;
Mi := 0;
Se := 0;
Su := 0.0;
end;
-- Normal case (not exactly one day)
else else
-- Sub second extraction -- Sub second extraction
...@@ -889,81 +1022,64 @@ package body Ada.Calendar is ...@@ -889,81 +1022,64 @@ package body Ada.Calendar is
-- Step 2: System call to VMS -- Step 2: System call to VMS
Timbuf (1) := Unsigned_Word (Year); Timbuf (1) := Unsigned_Word (Y);
Timbuf (2) := Unsigned_Word (Month); Timbuf (2) := Unsigned_Word (Mo);
Timbuf (3) := Unsigned_Word (Day); Timbuf (3) := Unsigned_Word (D);
Timbuf (4) := Unsigned_Word (H); Timbuf (4) := Unsigned_Word (H);
Timbuf (5) := Unsigned_Word (Mi); Timbuf (5) := Unsigned_Word (Mi);
Timbuf (6) := Unsigned_Word (Se); Timbuf (6) := Unsigned_Word (Se);
Timbuf (7) := 0; Timbuf (7) := 0;
Cvt_Vectim (Status, Timbuf, Result_M); Cvt_Vectim (Status, Timbuf, Res_M);
if Status mod 2 /= 1 then if Status mod 2 /= 1 then
raise Time_Error; raise Time_Error;
end if; end if;
-- Step 3: Potential day adjustment -- Step 3: Sub second adjustment
if Use_Day_Secs Res_M := Res_M + Time (Su * Mili_F);
and then Adjust_Day
then
Result_M := Result_M + Milis_In_Day;
end if;
-- Step 4: Sub second adjustment -- Step 4: Bounds check
Result_M := Result_M + Time (Su * Mili_F); Check_Within_Time_Bounds (Res_M);
-- Step 5: Time zone processing -- Step 5: Time zone processing
if Time_Zone /= 0 then if Time_Zone /= 0 then
Abs_Time_Zone := Time (abs (Time_Zone)) * 60 * Mili; Res_M := Res_M - Time (Time_Zone) * 60 * Mili;
if Time_Zone < 0 then
Result_M := Result_M + Abs_Time_Zone;
else
Result_M := Result_M - Abs_Time_Zone;
end if;
end if; end if;
-- Step 6: Leap seconds processing -- Step 6: Leap seconds processing
Cumulative_Leap_Seconds if Leap_Support then
(Ada_Low, Result_M, Elapsed_Leaps, Next_Leap_M); Cumulative_Leap_Seconds
(Start_Of_Time, Res_M, Elapsed_Leaps, Next_Leap_M);
Result_M := Result_M + Time (Elapsed_Leaps) * Mili; Res_M := Res_M + Time (Elapsed_Leaps) * Mili;
-- An Ada 2005 caller requesting an explicit leap second or an Ada -- An Ada 2005 caller requesting an explicit leap second or an
-- 95 caller accounting for an invisible leap second. -- Ada 95 caller accounting for an invisible leap second.
Rounded_Result_M := Result_M - (Result_M mod Mili); if Leap_Sec
or else Res_M >= Next_Leap_M
then
Res_M := Res_M + Time (1) * Mili;
end if;
if Leap_Sec -- Leap second validity check
or else Rounded_Result_M = Next_Leap_M
then
Result_M := Result_M + Time (1) * Mili;
Rounded_Result_M := Rounded_Result_M + Time (1) * Mili;
end if;
-- Leap second validity check Rounded_Res_M := Res_M - (Res_M mod Mili);
if Leap_Checks if Is_Ada_05
and then Leap_Sec and then Leap_Sec
and then Rounded_Result_M /= Next_Leap_M and then Rounded_Res_M /= Next_Leap_M
then then
raise Time_Error; raise Time_Error;
end if; end if;
-- Bounds check
if Result_M < Ada_Low
or else Result_M >= Ada_High_And_Leaps
then
raise Time_Error;
end if; end if;
return Result_M; return Res_M;
end Time_Of; end Time_Of;
end Formatting_Operations; end Formatting_Operations;
...@@ -1000,71 +1116,73 @@ package body Ada.Calendar is ...@@ -1000,71 +1116,73 @@ package body Ada.Calendar is
begin begin
-- Population of the leap seconds table -- Population of the leap seconds table
declare if Leap_Support then
type Leap_Second_Date is record declare
Year : Year_Number; type Leap_Second_Date is record
Month : Month_Number; Year : Year_Number;
Day : Day_Number; Month : Month_Number;
end record; Day : Day_Number;
end record;
Leap_Second_Dates :
constant array (1 .. N_Leap_Seconds) of Leap_Second_Date := Leap_Second_Dates :
((1972, 6, 30), (1972, 12, 31), (1973, 12, 31), (1974, 12, 31), constant array (1 .. Leap_Seconds_Count) of Leap_Second_Date :=
(1975, 12, 31), (1976, 12, 31), (1977, 12, 31), (1978, 12, 31), ((1972, 6, 30), (1972, 12, 31), (1973, 12, 31), (1974, 12, 31),
(1979, 12, 31), (1981, 6, 30), (1982, 6, 30), (1983, 6, 30), (1975, 12, 31), (1976, 12, 31), (1977, 12, 31), (1978, 12, 31),
(1985, 6, 30), (1987, 12, 31), (1989, 12, 31), (1990, 12, 31), (1979, 12, 31), (1981, 6, 30), (1982, 6, 30), (1983, 6, 30),
(1992, 6, 30), (1993, 6, 30), (1994, 6, 30), (1995, 12, 31), (1985, 6, 30), (1987, 12, 31), (1989, 12, 31), (1990, 12, 31),
(1997, 6, 30), (1998, 12, 31), (2005, 12, 31)); (1992, 6, 30), (1993, 6, 30), (1994, 6, 30), (1995, 12, 31),
(1997, 6, 30), (1998, 12, 31), (2005, 12, 31));
Ada_Min_Year : constant Year_Number := Year_Number'First;
Days_In_Four_Years : constant := 365 * 3 + 366; Ada_Min_Year : constant Year_Number := Year_Number'First;
VMS_Days : constant := 10 * 366 + 32 * 365 + 45; Days_In_Four_Years : constant := 365 * 3 + 366;
VMS_Days : constant := 10 * 366 + 32 * 365 + 45;
Days : Natural;
Leap : Leap_Second_Date; Days : Natural;
Years : Natural; Leap : Leap_Second_Date;
Years : Natural;
begin begin
for Index in 1 .. N_Leap_Seconds loop for Index in 1 .. Leap_Seconds_Count loop
Leap := Leap_Second_Dates (Index); Leap := Leap_Second_Dates (Index);
-- Calculate the number of days from the start of Ada time until -- Calculate the number of days from the start of Ada time until
-- the current leap second occurence. Non-leap centenial years -- the current leap second occurence. Non-leap centenial years
-- are not accounted for in these calculations since there are -- are not accounted for in these calculations since there are
-- no leap seconds after 2100 yet. -- no leap seconds after 2100 yet.
Years := Leap.Year - Ada_Min_Year; Years := Leap.Year - Ada_Min_Year;
Days := (Years / 4) * Days_In_Four_Years; Days := (Years / 4) * Days_In_Four_Years;
Years := Years mod 4; Years := Years mod 4;
if Years = 1 then if Years = 1 then
Days := Days + 365; Days := Days + 365;
elsif Years = 2 then elsif Years = 2 then
Days := Days + 365 * 2; Days := Days + 365 * 2;
elsif Years = 3 then elsif Years = 3 then
Days := Days + 365 * 3; Days := Days + 365 * 3;
end if; end if;
Days := Days + Cumulative_Days_Before_Month (Leap.Month); Days := Days + Cumulative_Days_Before_Month (Leap.Month);
if Is_Leap (Leap.Year) if Is_Leap (Leap.Year)
and then Leap.Month > 2 and then Leap.Month > 2
then then
Days := Days + 1; Days := Days + 1;
end if; end if;
-- Add the number of days since the start of VMS time till the -- Add the number of days since the start of VMS time till the
-- start of Ada time. -- start of Ada time.
Days := Days + Leap.Day + VMS_Days; Days := Days + Leap.Day + VMS_Days;
-- Index - 1 previous leap seconds are added to Time (Index) -- Index - 1 previous leap seconds are added to Time (Index)
Leap_Second_Times (Index) := Leap_Second_Times (Index) :=
(Time (Days) * Secs_In_Day + Time (Index - 1)) * Mili; (Time (Days) * Secs_In_Day + Time (Index - 1)) * Mili;
end loop; end loop;
end; end;
end if;
end Ada.Calendar; end Ada.Calendar;
...@@ -113,7 +113,7 @@ private ...@@ -113,7 +113,7 @@ private
-- system base date and time 1858-11-17 0.0 (the Smithsonian base date and -- system base date and time 1858-11-17 0.0 (the Smithsonian base date and
-- time for the astronomic calendar). -- time for the astronomic calendar).
-- The time value stored is typically a GMT value, as provided in standard -- The time value stored is typically a UTC value, as provided in standard
-- Unix environments. If this is the case then Split and Time_Of perform -- Unix environments. If this is the case then Split and Time_Of perform
-- required conversions to and from local times. -- required conversions to and from local times.
...@@ -123,11 +123,6 @@ private ...@@ -123,11 +123,6 @@ private
type Time is new OSP.OS_Time; type Time is new OSP.OS_Time;
-- The range of Ada time expressed as milis since the VMS Epoch
Ada_Low : constant Time := (10 * 366 + 32 * 365 + 45) * Milis_In_Day;
Ada_High : constant Time := (131 * 366 + 410 * 365 + 45) * Milis_In_Day;
Days_In_Month : constant array (Month_Number) of Day_Number := Days_In_Month : constant array (Month_Number) of Day_Number :=
(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
...@@ -145,7 +140,7 @@ private ...@@ -145,7 +140,7 @@ private
package Arithmetic_Operations is package Arithmetic_Operations is
function Add (Date : Time; Days : Long_Integer) return Time; function Add (Date : Time; Days : Long_Integer) return Time;
-- Add X number of days to a time value -- Add a certain number of days to a time value
procedure Difference procedure Difference
(Left : Time; (Left : Time;
...@@ -159,7 +154,7 @@ private ...@@ -159,7 +154,7 @@ private
-- values are positive, negative otherwise. -- values are positive, negative otherwise.
function Subtract (Date : Time; Days : Long_Integer) return Time; function Subtract (Date : Time; Days : Long_Integer) return Time;
-- Subtract X number of days from a time value -- Subtract a certain number of days from a time value
end Arithmetic_Operations; end Arithmetic_Operations;
package Formatting_Operations is package Formatting_Operations is
...@@ -168,18 +163,21 @@ private ...@@ -168,18 +163,21 @@ private
-- within the range of 0 .. 6 (Monday .. Sunday). -- within the range of 0 .. 6 (Monday .. Sunday).
procedure Split procedure Split
(Date : Time; (Date : Time;
Year : out Year_Number; Year : out Year_Number;
Month : out Month_Number; Month : out Month_Number;
Day : out Day_Number; Day : out Day_Number;
Day_Secs : out Day_Duration; Day_Secs : out Day_Duration;
Hour : out Integer; Hour : out Integer;
Minute : out Integer; Minute : out Integer;
Second : out Integer; Second : out Integer;
Sub_Sec : out Duration; Sub_Sec : out Duration;
Leap_Sec : out Boolean; Leap_Sec : out Boolean;
Time_Zone : Long_Integer); Is_Ada_05 : Boolean;
-- Split a time value into its components Time_Zone : Long_Integer);
-- Split a time value into its components. Set Is_Ada_05 to use the
-- local time zone (the value in Time_Zone is ignored) when splitting
-- a time value.
function Time_Of function Time_Of
(Year : Year_Number; (Year : Year_Number;
...@@ -191,18 +189,20 @@ private ...@@ -191,18 +189,20 @@ private
Second : Integer; Second : Integer;
Sub_Sec : Duration; Sub_Sec : Duration;
Leap_Sec : Boolean; Leap_Sec : Boolean;
Leap_Checks : Boolean;
Use_Day_Secs : Boolean; Use_Day_Secs : Boolean;
Is_Ada_05 : Boolean;
Time_Zone : Long_Integer) return Time; Time_Zone : Long_Integer) return Time;
-- Given all the components of a date, return the corresponding time -- Given all the components of a date, return the corresponding time
-- value. Set Use_Day_Secs to use the value in Day_Secs, otherwise the -- value. Set Use_Day_Secs to use the value in Day_Secs, otherwise the
-- day duration will be calculated from Hour, Minute, Second and Sub_ -- day duration will be calculated from Hour, Minute, Second and Sub_
-- Sec. Set flag Leap_Checks to verify the validity of a leap second. -- Sec. Set Is_Ada_05 to use the local time zone (the value in formal
-- Time_Zone is ignored) when building a time value and to verify the
-- validity of a requested leap second.
end Formatting_Operations; end Formatting_Operations;
package Time_Zones_Operations is package Time_Zones_Operations is
function UTC_Time_Offset (Date : Time) return Long_Integer; function UTC_Time_Offset (Date : Time) return Long_Integer;
-- Return the offset in seconds from GMT -- Return the offset in seconds from UTC
end Time_Zones_Operations; end Time_Zones_Operations;
end Ada.Calendar; end Ada.Calendar;
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
-- -- -- --
-- B o d y -- -- 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 -- -- 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- -- -- terms of the GNU General Public License as published by the Free Soft- --
...@@ -47,9 +47,9 @@ package body Ada.Calendar is ...@@ -47,9 +47,9 @@ package body Ada.Calendar is
-- --
-- Because time is measured in different units and from different origins -- Because time is measured in different units and from different origins
-- on various targets, a system independent model is incorporated into -- 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 -- 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 -- package Ada.Calendar
-- procedure Split (5 parameters) -------+ -- procedure Split (5 parameters) -------+
...@@ -76,17 +76,21 @@ package body Ada.Calendar is ...@@ -76,17 +76,21 @@ package body Ada.Calendar is
-- Local Subprograms -- -- 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 procedure Cumulative_Leap_Seconds
(Start_Date : Time; (Start_Date : Time_Rep;
End_Date : Time; End_Date : Time_Rep;
Elapsed_Leaps : out Natural; 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 -- 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 -- after Start_Date and before (strictly before) End_Date. Next_Leap_Sec
-- represents the next leap second occurence on or after End_Date. If -- 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. -- there are no leaps seconds after End_Date, End_Of_Time is returned.
-- After_Last_Leap can be used as End_Date to count all the leap seconds -- End_Of_Time can be used as End_Date to count all the leap seconds that
-- that have occured on or after Start_Date. -- have occured on or after Start_Date.
-- --
-- Note: Any sub seconds of Start_Date and End_Date are discarded before -- 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 -- the calculations are done. For instance: if 113 seconds is a leap
...@@ -100,46 +104,77 @@ package body Ada.Calendar is ...@@ -100,46 +104,77 @@ package body Ada.Calendar is
-- After_Last_Leap is designed so that this comparison works without -- After_Last_Leap is designed so that this comparison works without
-- having to first check if Next_Leap_Sec is a valid leap second. -- having to first check if Next_Leap_Sec is a valid leap second.
function To_Abs_Duration (T : Time) return Duration; function Duration_To_Time_Rep is
-- Convert a time value into a duration value. Note that the returned new Ada.Unchecked_Conversion (Duration, Time_Rep);
-- duration is always positive. -- 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; type Time_Dur is range 0 .. 2 ** 63 - 1;
-- 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.
--------------------- ---------------------
-- Local Constants -- -- 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; 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_Four_Years : constant := (3 * 365 + 366) * Secs_In_Day;
Secs_In_Non_Leap_Year : constant := 365 * 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 -- Lower and upper bound of Ada time. The zero (0) value of type Time is
-- GMT, it must be shifted to include all leap seconds. -- 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_And_Leaps : constant Time_Rep :=
Ada_High + Time (Leap_Seconds_Count) * Nano; Ada_High + Time_Rep (Leap_Seconds_Count) * Nano;
Hard_Ada_High_And_Leaps : constant Time := -- Two constants used in the calculations of elapsed leap seconds.
Hard_Ada_High + -- End_Of_Time is later than Ada_High in time zone -28. Start_Of_Time
Time (Leap_Seconds_Count) * Nano; -- 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 -- 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 : Cumulative_Days_Before_Month :
constant array (Month_Number) of Natural := constant array (Month_Number) of Natural :=
(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334); (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 -- Each value represents a time value which is one second before a leap
-- second occurence. This table is populated during the elaboration of -- second occurence. This table is populated during the elaboration of
-- Ada.Calendar. -- Ada.Calendar.
...@@ -151,46 +186,21 @@ package body Ada.Calendar is ...@@ -151,46 +186,21 @@ package body Ada.Calendar is
function "+" (Left : Time; Right : Duration) return Time is function "+" (Left : Time; Right : Duration) return Time is
pragma Unsuppress (Overflow_Check); pragma Unsuppress (Overflow_Check);
begin Left_N : constant Time_Rep := Time_Rep (Left);
if Right = 0.0 then Res_N : Time_Rep;
return Left;
elsif Right < 0.0 then begin
-- Trivial case
-- 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
Result : constant Time := Left + To_Abs_Time (Right); if Right = Duration (0.0) then
return Left;
end if;
begin Res_N := Left_N + Duration_To_Time_Rep (Right);
-- 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).
if Result >= Ada_High_And_Leaps then Check_Within_Time_Bounds (Res_N);
raise Time_Error;
end if;
return Result; return Time (Res_N);
end;
end if;
exception exception
when Constraint_Error => when Constraint_Error =>
...@@ -209,38 +219,21 @@ package body Ada.Calendar is ...@@ -209,38 +219,21 @@ package body Ada.Calendar is
function "-" (Left : Time; Right : Duration) return Time is function "-" (Left : Time; Right : Duration) return Time is
pragma Unsuppress (Overflow_Check); pragma Unsuppress (Overflow_Check);
begin Left_N : constant Time_Rep := Time_Rep (Left);
if Right = 0.0 then Res_N : Time_Rep;
return Left;
elsif Right < 0.0 then
return Left + abs (Right);
else
declare
Result : Time;
Right_T : constant Time := To_Abs_Time (Right);
begin begin
-- Subtracting a larger time value from a smaller time value -- Trivial case
-- will cause a wrap around since Time is a modular type. Note
-- that the time value has been normalized to GMT.
if Left < Right_T then if Right = Duration (0.0) then
raise Time_Error; return Left;
end if; end if;
Result := Left - Right_T; Res_N := Left_N - Duration_To_Time_Rep (Right);
if Result < Ada_Low Check_Within_Time_Bounds (Res_N);
or else Result > Ada_High_And_Leaps
then
raise Time_Error;
end if;
return Result; return Time (Res_N);
end;
end if;
exception exception
when Constraint_Error => when Constraint_Error =>
...@@ -250,54 +243,25 @@ package body Ada.Calendar is ...@@ -250,54 +243,25 @@ package body Ada.Calendar is
function "-" (Left : Time; Right : Time) return Duration is function "-" (Left : Time; Right : Time) return Duration is
pragma Unsuppress (Overflow_Check); 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 Dur_Low : constant Time_Rep := Duration_To_Time_Rep (Duration'First);
-- are denoted by the same number, it is sufficend to use Duration'Last Dur_High : constant Time_Rep := Duration_To_Time_Rep (Duration'Last);
-- when performing out of range checks.
Duration_Bound : constant Time := To_Time (Duration'Last); Res_N : Time_Rep;
Earlier : Time;
Later : Time;
Negate : Boolean := False;
Result : Time;
Result_D : Duration;
begin begin
-- This routine becomes a little tricky since time cannot be negative, Res_N := Time_Rep (Left) - Time_Rep (Right);
-- 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;
Result := Later - Earlier; -- The result does not fit in a duration value
-- Check whether the resulting difference is within the range of type if Res_N < Dur_Low
-- Duration. The following two conditions are examined with the same or else Res_N > Dur_High
-- piece of code: then
--
-- positive result > positive upper bound of duration
--
-- negative (negative result) > abs (negative bound of duration)
if Result > Duration_Bound then
raise Time_Error; raise Time_Error;
end if; end if;
Result_D := To_Abs_Duration (Result); return Time_Rep_To_Duration (Res_N);
if Negate then
Result_D := -Result_D;
end if;
return Result_D;
exception exception
when Constraint_Error => when Constraint_Error =>
raise Time_Error; raise Time_Error;
...@@ -339,39 +303,62 @@ package body Ada.Calendar is ...@@ -339,39 +303,62 @@ package body Ada.Calendar is
return Time_Rep (Left) >= Time_Rep (Right); return Time_Rep (Left) >= Time_Rep (Right);
end ">="; 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 -- -- Clock --
----------- -----------
function Clock return Time is function Clock return Time is
Elapsed_Leaps : Natural; Elapsed_Leaps : Natural;
Next_Leap : Time; Next_Leap_N : Time_Rep;
-- 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.
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 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 else
Now := Now + Time (1) * Nano; Elapsed_Leaps := 0;
end if; end if;
-- Add the buffer set aside for time zone processing since Split in Res_N := Res_N + Time_Rep (Elapsed_Leaps) * Nano;
-- Ada.Calendar.Formatting_Operations expects it to be there.
return Now + Buffer_N; return Time (Res_N);
end Clock; end Clock;
----------------------------- -----------------------------
...@@ -379,23 +366,22 @@ package body Ada.Calendar is ...@@ -379,23 +366,22 @@ package body Ada.Calendar is
----------------------------- -----------------------------
procedure Cumulative_Leap_Seconds procedure Cumulative_Leap_Seconds
(Start_Date : Time; (Start_Date : Time_Rep;
End_Date : Time; End_Date : Time_Rep;
Elapsed_Leaps : out Natural; Elapsed_Leaps : out Natural;
Next_Leap_Sec : out Time) Next_Leap : out Time_Rep)
is is
End_Index : Positive; End_Index : Positive;
End_T : Time := End_Date; End_T : Time_Rep := End_Date;
Start_Index : Positive; Start_Index : Positive;
Start_T : Time := Start_Date; Start_T : Time_Rep := Start_Date;
begin begin
-- Both input dates need to be normalized to GMT in order for this -- Both input dates must be normalized to UTC
-- routine to work properly.
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 -- Make sure that the end date does not excede the upper bound
-- of Ada time. -- of Ada time.
...@@ -416,12 +402,12 @@ package body Ada.Calendar is ...@@ -416,12 +402,12 @@ package body Ada.Calendar is
if End_T < Leap_Second_Times (1) then if End_T < Leap_Second_Times (1) then
Elapsed_Leaps := 0; Elapsed_Leaps := 0;
Next_Leap_Sec := Leap_Second_Times (1); Next_Leap := Leap_Second_Times (1);
return; return;
elsif Start_T > Leap_Second_Times (Leap_Seconds_Count) then elsif Start_T > Leap_Second_Times (Leap_Seconds_Count) then
Elapsed_Leaps := 0; Elapsed_Leaps := 0;
Next_Leap_Sec := After_Last_Leap; Next_Leap := End_Of_Time;
return; return;
end if; end if;
...@@ -458,7 +444,7 @@ package body Ada.Calendar is ...@@ -458,7 +444,7 @@ package body Ada.Calendar is
end loop; end loop;
if End_Index <= Leap_Seconds_Count then if End_Index <= Leap_Seconds_Count then
Next_Leap_Sec := Leap_Second_Times (End_Index); Next_Leap := Leap_Second_Times (End_Index);
end if; end if;
Elapsed_Leaps := End_Index - Start_Index; Elapsed_Leaps := End_Index - Start_Index;
...@@ -549,12 +535,24 @@ package body Ada.Calendar is ...@@ -549,12 +535,24 @@ package body Ada.Calendar is
Se : Integer; Se : Integer;
Ss : Duration; Ss : Duration;
Le : Boolean; Le : Boolean;
Tz : constant Long_Integer :=
Time_Zones_Operations.UTC_Time_Offset (Date) / 60;
begin 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 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 -- Validity checks
...@@ -586,11 +584,9 @@ package body Ada.Calendar is ...@@ -586,11 +584,9 @@ package body Ada.Calendar is
Se : constant Integer := 1; Se : constant Integer := 1;
Ss : constant Duration := 0.1; Ss : constant Duration := 0.1;
Mid_Offset : Long_Integer;
Mid_Result : Time;
Offset : Long_Integer;
begin begin
-- Validity checks
if not Year'Valid if not Year'Valid
or else not Month'Valid or else not Month'Valid
or else not Day'Valid or else not Day'Valid
...@@ -599,96 +595,25 @@ package body Ada.Calendar is ...@@ -599,96 +595,25 @@ package body Ada.Calendar is
raise Time_Error; raise Time_Error;
end if; end if;
-- Building a time value in a local time zone is tricky since the -- Even though the input time zone is UTC (0), the flag Is_Ada_05 will
-- local time zone offset at the point of creation may not be the -- ensure that Split picks up the local time zone.
-- same as the actual time zone offset designated by the input
-- values. The following example is relevant to New York, USA. return
-- Formatting_Operations.Time_Of
-- Creation date: 2006-10-10 0.0 Offset -240 mins (in DST) (Year => Year,
-- Actual date : 1901-01-01 0.0 Offset -300 mins (no DST) Month => Month,
Day => Day,
-- We first start by obtaining the current local time zone offset Day_Secs => Seconds,
-- using Ada.Calendar.Clock, then building an intermediate time Hour => H,
-- value using that offset. Minute => M,
Second => Se,
Mid_Offset := Time_Zones_Operations.UTC_Time_Offset (Clock) / 60; Sub_Sec => Ss,
Mid_Result := Formatting_Operations.Time_Of Leap_Sec => False,
(Year, Month, Day, Seconds, H, M, Se, Ss, Use_Day_Secs => True,
Leap_Sec => False, Is_Ada_05 => False,
Leap_Checks => False, Time_Zone => 0);
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;
end Time_Of; 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 -- -- Year --
---------- ----------
...@@ -703,9 +628,9 @@ package body Ada.Calendar is ...@@ -703,9 +628,9 @@ package body Ada.Calendar is
return Y; return Y;
end Year; 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 -- 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 -- -- Arithmetic_Operations --
...@@ -718,27 +643,23 @@ package body Ada.Calendar is ...@@ -718,27 +643,23 @@ package body Ada.Calendar is
--------- ---------
function Add (Date : Time; Days : Long_Integer) return Time 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 begin
-- Trivial case
if Days = 0 then if Days = 0 then
return Date; return Date;
end if;
elsif Days < 0 then Res_N := Date_N + Time_Rep (Days) * Nanos_In_Day;
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
if Result > Ada_High_And_Leaps then Check_Within_Time_Bounds (Res_N);
raise Time_Error;
end if;
return Result; return Time (Res_N);
end;
end if;
exception exception
when Constraint_Error => when Constraint_Error =>
...@@ -756,54 +677,70 @@ package body Ada.Calendar is ...@@ -756,54 +677,70 @@ package body Ada.Calendar is
Seconds : out Duration; Seconds : out Duration;
Leap_Seconds : out Integer) Leap_Seconds : out Integer)
is is
Diff_N : Time; Res_Dur : Time_Dur;
Diff_S : Time; Earlier : Time_Rep;
Earlier : Time; Earlier_Sub : Time_Rep;
Elapsed_Leaps : Natural; Elapsed_Leaps : Natural;
Later : Time; Later : Time_Rep;
Later_Sub : Time_Rep;
Negate : Boolean := False; Negate : Boolean := False;
Next_Leap : Time; Next_Leap_N : Time_Rep;
Sub_Seconds : Duration; Sub_Seconds : Duration;
begin 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 if Left >= Right then
Later := Left; Later := Time_Rep (Left);
Earlier := Right; Earlier := Time_Rep (Right);
else else
Later := Right; Later := Time_Rep (Right);
Earlier := Left; Earlier := Time_Rep (Left);
Negate := True; Negate := True;
end if; 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 if Later >= Next_Leap_N then
Elapsed_Leaps := Elapsed_Leaps + 1; Elapsed_Leaps := Elapsed_Leaps + 1;
end if;
-- The target does not support leap seconds
else
Elapsed_Leaps := 0;
end if; 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 Sub_Seconds := Duration (Later_Sub - Earlier_Sub) / Nano_F;
-- seconds automatically.
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); Days := Long_Integer (Res_Dur / Secs_In_Day);
Seconds := Duration (Diff_S mod Secs_In_Day) + Sub_Seconds; Seconds := Duration (Res_Dur mod Secs_In_Day) + Sub_Seconds;
Leap_Seconds := Integer (Elapsed_Leaps); Leap_Seconds := Integer (Elapsed_Leaps);
if Negate then if Negate then
Days := -Days; Days := -Days;
Seconds := -Seconds; Seconds := -Seconds;
Leap_Seconds := -Leap_Seconds;
if Leap_Seconds /= 0 then
Leap_Seconds := -Leap_Seconds;
end if;
end if; end if;
end Difference; end Difference;
...@@ -812,37 +749,23 @@ package body Ada.Calendar is ...@@ -812,37 +749,23 @@ package body Ada.Calendar is
-------------- --------------
function Subtract (Date : Time; Days : Long_Integer) return Time is function Subtract (Date : Time; Days : Long_Integer) return Time is
begin pragma Unsuppress (Overflow_Check);
if Days = 0 then
return Date;
elsif Days < 0 then Date_N : constant Time_Rep := Time_Rep (Date);
return Add (Date, abs (Days)); Res_N : Time_Rep;
else begin
declare -- Trivial case
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.
if Date < Days_T then if Days = 0 then
raise Time_Error; return Date;
end if; end if;
Result := Date - Days_T; Res_N := Date_N - Time_Rep (Days) * Nanos_In_Day;
if Result < Ada_Low Check_Within_Time_Bounds (Res_N);
or else Result > Ada_High_And_Leaps
then
raise Time_Error;
end if;
return Result; return Time (Res_N);
end;
end if;
exception exception
when Constraint_Error => when Constraint_Error =>
...@@ -860,37 +783,39 @@ package body Ada.Calendar is ...@@ -860,37 +783,39 @@ package body Ada.Calendar is
-- To_Duration -- -- To_Duration --
----------------- -----------------
function To_Duration (Ada_Time : Time) return Duration is function To_Duration (Date : Time) return Duration is
Elapsed_Leaps : Natural; Elapsed_Leaps : Natural;
Modified_Time : Time; Next_Leap_N : Time_Rep;
Next_Leap : Time; Res_N : Time_Rep;
Result : Duration;
Rounded_Time : Time;
begin begin
Modified_Time := Ada_Time; Res_N := Time_Rep (Date);
Rounded_Time := Modified_Time - (Modified_Time mod Nano);
-- Remove all leap seconds -- If the target supports leap seconds, remove any leap seconds
-- elapsed upto the input date.
Cumulative_Leap_Seconds if Leap_Support then
(Time_Zero, Modified_Time, Elapsed_Leaps, Next_Leap); 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 -- The target does not support leap seconds
Modified_Time := Modified_Time - Time (1) * Nano;
end if;
-- 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 To_Duration;
end Delays_Operations; end Delays_Operations;
...@@ -908,34 +833,58 @@ package body Ada.Calendar is ...@@ -908,34 +833,58 @@ package body Ada.Calendar is
Y : Year_Number; Y : Year_Number;
Mo : Month_Number; Mo : Month_Number;
D : Day_Number; D : Day_Number;
Dd : Day_Duration; Ds : Day_Duration;
H : Integer; H : Integer;
Mi : Integer; Mi : Integer;
Se : Integer; Se : Integer;
Su : Duration; Su : Duration;
Le : Boolean; Le : Boolean;
Day_Count : Long_Integer; Day_Count : Long_Integer;
Midday_Date_S : Time; Res_Dur : Time_Dur;
Res_N : Time_Rep;
begin begin
Formatting_Operations.Split Formatting_Operations.Split
(Date, Y, Mo, D, Dd, H, Mi, Se, Su, Le, 0); (Date => Date,
Year => Y,
-- Build a time value in the middle of the same day, remove the Month => Mo,
-- lower buffer and convert the time value to seconds. Day => D,
Day_Secs => Ds,
Midday_Date_S := (Formatting_Operations.Time_Of Hour => H,
(Y, Mo, D, 0.0, 12, 0, 0, 0.0, Minute => Mi,
Leap_Sec => False, Second => Se,
Leap_Checks => False, Sub_Sec => Su,
Use_Day_Secs => False, Leap_Sec => Le,
Time_Zone => 0) - Buffer_N) / Nano; 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 -- Count the number of days since the start of Ada time. 1901-1-1
-- GMT was a Tuesday. -- 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); return Integer (Day_Count mod 7);
end Day_Of_Week; end Day_Of_Week;
...@@ -945,180 +894,139 @@ package body Ada.Calendar is ...@@ -945,180 +894,139 @@ package body Ada.Calendar is
----------- -----------
procedure Split procedure Split
(Date : Time; (Date : Time;
Year : out Year_Number; Year : out Year_Number;
Month : out Month_Number; Month : out Month_Number;
Day : out Day_Number; Day : out Day_Number;
Day_Secs : out Day_Duration; Day_Secs : out Day_Duration;
Hour : out Integer; Hour : out Integer;
Minute : out Integer; Minute : out Integer;
Second : out Integer; Second : out Integer;
Sub_Sec : out Duration; Sub_Sec : out Duration;
Leap_Sec : out Boolean; Leap_Sec : out Boolean;
Time_Zone : Long_Integer) Is_Ada_05 : Boolean;
Time_Zone : Long_Integer)
is is
-- The following constants represent the number of nanoseconds -- The following constants represent the number of nanoseconds
-- elapsed since the start of Ada time to and including the non -- elapsed since the start of Ada time to and including the non
-- leap centenial years. -- leap centenial years.
Year_2101 : constant Time := (49 * 366 + 151 * 365) * Nanos_In_Day; Year_2101 : constant Time_Rep := Ada_Low +
Year_2201 : constant Time := (73 * 366 + 227 * 365) * Nanos_In_Day; Time_Rep (49 * 366 + 151 * 365) * Nanos_In_Day;
Year_2301 : constant Time := (97 * 366 + 303 * 365) * Nanos_In_Day; Year_2201 : constant Time_Rep := Ada_Low +
Time_Rep (73 * 366 + 227 * 365) * Nanos_In_Day;
Abs_Time_Zone : Time; Year_2301 : constant Time_Rep := Ada_Low +
Day_Seconds : Natural; Time_Rep (97 * 366 + 303 * 365) * Nanos_In_Day;
Elapsed_Leaps : Natural;
Four_Year_Segs : Natural; Date_Dur : Time_Dur;
Hour_Seconds : Natural; Date_N : Time_Rep;
Is_Leap_Year : Boolean; Day_Seconds : Natural;
Modified_Date_N : Time; Elapsed_Leaps : Natural;
Modified_Date_S : Time; Four_Year_Segs : Natural;
Next_Leap_N : Time; Hour_Seconds : Natural;
Rem_Years : Natural; Is_Leap_Year : Boolean;
Rounded_Date_N : Time; Next_Leap_N : Time_Rep;
Year_Day : Natural; Rem_Years : Natural;
Sub_Sec_N : Time_Rep;
Year_Day : Natural;
begin begin
Modified_Date_N := Date; Date_N := Time_Rep (Date);
if Modified_Date_N < Hard_Ada_Low -- Step 1: Leap seconds processing in UTC
or else Modified_Date_N > Hard_Ada_High_And_Leaps
then
raise Time_Error;
end if;
-- Step 1: Leap seconds processing in GMT if Leap_Support then
Cumulative_Leap_Seconds
-- Day_Duration: 86_398 86_399 X (86_400) 0 (1) 1 (2) (Start_Of_Time, Date_N, Elapsed_Leaps, Next_Leap_N);
-- 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.
Cumulative_Leap_Seconds Leap_Sec := Date_N >= Next_Leap_N;
(Time_Zero, Modified_Date_N, Elapsed_Leaps, Next_Leap_N);
Rounded_Date_N := Modified_Date_N - (Modified_Date_N mod Nano); if Leap_Sec then
Leap_Sec := Rounded_Date_N = Next_Leap_N; Elapsed_Leaps := Elapsed_Leaps + 1;
Modified_Date_N := Modified_Date_N - Time (Elapsed_Leaps) * Nano; end if;
-- The target does not support leap seconds
if Leap_Sec then else
Modified_Date_N := Modified_Date_N - Time (1) * Nano; Elapsed_Leaps := 0;
Leap_Sec := False;
end if; end if;
Date_N := Date_N - Time_Rep (Elapsed_Leaps) * Nano;
-- Step 2: Time zone processing. This action converts the input date -- Step 2: Time zone processing. This action converts the input date
-- from GMT to the requested time zone. -- from GMT to the requested time zone.
if Time_Zone /= 0 then if Is_Ada_05 then
Abs_Time_Zone := Time (abs (Time_Zone)) * 60 * Nano; if Time_Zone /= 0 then
Date_N := Date_N + Time_Rep (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;
end if; end if;
end if;
-- After the elapsed leap seconds have been removed and the date -- Ada 83 and 95
-- has been normalized, it should fall withing the soft bounds of
-- Ada time.
if Modified_Date_N < Ada_Low else
or else Modified_Date_N > Ada_High declare
then Off : constant Long_Integer :=
raise Time_Error; Time_Zones_Operations.UTC_Time_Offset (Time (Date_N));
begin
Date_N := Date_N + Time_Rep (Off) * Nano;
end;
end if; 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 -- Step 3: Non-leap centenial year adjustment in local time zone
-- In order for all divisions to work properly and to avoid more -- In order for all divisions to work properly and to avoid more
-- complicated arithmetic, we add fake Febriary 29s to dates which -- complicated arithmetic, we add fake Febriary 29s to dates which
-- occur after a non-leap centenial year. -- occur after a non-leap centenial year.
if Modified_Date_N >= Year_2301 then if Date_N >= Year_2301 then
Modified_Date_N := Modified_Date_N + Time (3) * Nanos_In_Day; Date_N := Date_N + Time_Rep (3) * Nanos_In_Day;
elsif Modified_Date_N >= Year_2201 then elsif Date_N >= Year_2201 then
Modified_Date_N := Modified_Date_N + Time (2) * Nanos_In_Day; Date_N := Date_N + Time_Rep (2) * Nanos_In_Day;
elsif Modified_Date_N >= Year_2101 then elsif Date_N >= Year_2101 then
Modified_Date_N := Modified_Date_N + Time (1) * Nanos_In_Day; Date_N := Date_N + Time_Rep (1) * Nanos_In_Day;
end if; end if;
-- Step 4: Sub second processing in local time zone -- 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 -- Convert Date_N into a time duration value, changing the units
-- dropped. -- 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 -- Step 5: Year processing in local time zone. Determine the number
-- of four year segments since the start of Ada time and the input -- of four year segments since the start of Ada time and the input
-- date. -- 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 if Four_Year_Segs > 0 then
Modified_Date_S := Modified_Date_S - Time (Four_Year_Segs) * Date_Dur := Date_Dur - Time_Dur (Four_Year_Segs) *
Secs_In_Four_Years; Secs_In_Four_Years;
end if; end if;
-- Calculate the remaining non-leap years -- 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 if Rem_Years > 3 then
Rem_Years := 3; Rem_Years := 3;
end if; end if;
Modified_Date_S := Modified_Date_S - Time (Rem_Years) * Date_Dur := Date_Dur - Time_Dur (Rem_Years) * Secs_In_Non_Leap_Year;
Secs_In_Non_Leap_Year;
Year := Ada_Min_Year + Natural (4 * Four_Year_Segs + Rem_Years); Year := Ada_Min_Year + Natural (4 * Four_Year_Segs + Rem_Years);
Is_Leap_Year := Is_Leap (Year); Is_Leap_Year := Is_Leap (Year);
-- Step 6: Month and day processing in local time zone -- 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; Month := 1;
...@@ -1131,8 +1039,7 @@ package body Ada.Calendar is ...@@ -1131,8 +1039,7 @@ package body Ada.Calendar is
-- Processing for a new month or a leap February -- Processing for a new month or a leap February
if Year_Day > 28 if Year_Day > 28
and then (not Is_Leap_Year and then (not Is_Leap_Year or else Year_Day > 29)
or else Year_Day > 29)
then then
Month := 3; Month := 3;
Year_Day := Year_Day - 28; Year_Day := Year_Day - 28;
...@@ -1154,7 +1061,7 @@ package body Ada.Calendar is ...@@ -1154,7 +1061,7 @@ package body Ada.Calendar is
-- time zone. -- time zone.
Day := Day_Number (Year_Day); 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; Day_Secs := Duration (Day_Seconds) + Sub_Sec;
Hour := Day_Seconds / 3_600; Hour := Day_Seconds / 3_600;
Hour_Seconds := Day_Seconds mod 3_600; Hour_Seconds := Day_Seconds mod 3_600;
...@@ -1176,16 +1083,15 @@ package body Ada.Calendar is ...@@ -1176,16 +1083,15 @@ package body Ada.Calendar is
Second : Integer; Second : Integer;
Sub_Sec : Duration; Sub_Sec : Duration;
Leap_Sec : Boolean; Leap_Sec : Boolean;
Leap_Checks : Boolean;
Use_Day_Secs : Boolean; Use_Day_Secs : Boolean;
Is_Ada_05 : Boolean;
Time_Zone : Long_Integer) return Time Time_Zone : Long_Integer) return Time
is is
Abs_Time_Zone : Time; Count : Integer;
Count : Integer; Elapsed_Leaps : Natural;
Elapsed_Leaps : Natural; Next_Leap_N : Time_Rep;
Next_Leap_N : Time; Res_N : Time_Rep;
Result_N : Time; Rounded_Res_N : Time_Rep;
Rounded_Result_N : Time;
begin begin
-- Step 1: Check whether the day, month and year form a valid date -- Step 1: Check whether the day, month and year form a valid date
...@@ -1196,37 +1102,35 @@ package body Ada.Calendar is ...@@ -1196,37 +1102,35 @@ package body Ada.Calendar is
raise Time_Error; raise Time_Error;
end if; end if;
-- Start accumulating nanoseconds from the low bound of Ada time. -- Start accumulating nanoseconds from the low bound of Ada time
-- Note: This starting point includes the lower buffer dedicated
-- to time zones.
Result_N := Ada_Low; Res_N := Ada_Low;
-- Step 2: Year processing and centenial year adjustment. Determine -- Step 2: Year processing and centenial year adjustment. Determine
-- the number of four year segments since the start of Ada time and -- the number of four year segments since the start of Ada time and
-- the input date. -- the input date.
Count := (Year - Year_Number'First) / 4; Count := (Year - Year_Number'First) / 4;
Result_N := Result_N + Time (Count) * Secs_In_Four_Years * Nano; Res_N := Res_N + Time_Rep (Count) * Secs_In_Four_Years * Nano;
-- Note that non-leap centenial years are automatically considered -- Note that non-leap centenial years are automatically considered
-- leap in the operation above. An adjustment of several days is -- leap in the operation above. An adjustment of several days is
-- required to compensate for this. -- required to compensate for this.
if Year > 2300 then 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 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 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; end if;
-- Add the remaining non-leap years -- Add the remaining non-leap years
Count := (Year - Year_Number'First) mod 4; Count := (Year - Year_Number'First) mod 4;
Result_N := Result_N + Time (Count) * Secs_In_Non_Leap_Year * Nano; Res_N := Res_N + Time_Rep (Count) * Secs_In_Non_Leap_Year * Nano;
-- Step 3: Day of month processing. Determine the number of days -- Step 3: Day of month processing. Determine the number of days
-- since the start of the current year. Do not add the current -- since the start of the current year. Do not add the current
...@@ -1242,92 +1146,87 @@ package body Ada.Calendar is ...@@ -1242,92 +1146,87 @@ package body Ada.Calendar is
Count := Count + 1; Count := Count + 1;
end if; 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 -- Step 4: Hour, minute, second and sub second processing
if Use_Day_Secs then 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 else
Result_N := Result_N + Res_N := Res_N +
Time (Hour * 3_600 + Minute * 60 + Second) * Nano; Time_Rep (Hour * 3_600 + Minute * 60 + Second) * Nano;
if Sub_Sec = 1.0 then if Sub_Sec = 1.0 then
Result_N := Result_N + Time (1) * Nano; Res_N := Res_N + Time_Rep (1) * Nano;
else else
Result_N := Result_N + To_Abs_Time (Sub_Sec); Res_N := Res_N + Duration_To_Time_Rep (Sub_Sec);
end if; end if;
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 -- Step 4: Time zone processing. At this point we have built an
-- arbitrary time value which is not related to any time zone. -- arbitrary time value which is not related to any time zone.
-- For simplicity, the time value is normalized to GMT, producing -- For simplicity, the time value is normalized to GMT, producing
-- a uniform representation which can be treated by arithmetic -- a uniform representation which can be treated by arithmetic
-- operations for instance without any additional corrections. -- operations for instance without any additional corrections.
if Result_N < Ada_Low if Is_Ada_05 then
or else Result_N > Ada_High if Time_Zone /= 0 then
then Res_N := Res_N - Time_Rep (Time_Zone) * 60 * Nano;
raise Time_Error; end if;
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 Result_N < Abs_Time_Zone then -- Ada 83 and 95
raise Time_Error;
end if;
Result_N := Result_N - Abs_Time_Zone; else
end if; 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; end if;
-- Step 5: Leap seconds processing in GMT -- Step 5: Leap seconds processing in GMT
Cumulative_Leap_Seconds if Leap_Support then
(Time_Zero, Result_N, Elapsed_Leaps, Next_Leap_N); Cumulative_Leap_Seconds
(Start_Of_Time, Res_N, Elapsed_Leaps, Next_Leap_N);
Result_N := Result_N + Time (Elapsed_Leaps) * Nano;
-- An Ada 2005 caller requesting an explicit leap second or an Ada Res_N := Res_N + Time_Rep (Elapsed_Leaps) * Nano;
-- 95 caller accounting for an invisible leap second.
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 if Leap_Sec
or else Rounded_Result_N = Next_Leap_N or else Res_N >= Next_Leap_N
then then
Result_N := Result_N + Time (1) * Nano; Res_N := Res_N + Time_Rep (1) * Nano;
Rounded_Result_N := Rounded_Result_N + Time (1) * Nano; end if;
end if;
-- Leap second validity check -- Leap second validity check
if Leap_Checks Rounded_Res_N := Res_N - (Res_N mod Nano);
and then Leap_Sec
and then Rounded_Result_N /= Next_Leap_N
then
raise Time_Error;
end if;
-- Final bounds check
if Result_N < Hard_Ada_Low if Is_Ada_05
or else Result_N > Hard_Ada_High_And_Leaps and then Leap_Sec
then and then Rounded_Res_N /= Next_Leap_N
raise Time_Error; then
raise Time_Error;
end if;
end if; end if;
return Result_N; return Time (Res_N);
end Time_Of; end Time_Of;
end Formatting_Operations; end Formatting_Operations;
...@@ -1337,35 +1236,33 @@ package body Ada.Calendar is ...@@ -1337,35 +1236,33 @@ package body Ada.Calendar is
package body Time_Zones_Operations 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 := Unix_Min : constant Time_Rep := Ada_Low +
Time (17 * 366 + 52 * 365 + 2) * Secs_In_Day; Time_Rep (17 * 366 + 52 * 365) * Nanos_In_Day;
-- 1970/1/1
Unix_Max : constant Time := Unix_Max : constant Time_Rep := Ada_Low +
Time (34 * 366 + 102 * 365 + 2) * Secs_In_Day + Time_Rep (34 * 366 + 102 * 365) * Nanos_In_Day +
Time (Leap_Seconds_Count); Time_Rep (Leap_Seconds_Count) * Nano;
-- 2037/1/1
-- The following constants denote February 28 during non-leap -- The following constants denote February 28 during non-leap
-- centenial years, the units are nanoseconds. -- centenial years, the units are nanoseconds.
T_2100_2_28 : constant Time := T_2100_2_28 : constant Time_Rep := Ada_Low +
(Time (49 * 366 + 150 * 365 + 59 + 2) * Secs_In_Day + (Time_Rep (49 * 366 + 150 * 365 + 59) * Secs_In_Day +
Time (Leap_Seconds_Count)) * Nano; Time_Rep (Leap_Seconds_Count)) * Nano;
T_2200_2_28 : constant Time := T_2200_2_28 : constant Time_Rep := Ada_Low +
(Time (73 * 366 + 226 * 365 + 59 + 2) * Secs_In_Day + (Time_Rep (73 * 366 + 226 * 365 + 59) * Secs_In_Day +
Time (Leap_Seconds_Count)) * Nano; Time_Rep (Leap_Seconds_Count)) * Nano;
T_2300_2_28 : constant Time := T_2300_2_28 : constant Time_Rep := Ada_Low +
(Time (97 * 366 + 302 * 365 + 59 + 2) * Secs_In_Day + (Time_Rep (97 * 366 + 302 * 365 + 59) * Secs_In_Day +
Time (Leap_Seconds_Count)) * Nano; 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 -- Base C types. There is no point dragging in Interfaces.C just for
-- these four types. -- these four types.
...@@ -1378,17 +1275,17 @@ package body Ada.Calendar is ...@@ -1378,17 +1275,17 @@ package body Ada.Calendar is
-- The Ada equivalent of struct tm and type time_t -- The Ada equivalent of struct tm and type time_t
type tm is record type tm is record
tm_sec : int; -- seconds after the minute (0 .. 60) tm_sec : int; -- seconds after the minute (0 .. 60)
tm_min : int; -- minutes after the hour (0 .. 59) tm_min : int; -- minutes after the hour (0 .. 59)
tm_hour : int; -- hours since midnight (0 .. 24) tm_hour : int; -- hours since midnight (0 .. 24)
tm_mday : int; -- day of the month (1 .. 31) tm_mday : int; -- day of the month (1 .. 31)
tm_mon : int; -- months since January (0 .. 11) tm_mon : int; -- months since January (0 .. 11)
tm_year : int; -- years since 1900 tm_year : int; -- years since 1900
tm_wday : int; -- days since Sunday (0 .. 6) tm_wday : int; -- days since Sunday (0 .. 6)
tm_yday : int; -- days since January 1 (0 .. 365) tm_yday : int; -- days since January 1 (0 .. 365)
tm_isdst : int; -- Daylight Savings Time flag (-1 .. 1) tm_isdst : int; -- Daylight Savings Time flag (-1 .. 1)
tm_gmtoff : long; -- offset from UTC in seconds tm_gmtoff : long; -- offset from UTC in seconds
tm_zone : char_Pointer; -- timezone abbreviation tm_zone : char_Pointer; -- timezone abbreviation
end record; end record;
type tm_Pointer is access all tm; type tm_Pointer is access all tm;
...@@ -1411,24 +1308,22 @@ package body Ada.Calendar is ...@@ -1411,24 +1308,22 @@ package body Ada.Calendar is
--------------------- ---------------------
function UTC_Time_Offset (Date : Time) return Long_Integer is function UTC_Time_Offset (Date : Time) return Long_Integer is
Adj_Cent : Integer := 0;
Adj_Cent : Integer := 0; Date_N : Time_Rep;
Adj_Date_N : Time; Offset : aliased long;
Adj_Date_S : Time; Secs_T : aliased time_t;
Offset : aliased long; Secs_TM : aliased tm;
Secs_T : aliased time_t;
Secs_TM : aliased tm;
begin begin
Adj_Date_N := Date; Date_N := Time_Rep (Date);
-- Dates which are 56 years appart fall on the same day, day light -- 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 -- saving and so on. Non-leap centenial years violate this rule by
-- one day and as a consequence, special adjustment is needed. -- one day and as a consequence, special adjustment is needed.
if Adj_Date_N > T_2100_2_28 then if Date_N > T_2100_2_28 then
if Adj_Date_N > T_2200_2_28 then if Date_N > T_2200_2_28 then
if Adj_Date_N > T_2300_2_28 then if Date_N > T_2300_2_28 then
Adj_Cent := 3; Adj_Cent := 3;
else else
Adj_Cent := 2; Adj_Cent := 2;
...@@ -1440,25 +1335,26 @@ package body Ada.Calendar is ...@@ -1440,25 +1335,26 @@ package body Ada.Calendar is
end if; end if;
if Adj_Cent > 0 then 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; 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 Date_N < Unix_Min loop
while Adj_Date_S < Unix_Min loop Date_N := Date_N + Nanos_In_56_Years;
Adj_Date_S := Adj_Date_S + Secs_In_56_Years;
end loop; end loop;
while Adj_Date_S >= Unix_Max loop while Date_N >= Unix_Max loop
Adj_Date_S := Adj_Date_S - Secs_In_56_Years; Date_N := Date_N - Nanos_In_56_Years;
end loop; end loop;
-- Perform a shift in origins from Ada to Unix -- 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 localtime_tzoff
(Secs_T'Unchecked_Access, (Secs_T'Unchecked_Access,
...@@ -1476,67 +1372,69 @@ begin ...@@ -1476,67 +1372,69 @@ begin
-- Population of the leap seconds table -- Population of the leap seconds table
declare if Leap_Support then
type Leap_Second_Date is record declare
Year : Year_Number; type Leap_Second_Date is record
Month : Month_Number; Year : Year_Number;
Day : Day_Number; Month : Month_Number;
end record; Day : Day_Number;
end record;
Leap_Second_Dates :
constant array (1 .. Leap_Seconds_Count) of Leap_Second_Date := Leap_Second_Dates :
((1972, 6, 30), (1972, 12, 31), (1973, 12, 31), (1974, 12, 31), constant array (1 .. Leap_Seconds_Count) of Leap_Second_Date :=
(1975, 12, 31), (1976, 12, 31), (1977, 12, 31), (1978, 12, 31), ((1972, 6, 30), (1972, 12, 31), (1973, 12, 31), (1974, 12, 31),
(1979, 12, 31), (1981, 6, 30), (1982, 6, 30), (1983, 6, 30), (1975, 12, 31), (1976, 12, 31), (1977, 12, 31), (1978, 12, 31),
(1985, 6, 30), (1987, 12, 31), (1989, 12, 31), (1990, 12, 31), (1979, 12, 31), (1981, 6, 30), (1982, 6, 30), (1983, 6, 30),
(1992, 6, 30), (1993, 6, 30), (1994, 6, 30), (1995, 12, 31), (1985, 6, 30), (1987, 12, 31), (1989, 12, 31), (1990, 12, 31),
(1997, 6, 30), (1998, 12, 31), (2005, 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_In_Four_Years : constant := 365 * 3 + 366;
Days : Natural;
Leap : Leap_Second_Date; Days : Natural;
Years : Natural; Leap : Leap_Second_Date;
Years : Natural;
begin begin
for Index in 1 .. Leap_Seconds_Count loop for Index in 1 .. Leap_Seconds_Count loop
Leap := Leap_Second_Dates (Index); Leap := Leap_Second_Dates (Index);
-- Calculate the number of days from the start of Ada time until -- Calculate the number of days from the start of Ada time until
-- the current leap second occurence. Non-leap centenial years -- the current leap second occurence. Non-leap centenial years
-- are not accounted for in these calculations since there are -- are not accounted for in these calculations since there are
-- no leap seconds after 2100 yet. -- no leap seconds after 2100 yet.
Years := Leap.Year - Ada_Min_Year; Years := Leap.Year - Ada_Min_Year;
Days := (Years / 4) * Days_In_Four_Years; Days := (Years / 4) * Days_In_Four_Years;
Years := Years mod 4; Years := Years mod 4;
if Years = 1 then if Years = 1 then
Days := Days + 365; Days := Days + 365;
elsif Years = 2 then elsif Years = 2 then
Days := Days + 365 * 2; Days := Days + 365 * 2;
elsif Years = 3 then elsif Years = 3 then
Days := Days + 365 * 3; Days := Days + 365 * 3;
end if; end if;
Days := Days + Cumulative_Days_Before_Month (Leap.Month); Days := Days + Cumulative_Days_Before_Month (Leap.Month);
if Is_Leap (Leap.Year) if Is_Leap (Leap.Year)
and then Leap.Month > 2 and then Leap.Month > 2
then then
Days := Days + 1; Days := Days + 1;
end if; end if;
Days := Days + Leap.Day; Days := Days + Leap.Day;
-- Index - 1 previous leap seconds are added to Time (Index) as -- Index - 1 previous leap seconds are added to Time (Index) as
-- well as the lower buffer for time zones. -- well as the lower buffer for time zones.
Leap_Second_Times (Index) := Ada_Low + Leap_Second_Times (Index) := Ada_Low +
(Time (Days) * Secs_In_Day + Time (Index - 1)) * Nano; (Time_Rep (Days) * Secs_In_Day + Time_Rep (Index - 1)) * Nano;
end loop; end loop;
end; end;
end if;
end Ada.Calendar; end Ada.Calendar;
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
-- -- -- --
-- S p e c -- -- S p e c --
-- -- -- --
-- Copyright (C) 1992-2006, Free Software Foundation, Inc. -- -- Copyright (C) 1992-2007, Free Software Foundation, Inc. --
-- -- -- --
-- This specification is derived from the Ada Reference Manual for use with -- -- This specification is derived from the Ada Reference Manual for use with --
-- GNAT. The copyright notice above, and the license provisions that follow -- -- GNAT. The copyright notice above, and the license provisions that follow --
...@@ -53,7 +53,7 @@ package Ada.Calendar is ...@@ -53,7 +53,7 @@ package Ada.Calendar is
function Clock return Time; function Clock return Time;
-- The returned time value is the number of nanoseconds since the start -- The returned time value is the number of nanoseconds since the start
-- of Ada time (1901-1-1 0.0 GMT). -- of Ada time (1901-01-01 00:00:00.0 UTC).
function Year (Date : Time) return Year_Number; function Year (Date : Time) return Year_Number;
function Month (Date : Time) return Month_Number; function Month (Date : Time) return Month_Number;
...@@ -68,8 +68,8 @@ package Ada.Calendar is ...@@ -68,8 +68,8 @@ package Ada.Calendar is
Seconds : out Day_Duration); Seconds : out Day_Duration);
-- Break down a time value into its date components set in the current -- Break down a time value into its date components set in the current
-- time zone. If Split is called on a time value created using Ada 2005 -- time zone. If Split is called on a time value created using Ada 2005
-- Time_Of in some arbitrary time zone, the input value always will be -- Time_Of in some arbitrary time zone, the input value will always be
-- interpreted as some point in time relative to the local time zone. -- interpreted as relative to the local time zone.
function Time_Of function Time_Of
(Year : Year_Number; (Year : Year_Number;
...@@ -96,8 +96,8 @@ package Ada.Calendar is ...@@ -96,8 +96,8 @@ package Ada.Calendar is
function "-" (Left : Time; Right : Duration) return Time; function "-" (Left : Time; Right : Duration) return Time;
function "-" (Left : Time; Right : Time) return Duration; function "-" (Left : Time; Right : Time) return Duration;
-- The first three functions will raise Time_Error if the resulting time -- The first three functions will raise Time_Error if the resulting time
-- value is less than the start of Ada time in GMT or greater than the -- value is less than the start of Ada time in UTC or greater than the
-- end of Ada time in GMT. The last function will raise Time_Error if the -- end of Ada time in UTC. The last function will raise Time_Error if the
-- resulting difference cannot fit into a duration value. -- resulting difference cannot fit into a duration value.
function "<" (Left, Right : Time) return Boolean; function "<" (Left, Right : Time) return Boolean;
...@@ -135,16 +135,16 @@ private ...@@ -135,16 +135,16 @@ private
-- Implementation of Time -- -- Implementation of Time --
---------------------------- ----------------------------
-- Time is represented as an unsigned 64 bit integer count of nanoseconds -- Time is represented as a signed 64 bit integer count of nanoseconds
-- since the start of Ada time (1901-1-1 0.0 GMT). Time values produced -- since the start of Ada time (1901-01-01 00:00:00.0 UTC). Time values
-- by Time_Of are internaly normalized to GMT regardless of their local -- produced by Time_Of are internaly normalized to UTC regardless of their
-- time zone. This representation ensures correct handling of leap seconds -- local time zone. This representation ensures correct handling of leap
-- as well as performing arithmetic. In Ada 95, Split will treat a time -- seconds as well as performing arithmetic. In Ada 95, Split and Time_Of
-- value as being in the local time zone and break it down accordingly. -- will treat a time value as being in the local time zone, in Ada 2005,
-- In Ada 2005, Split will treat a time value as being in the designated -- Split and Time_Of will treat a time value as being in the designated
-- time zone by the corresponding formal parameter or in GMT by default. -- time zone by the formal parameter or in UTC by default. The size of the
-- The size of the type is large enough to cover the Ada 2005 range of -- type is large enough to cover the Ada 2005 range of time (1901-01-01
-- time (1901-1-1 0.0 GMT - 2399-12-31-86_399.999999999 GMT). -- 00:00:00.0 UTC - 2399-12-31-23:59:59.999999999 UTC).
------------------ ------------------
-- Leap seconds -- -- Leap seconds --
...@@ -155,17 +155,19 @@ private ...@@ -155,17 +155,19 @@ private
-- leap second is added after the last day of June or December. The count -- leap second is added after the last day of June or December. The count
-- of seconds during those occurences becomes: -- of seconds during those occurences becomes:
-- ... 58, 59, leap second 60, 1, 2 ... -- ... 58, 59, leap second 60, 0, 1, 2 ...
-- Unlike leap days, leap seconds occur simultaneously around the world. -- Unlike leap days, leap seconds occur simultaneously around the world.
-- In other words, if a leap second occurs at 23:59:60 GMT, it also occurs -- In other words, if a leap second occurs at 23:59:60 UTC, it also occurs
-- on 18:59:60 -5 or 2:59:60 +2 on the next day. -- on 18:59:60 -5 the same day or 2:59:60 +2 on the next day.
-- Leap seconds do not follow a formula. The International Earth Rotation -- Leap seconds do not follow a formula. The International Earth Rotation
-- and Reference System Service decides when to add one. Leap seconds are -- and Reference System Service decides when to add one. Leap seconds are
-- included in the representation of time in Ada 95 mode. As a result, -- included in the representation of time in Ada 95 mode. As a result,
-- the following two time values will conceptually differ by two seconds: -- the following two time values will differ by two seconds:
-- Time_Of (1972, 7, 1, 0.0) - Time_Of (1972, 6, 30, 86_399.0) = 2 secs -- 1972-06-30 23:59:59.0
-- 1972-07-01 00:00:00.0
-- When a new leap second is added, the following steps must be carried -- When a new leap second is added, the following steps must be carried
-- out: -- out:
...@@ -185,41 +187,14 @@ private ...@@ -185,41 +187,14 @@ private
-- non-leap. As a consequence, seven non-leap years occur over the period -- non-leap. As a consequence, seven non-leap years occur over the period
-- of year - 4 to year + 4. Internaly, routines Split and Time_Of add or -- of year - 4 to year + 4. Internaly, routines Split and Time_Of add or
-- subtract a "fake" February 29 to facilitate the arithmetic involved. -- subtract a "fake" February 29 to facilitate the arithmetic involved.
-- This small "cheat" remains hidden and the following calculations do
-- produce the correct difference.
-- Time_Of (2100, 3, 1, 0.0) - Time_Of (2100, 2, 28, 0.0) = 1 day -- The underlying type of Time has been chosen to be a 64 bit signed
-- Time_Of (2101, 1, 1, 0.0) - Time_Of (2100, 12, 31, 0.0) = 1 day -- integer number since it allows for easier processing of sub seconds
-- and arithmetic.
type Time_Rep is mod 2 ** 64; type Time_Rep is range -2 ** 63 .. +2 ** 63 - 1;
type Time is new Time_Rep; type Time is new Time_Rep;
-- Due to boundary time values and time zones, two days of buffer space
-- are set aside at both end points of Ada time:
-- Abs zero Hard low Soft low Soft high Hard high
-- +---------+============+#################+============+----------->
-- Buffer 1 Real Ada time Buffer 2
-- A time value in a any time zone may not excede the hard bounds of Ada
-- time, while a value in GMT may not go over the soft bounds.
Buffer_D : constant Duration := 2.0 * Secs_In_Day;
Buffer_N : constant Time := 2 * Nanos_In_Day;
-- Lower and upper bound of Ada time shifted by two days from the absolute
-- zero. Note that the upper bound includes the non-leap centenial years.
Ada_Low : constant Time := Buffer_N;
Ada_High : constant Time := (121 * 366 + 378 * 365) * Nanos_In_Day +
Buffer_N;
-- Both of these hard bounds are 28 hours before and after their regular
-- counterpart. The value of 28 is taken from Ada.Calendar.Time_Zones.
Hard_Ada_Low : constant Time := Ada_Low - 100_800 * Nano;
Hard_Ada_High : constant Time := Ada_High + 100_800 * Nano;
Days_In_Month : constant array (Month_Number) of Day_Number := Days_In_Month : constant array (Month_Number) of Day_Number :=
(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
...@@ -234,7 +209,7 @@ private ...@@ -234,7 +209,7 @@ private
package Arithmetic_Operations is package Arithmetic_Operations is
function Add (Date : Time; Days : Long_Integer) return Time; function Add (Date : Time; Days : Long_Integer) return Time;
-- Add X number of days to a time value -- Add a certain number of days to a time value
procedure Difference procedure Difference
(Left : Time; (Left : Time;
...@@ -248,11 +223,11 @@ private ...@@ -248,11 +223,11 @@ private
-- values are positive, negative otherwise. -- values are positive, negative otherwise.
function Subtract (Date : Time; Days : Long_Integer) return Time; function Subtract (Date : Time; Days : Long_Integer) return Time;
-- Subtract X number of days from a time value -- Subtract a certain number of days from a time value
end Arithmetic_Operations; end Arithmetic_Operations;
package Delays_Operations is package Delays_Operations is
function To_Duration (Ada_Time : Time) return Duration; function To_Duration (Date : Time) return Duration;
-- Given a time value in nanoseconds since 1901, convert it into a -- Given a time value in nanoseconds since 1901, convert it into a
-- duration value giving the number of nanoseconds since the Unix Epoch. -- duration value giving the number of nanoseconds since the Unix Epoch.
end Delays_Operations; end Delays_Operations;
...@@ -263,18 +238,21 @@ private ...@@ -263,18 +238,21 @@ private
-- within the range of 0 .. 6 (Monday .. Sunday). -- within the range of 0 .. 6 (Monday .. Sunday).
procedure Split procedure Split
(Date : Time; (Date : Time;
Year : out Year_Number; Year : out Year_Number;
Month : out Month_Number; Month : out Month_Number;
Day : out Day_Number; Day : out Day_Number;
Day_Secs : out Day_Duration; Day_Secs : out Day_Duration;
Hour : out Integer; Hour : out Integer;
Minute : out Integer; Minute : out Integer;
Second : out Integer; Second : out Integer;
Sub_Sec : out Duration; Sub_Sec : out Duration;
Leap_Sec : out Boolean; Leap_Sec : out Boolean;
Time_Zone : Long_Integer); Is_Ada_05 : Boolean;
-- Split a time value into its components Time_Zone : Long_Integer);
-- Split a time value into its components. Set Is_Ada_05 to use the
-- local time zone (the value in Time_Zone is ignored) when splitting
-- a time value.
function Time_Of function Time_Of
(Year : Year_Number; (Year : Year_Number;
...@@ -286,19 +264,20 @@ private ...@@ -286,19 +264,20 @@ private
Second : Integer; Second : Integer;
Sub_Sec : Duration; Sub_Sec : Duration;
Leap_Sec : Boolean; Leap_Sec : Boolean;
Leap_Checks : Boolean;
Use_Day_Secs : Boolean; Use_Day_Secs : Boolean;
Is_Ada_05 : Boolean;
Time_Zone : Long_Integer) return Time; Time_Zone : Long_Integer) return Time;
-- Given all the components of a date, return the corresponding time -- Given all the components of a date, return the corresponding time
-- value. Set Use_Day_Secs to use the value in Day_Secs, otherwise the -- value. Set Use_Day_Secs to use the value in Day_Secs, otherwise the
-- day duration will be calculated from Hour, Minute, Second and Sub_ -- day duration will be calculated from Hour, Minute, Second and Sub_
-- Sec. Set flag Leap_Checks to verify the validity of a leap second. -- Sec. Set Is_Ada_05 to use the local time zone (the value in formal
-- Time_Zone is ignored) when building a time value and to verify the
-- validity of a requested leap second.
end Formatting_Operations; end Formatting_Operations;
package Time_Zones_Operations is package Time_Zones_Operations is
function UTC_Time_Offset (Date : Time) return Long_Integer; function UTC_Time_Offset (Date : Time) return Long_Integer;
-- Return the offset in seconds from GMT -- Return the offset in seconds from UTC
end Time_Zones_Operations; end Time_Zones_Operations;
end Ada.Calendar; end Ada.Calendar;
...@@ -457,7 +457,18 @@ package body Ada.Calendar.Formatting is ...@@ -457,7 +457,18 @@ package body Ada.Calendar.Formatting is
begin begin
Formatting_Operations.Split Formatting_Operations.Split
(Date, Year, Month, Day, Seconds, H, M, Se, Su, Leap_Second, Tz); (Date => Date,
Year => Year,
Month => Month,
Day => Day,
Day_Secs => Seconds,
Hour => H,
Minute => M,
Second => Se,
Sub_Sec => Su,
Leap_Sec => Leap_Second,
Time_Zone => Tz,
Is_Ada_05 => True);
-- Validity checks -- Validity checks
...@@ -491,8 +502,18 @@ package body Ada.Calendar.Formatting is ...@@ -491,8 +502,18 @@ package body Ada.Calendar.Formatting is
begin begin
Formatting_Operations.Split Formatting_Operations.Split
(Date, Year, Month, Day, Dd, (Date => Date,
Hour, Minute, Second, Sub_Second, Le, Tz); Year => Year,
Month => Month,
Day => Day,
Day_Secs => Dd,
Hour => Hour,
Minute => Minute,
Second => Second,
Sub_Sec => Sub_Second,
Leap_Sec => Le,
Time_Zone => Tz,
Is_Ada_05 => True);
-- Validity checks -- Validity checks
...@@ -529,8 +550,18 @@ package body Ada.Calendar.Formatting is ...@@ -529,8 +550,18 @@ package body Ada.Calendar.Formatting is
begin begin
Formatting_Operations.Split Formatting_Operations.Split
(Date, Year, Month, Day, Dd, (Date => Date,
Hour, Minute, Second, Sub_Second, Leap_Second, Tz); Year => Year,
Month => Month,
Day => Day,
Day_Secs => Dd,
Hour => Hour,
Minute => Minute,
Second => Second,
Sub_Sec => Sub_Second,
Leap_Sec => Leap_Second,
Time_Zone => Tz,
Is_Ada_05 => True);
-- Validity checks -- Validity checks
...@@ -621,10 +652,17 @@ package body Ada.Calendar.Formatting is ...@@ -621,10 +652,17 @@ package body Ada.Calendar.Formatting is
return return
Formatting_Operations.Time_Of Formatting_Operations.Time_Of
(Adj_Year, Adj_Month, Adj_Day, Seconds, H, M, Se, Ss, (Year => Adj_Year,
Month => Adj_Month,
Day => Adj_Day,
Day_Secs => Seconds,
Hour => H,
Minute => M,
Second => Se,
Sub_Sec => Ss,
Leap_Sec => Leap_Second, Leap_Sec => Leap_Second,
Leap_Checks => True,
Use_Day_Secs => True, Use_Day_Secs => True,
Is_Ada_05 => True,
Time_Zone => Tz); Time_Zone => Tz);
end Time_Of; end Time_Of;
...@@ -663,10 +701,17 @@ package body Ada.Calendar.Formatting is ...@@ -663,10 +701,17 @@ package body Ada.Calendar.Formatting is
return return
Formatting_Operations.Time_Of Formatting_Operations.Time_Of
(Year, Month, Day, Dd, Hour, Minute, Second, Sub_Second, (Year => Year,
Month => Month,
Day => Day,
Day_Secs => Dd,
Hour => Hour,
Minute => Minute,
Second => Second,
Sub_Sec => Sub_Second,
Leap_Sec => Leap_Second, Leap_Sec => Leap_Second,
Leap_Checks => True,
Use_Day_Secs => False, Use_Day_Secs => False,
Is_Ada_05 => True,
Time_Zone => Tz); Time_Zone => Tz);
end Time_Of; end Time_Of;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment