-- Implementation of Time --
----------------------------
- -- Time is represented as a signed 64 bit integer count of nanoseconds
- -- since the start of Ada time (1901-01-01 00:00:00.0 UTC). Time values
- -- produced by Time_Of are internally normalized to UTC regardless of their
- -- local time zone. This representation ensures correct handling of leap
- -- seconds as well as performing arithmetic. In Ada 95, Split and Time_Of
- -- will treat a time value as being in the local time zone, in Ada 2005,
- -- Split and Time_Of will treat a time value as being in the designated
- -- time zone by the formal parameter or in UTC by default. The size of the
- -- type is large enough to cover the Ada 2005 range of time (1901-01-01
- -- 00:00:00.0 UTC - 2399-12-31-23:59:59.999999999 UTC).
+ -- Time is represented as a signed 64 bit signed integer count of
+ -- nanoseconds since the "epoch" 2150-01-01 00:00:00 UTC. Thus a value of 0
+ -- represents the epoch. As of this writing, the epoch is in the future,
+ -- so Time values returned by Clock will be negative.
+ --
+ -- Time values produced by Time_Of are internally normalized to UTC
+ -- regardless of their local time zone. This representation ensures correct
+ -- handling of leap seconds as well as performing arithmetic. In Ada 95,
+ -- Split and Time_Of will treat a time value as being in the local time
+ -- zone, in Ada 2005, Split and Time_Of will treat a time value as being in
+ -- the designated time zone by the formal parameter or in UTC by
+ -- default. The size of the type is large enough to cover the Ada
+ -- range of time (1901-01-01T00:00:00.0 UTC - 2399-12-31T23:59:59.999999999
+ -- UTC).
------------------
-- Leap Seconds --
function Epoch_Offset return Time_Rep;
pragma Inline (Epoch_Offset);
- -- Return the difference between 2150-1-1 UTC and 1970-1-1 UTC expressed in
- -- nanoseconds. Note that year 2100 is non-leap.
+ -- Return the difference between our epoch and 1970-1-1 UTC (the Unix
+ -- epoch) expressed in nanoseconds. Note that year 2100 is non-leap.
Days_In_Month : constant array (Month_Number) of Day_Number :=
(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
------------------------------------------------------------------------------
with Ada.Calendar; use Ada.Calendar;
+with Ada.Calendar.Time_Zones;
with Ada.Characters.Handling;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Text_IO;
Length : Natural := 0) return String;
-- As above with N provided in Integer format
- procedure Parse_ISO_8861_UTC
+ procedure Parse_ISO_8601
(Date : String;
Time : out Ada.Calendar.Time;
Success : out Boolean);
-- Subsidiary of function Value. It parses the string Date, interpreted as
- -- an ISO 8861 time representation, and returns corresponding Time value.
- -- Success is set to False when the string is not a supported ISO 8861
- -- date. The following regular expression defines the supported format:
- --
- -- (yyyymmdd | yyyy'-'mm'-'dd)'T'(hhmmss | hh':'mm':'ss)
- -- [ ('Z' | ('.' | ',') s{s} | ('+'|'-')hh':'mm) ]
- --
- -- Trailing characters (in particular spaces) are not allowed.
+ -- an ISO 8601 time representation, and returns corresponding Time value.
+ -- Success is set to False when the string is not a supported ISO 8601
+ -- date.
--
-- Examples:
--
return Abbrev_Upper_Month_Names'First;
end Month_Name_To_Number;
- ------------------------
- -- Parse_ISO_8861_UTC --
- ------------------------
+ --------------------
+ -- Parse_ISO_8601 --
+ --------------------
- procedure Parse_ISO_8861_UTC
+ procedure Parse_ISO_8601
(Date : String;
Time : out Ada.Calendar.Time;
Success : out Boolean)
-- Local variables
+ use Time_Zones;
+
Date_Separator : constant Character := '-';
Hour_Separator : constant Character := ':';
Local_Hour : Hour_Number := 0;
Local_Minute : Minute_Number := 0;
Local_Sign : Character := ' ';
- Local_Disp : Duration;
+ Time_Zone : Time_Offset; -- initialized when Local_Sign is set
Sep_Required : Boolean := False;
-- True if a separator is seen (and therefore required after it!)
if Index <= Date'Last then
- -- Suffix 'Z' just confirms that this is an UTC time. No further
- -- action needed.
+ -- Suffix 'Z' signifies that this is UTC time (time zone 0)
if Symbol = 'Z' then
+ Local_Sign := '+';
+ Time_Zone := 0;
Advance;
-- A decimal fraction shall have at least one digit, and has as
-- many digits as supported by the underlying implementation.
-- The valid decimal separators are those specified in ISO 31-0,
-- i.e. the comma [,] or full stop [.]. Of these, the comma is
- -- the preferred separator of ISO-8861.
+ -- the preferred separator of ISO-8601.
elsif Symbol = ',' or else Symbol = '.' then
Advance; -- past decimal separator
-- Compute local displacement
- Local_Disp := Local_Hour * 3600.0 + Local_Minute * 60.0;
+ Time_Zone := Time_Offset (Local_Hour * 60 + Local_Minute);
+
+ if Local_Sign = '-' then
+ Time_Zone := -Time_Zone;
+ end if;
else
raise Wrong_Syntax;
end if;
raise Wrong_Syntax;
end if;
- -- Compute time without local displacement
+ -- If no time zone was specified, we call GNAT.Calendar.Time_Of, which
+ -- uses local time. Otherwise, we use Ada.Calendar.Formatting.Time_Of
+ -- and specify the time zone.
if Local_Sign = ' ' then
- Time := Time_Of (Year, Month, Day, Hour, Minute, Second, Subsec);
-
- -- Compute time with positive local displacement
-
- elsif Local_Sign = '+' then
- Time :=
- Time_Of (Year, Month, Day, Hour, Minute, Second, Subsec) -
- Local_Disp;
-
- -- Compute time with negative local displacement
-
- elsif Local_Sign = '-' then
- Time :=
- Time_Of (Year, Month, Day, Hour, Minute, Second, Subsec) +
- Local_Disp;
+ Time := GNAT.Calendar.Time_Of
+ (Year, Month, Day, Hour, Minute, Second, Subsec);
+ else
+ Time := Ada.Calendar.Formatting.Time_Of
+ (Year, Month, Day, Hour, Minute, Second, Subsec,
+ Time_Zone => Time_Zone);
end if;
-- Notify that the input string was successfully parsed
Time :=
Time_Of (Year_Number'First, Month_Number'First, Day_Number'First);
Success := False;
- end Parse_ISO_8861_UTC;
+ end Parse_ISO_8601;
-----------
-- Value --
-- Start of processing for Value
begin
- -- Let's try parsing Date as a supported ISO-8861 format. If we do not
+ -- Let's try parsing Date as a supported ISO-8601 format. If we do not
-- succeed, then retry using all the other GNAT supported formats.
- Parse_ISO_8861_UTC (Date, Time, Success);
+ Parse_ISO_8601 (Date, Time, Success);
if Success then
return Time;
-- Length checks
- if D_Length /= 8
- and then D_Length /= 10
- and then D_Length /= 11
- and then D_Length /= 12
- and then D_Length /= 17
- and then D_Length /= 19
- and then D_Length /= 20
- and then D_Length /= 21
- then
+ if D_Length not in 8 | 10 | 11 | 12 | 17 | 19 | 20 | 21 then
raise Constraint_Error;
end if;