Version 1.4 of ai05s/ai05-0119-1.txt

Unformatted version of ai05s/ai05-0119-1.txt version 1.4
Other versions for file ai05s/ai05-0119-1.txt

!standard 9.6(24)          08-10-17 AI05-0119-1/00
!standard 9.6.1(41/2)
!standard 9.6.1(42/2)
!class Amendment 08-10-17
!status work item 08-10-17
!status received 08-08-05
!priority Medium
!difficulty Hard
!subject Package Calendar, Daylight Savings Time, and UTC_Offset
!summary
(See proposal.)
!problem
Ada.Calendar time is not well-defined in the presence of daylight saving time adjustments.
1. Ada.Calendar.Time_Of is ill-defined within the hour when the clock is adjusted backwards. The same year-month-day-seconds quadruple may denote two different dates. For example:
year => 2008, month => 10, day => 26, seconds => 2.5 * 3600.0
may refer in Central Europe to 02:30 before the clock adjustment (02:30 CEST) or else a date 1 hour later, after the clock adjustment (02:30 CET).
2. The semantics of Ada.Calendar.Time is not defined. The following example illustrates the case:
T0, T1, T2 : Time;
begin
T0 := Time_Of - 01:30 CEST ( Year => 2008, Day => 26, Month => 10, Seconds => 1.5 * 3600.0 ); T1 := T0 + 3600.0; -- +1h T2 := T0 + 2.0 * 3600.0; -- +2h
The Standard does not clarify whether T1 = T2 or not. A similar question can be asked about the time retrieved by Ada.Calendar.Clock at the times T1 and T2. (The answer may not be the same.)
3. Ada.Calendar.Time_Zones.UTC_Offset function is ill-defined because of the issue of 2. If T1 = T2, then UTC_Offset is broken because T1 is CEST and T2 is CET. These have different offsets to the UTC time, +2h and +1h correspondingly.
!proposal
** TBD **
!wording
** TBD **
!discussion
[Editor's note: To think about this in the US, use the time November 02, 2008 01:30 AM as the clock will be adjusted at 2:00 AM that day.]
The basic problem is that a number of characteristics of Ada.Calendar were left unspecified. This is problematical, because it means that applications that depend on the clock (especially those that require UTC time) are not portable and may break occassionally when the clock is adjusted (depending on the implementation model.
A number of fixes are possible:
(1) Confirm the standard that these things are undefined. This surely won't break anything, but it also means that there is no way to write some applications in Ada (Ada.Real_Time does not have sufficient range to be used as a replacement).
(2) Change the definition of Ada.Calendar.Time to ensure that some indication of the time zone and adjustment is included. Then operations like these can be defined unambigously. But that changes the representation of Ada.Calendar.Time on many implementations.
(3) Define that Ada.Calendar.Time is UTC_Time, and provide a function to get local time. That's what Ada 83 ought to have done, but it seems too incompatable to change now.
(4) Add wording to 9.6.1(41/2) to specify which result is returned in ambiguous cases. But that really doesn't help much, because there is then an hour of UTC time that can never occur.
(5) Create a parallel universe of a UTC_Time package which works right. But that seems amazingly heavy.
(6) A simplified proposal like the one found in the e-mail attached here. But that means that for most operations, UTC.Time has to be converted to Calendar.Time (since UTC.Time does not have constructors or splitters), which would clutter programs.
There probably are even more approaches necessary. But first we have to decide whether to actually fix this problem.
!example
!ACATS test
!appendix

From: Dmitry A. Kazakov
Date: Tuesday, August 5, 2008  3:57 AM

1. What is the result (according to the language reference manual) of:

   UTC_Time_Offset (Ada.Calendar.Time_Of (2008, 26, 10, 2.5*60.0*60.0));

called on a machine with the time zone Amsterdam, Berlin, Bern...

Note. 26.10.2008 is the day when the clock will be adjusted 1h backward at 03:00.

2. What is the result of Ada.Calendar.Split applied to Ada.Calendar.Clock
called:

2.a. at 26.10.2008 02:30 before clock adjustment; 2.b. one hour later
(at 26.10.2008 02:30 after clock adjustment).

-------------------
UTC_Time_Offset and Time_Of are ambiguous as defined in the RM.

RM 9.6.1 42/2:

"Returns, as a number of minutes, the difference between the
implementation-defined time zone of Calendar, and UTC time, at the time Date."

It is not a function.

RM 9.6 24/2:

"The functions Year, Month, Day, and Seconds return the corresponding values
for a given value of the type Time, as appropriate to an
implementation-defined time zone; the procedure Split returns all four
corresponding values. Conversely, the function Time_Of combines a year number,
a month number, a day number, and a duration, into a value of type Time."

Split cannot be "converse," because it is irreversible.

****************************************************************

From: Jeffrey R. Carter
Date: Wednesday, August 6, 2008  2:16 PM

> 1. What is the result (according to the language reference manual) of:
>
>    UTC_Time_Offset (Ada.Calendar.Time_Of (2008, 26, 10,
> 2.5*60.0*60.0));
>
> called on a machine with the time zone Amsterdam, Berlin, Bern...

This depends on the definition of the user-defined procedure UTC_Time_Offset.

Presuming "use Ada.Calendar.Time_Zones;" and no user-defined procedure
UTC_Time_Offset, a compiler error message, since
Ada.Calendar.Time_Zones.UTC_Time_Offset is a function, and this is a
procedure call.

Presuming further that this is a fragment and does represent a function call,
Constraint_Error should be raised at the point of the call, because 26
is not a value of Ada.Calendar.Month_Number.

****************************************************************

From: Christoph Grein
Date: Wednesday, August 6, 2008  11:47 PM

I think your answer is a bit saucy, at least a bad joke.
The question is absolute clear for any non-malevolent person :-(

The RM is unclear about this, and the AARM (42.b/2, c/2) admits this.

****************************************************************

From: Jeffery R. Carter
Date: Thursday, August 7, 2008  2:09 PM

> I think your answer is a bit saucy, at least a bad joke.
> The question is absolute clear for any non-malevolent person :-(

Perhaps. Is it unreasonable to expect precision from someone who is
asking the ARG for precision?

> The RM is unclear about this, and the AARM (42.b/2, c/2) admits this.

I'm working on an application that deals with times in different time
zones, though we never have to deal with times when the clock changes.
I've found that understanding and reasoning about the definitions for
dealing with time zones in the ARM requires careful attention, but the
conclusions I've reached have agreed with the GNAT implementation so far.

****************************************************************

From: Pascal Leroy
Date: Thursday, August 7, 2008  3:06 AM

> RM 9.6.1 42/2:

> "Returns, as a number of minutes, the difference between the
> implementation-defined time zone of Calendar, and UTC time, at the time
> Date."
>
> It is not a function.

I think that 42/2 is fine because UTC_Time_Offset *is* a function.
But 44/2 could be worded better because Difference is *not* a function.
Perhaps "Computes the difference between...".

> RM 9.6 24/2:
>
> "The functions Year, Month, Day, and Seconds return the corresponding
> values for a given value of the type Time, as appropriate to an
> implementation-defined time zone; the procedure Split returns all four
> corresponding values. Conversely, the function Time_Of combines a year
> number, a month number, a day number, and a duration, into a value of type
> Time."
>
> Split cannot be "converse," because it is irreversible.

I think you are reading too much in the word "conversely".  I don't
believe that it was intended to imply reversibility.

****************************************************************

From: Dmitry A. Kazakov
Date: Thursday, August 7, 2008  3:30 PM

> I think that 42/2 is fine because UTC_Time_Offset *is* a function.
> But 44/2 could be worded better because Difference is *not* a
> function.  Perhaps "Computes the difference between...".

That does not compute either. Because "the difference" implies that
there is exactly one result for each argument, which is equivalent of
being a mathematical function.

...

> I think you are reading too much in the word "conversely".  I don't
> believe that it was intended to imply reversibility.

If that is true, then the text becomes meaningless: "four corresponding
values" of what? How they correspond etc.

Basically the texts are inconsistent. Either:

1. Ada.Calendar.Time is defined to have different values (in the sense
of the operation "=") at 26.10.2008 02:30 before the clock skew and after
it. In this case Time_Of cannot be defined as it was, without specifying
with of two dates is meant.

2. Ada.Calendar.Time has same values at both times, then Time_Of is
well-defined but UTC_Time_Offset is not a function of the argument.

Now which variant is mandatory? And what is the semantics of ill-defined
things?

P.S. As for existing implementations. GNAT under Windows seems to use the
variant 1, with Time_Of returning the time after the skew.

****************************************************************

From: Adam Beneschan
Date: Thursday, August 7, 2008  7:01 PM

> > I think that 42/2 is fine because UTC_Time_Offset *is* a function.
> > But 44/2 could be worded better because Difference is *not* a
> > function.  Perhaps "Computes the difference between...".
>
> That does not compute either. Because "the difference" implies that
> there is exactly one result for each argument, which is equivalent of
> being a mathematical function.

But there *is* one "difference".  The part that returns "the difference"
is a mathematical function.  The result of this function is then broken
into three parts and returned as three OUT parameters.
I don't see the problem---it seems clear to me.  And Ada-Comment regulars
are aware that I'm a major nit-picker whenever I see wording in the RM
that I think doesn't say what it means, or is ambiguous.
But this paragraph doesn't bother me at all, although I can see why
"Returns the difference" appears a little odd since Difference isn't an
Ada function.

...
> > I think you are reading too much in the word "conversely".  I don't
> > believe that it was intended to imply reversibility.
>
> If that is true, then the text becomes meaningless: "four
> corresponding values" of what? How they correspond etc.

It means that Split returns four OUT parameters, "Year", "Month", "Day",
and "Seconds"; and these OUT parameters are set to the same values that
the functions of the same names would return.  Thus, Split (Date, Y, M, D, S)
sets Y := Year(Date), M := Month(Date), and so on.
That's what "corresponding" means.  It has nothing whatsoever to do with the
second sentence of paragraph 24, nor of the Time_Of function.

Your issues regarding how this package works around Daylight Savings Time
changes point out a real problem.  But the questions about wording that you're
asking above don't have anything to do with this issue.

****************************************************************

From: Dmitry A. Kazakov
Date: Friday, August 8, 2008  3:09 AM

...
>> That does not compute either. Because "the difference" implies that
>> there is exactly one result for each argument, which is equivalent of
>> being a mathematical function.
>
> But there *is* one "difference".

No, I meant UTC_Time_Offset, which depends on the definition of the
political time. The customary, naive, definition seen in texts refers
to "clock adjustment." This reads to me as 26.10.2008 02:30 would be
exactly the same Berlin time before and after the clock skew. Otherwise,
I don't know what the [political] clock does show. In my previous post
it was denoted as the variant 2. The variant 1 referred to a hidden UTC
clock which is inconsistent with Time_Of.

[As well as with political time, in general. I greatly welcome GNAT's
implementation dumping political time, but was it the intention of RM?
I thought it wished to preserve political time...]

...
> It means that Split returns four OUT parameters, "Year", "Month",
> "Day", and "Seconds"; and these OUT parameters are set to the same
> values that the functions of the same names would return.  Thus, Split
> (Date, Y, M, D, S) sets Y := Year(Date), M := Month(Date), and so on.
> That's what "corresponding" means.  It has nothing whatsoever to do
> with the second sentence of paragraph 24, nor of the Time_Of function.

How so? The wording is:

   "The functions Year, Month, Day, and Seconds return the corresponding
   values for a given value of the type Time ..." -- 9.6 24/2

I.e. the "correspondence" is to be between Year, Month, Day, Seconds and
Time. Now, do the quadruple (y, m, d, s) unambiguously denote a political
time date or not? RM is silent about that, though the wording let suggest
that it does [=variant 2!]. Observe, that the quadruple does not [always]
denote an UTC time [because seconds there are "political"], which is
inconsistent with "the difference" allegedly returned by UTC_Time_Offset,
etc.

> Your issues regarding how this package works around Daylight Savings
> Time changes point out a real problem.  But the questions about
> wording that you're asking above don't have anything to do with this
> issue.

My question was about the semantics. Actually, I don't care much about
wording so long I can understand the meaning. Reading RM I was perplexed
what is the expected behavior of UTC_Time_Offset, Time_Of and Split.

P.S. The problem is quite technical. How to convert Time to UTC time and
reverse?

****************************************************************

From: Adam Beneschan
Date: Friday, August 8, 2008  2:24 PM

> How so? The wording is:
>
>    "The functions Year, Month, Day, and Seconds return the
> corresponding values for a given value of the type Time ..." -- 9.6
> 24/2
>
> I.e. the "correspondence" is to be between Year, Month, Day, Seconds
> and Time. Now, do the quadruple (y, m, d, s) unambiguously denote a
> political time date or not?

OK, I see.  The word "corresponding" appears twice in this paragraph,
and I thought you were asking about the use of the word in the phrase
"four corresponding values", not the other use of the word.  I'm
beginning to think the RM is using this word too much, especially since
a different issue that came up on this list today also involves how
that same word is interpreted.  I suggest that "corresponding" be added
to the Glossary (Annex N), with the definition "We assume you know what
we mean".  :) :)

Anyway, I was answering the question I thought you were asking, but I
don't have an answer for the question you really wanted to know about.
I'll have to leave that to others, for now.

****************************************************************

From: Dmitry A. Kazakov
Date: Thursday, August 14, 2008  10:31 AM

!summary

The proposal is to define the semantics of political time and to
add UTC time. The proposal reuses as much as possible of the existing
packages descending from Ada.Calendar. Ada.Calendar.Clock can be
derived from UTC clock.

!problem

In presence of daylight saving:

1. Ada.Calendar.Time_Of is ill-defined within the hour when the clock
is adjusted backwards. The same year-month-day-seconds quadruple may
denote two different dates. For example:

year => 2008,
month => 10,
day => 26,
seconds => 2.5 * 3600.0

may refer in Central Europe to 02:30 before the clock adjustment (02:30
CEST) or else a date 1 hour later, after the clock adjustment (02:30 CET).

2. The semantics of Ada.Calendar.Time is not defined. The following
example illustrates the case:

   T0, T1, T2 : Time;
begin
   T0 := Time_Of  01:30 CEST
         (  Year    => 2008,
            Day     => 26,
            Month   => 10,
            Seconds => 1.5 * 3600.0
         );
   T1 := T0 + 3600.0; -- +1h
   T2 := T0 + 2.0 * 3600.0; -- +2h

RM does not clarify whether T1 = T2 or not.

3. Ada.Calendar.Time_Zones.UTC_Offset function is ill-defined because
of the issue of 2. If T1 = T2, then UTC_Offset is broken because T1 is
CEST and T2 is CET. These have different offsets to the UTC time, +2h
and +1h correspondingly.

!proposal

1. Ada.Calendar.Time is defined as a political time. In the example:

   T0, T1, T2 : Time;
begin
   T0 := Time_Of -- 01:30 CEST
         (  Year    => 2008,
            Day     => 26,
            Month   => 10,
            Seconds => 1.5 * 3600.0
         );
   T1 := T0 + 3600.0; -- +1h
   T2 := T0 + 2.0 * 3600.0; -- +2h

It is required T1 = T2. The time arithmetic is defined to satisfy this
requirement.

In particular, if a difference is calculated between two times of which
one belongs to the overlapping hour, then this time is treated as having
the same UTC offset as another. For example:

   T0, T1, T2 : Time;
begin
   T0 := Time_Of  01:30 CEST
         (  Year    => 2008,
            Day     => 26,
            Month   => 10,
            Seconds => 1.5 * 3600.0
         );
   T1 := Time_Of  02:30 CEST or CET
         (  Year    => 2008,
            Day     => 26,
            Month   => 10,
            Seconds => 2.5 * 3600.0
         );
   T2 := Time_Of  03:30 CET
         (  Year    => 2008,
            Day     => 26,
            Month   => 10,
            Seconds => 3.5 * 3600.0
         );

T2  T1 = 3600.0, here T1 is considered CET as T2 is. T1  T0 = 3600.0,
T1 here is considered CEST.

2. Ada.Calendar.Time_Zones.UTC_Offset propagates Time_Error when the
argument is ambiguous, like:

         Time_Of  02:30 CEST or CET?
         (  Year    => 2008,
            Day     => 26,
            Month   => 10,
            Seconds => 2.5 * 3600.0
         );

3. The following package is introduced:

package Ada.UTC is
   type Time is private;
   function Clock return Time;
   function "+" (Left : Time; Right : Duration) return Time;
   function "+" (Left : Duration; Right : Time) return Time;
   function "-" (Left : Time; Right : Duration) return Time;
   function "-" (Left : Time; Time : Time) return Duration;
   function "<" (Left, Right : Time) return Boolean;
   function "<="(Left, Right : Time) return Boolean;
   function ">" (Left, Right : Time) return Boolean;
   function ">="(Left, Right : Time) return Boolean; end Ada.UTC;

The package provides UTC clock, time and operations on it.

4. The package Ada.Calendar.Time_Zones contains four new functions:

4.a. function UTC_Time_Offset
        (Date : Ada.UTC.Time := Ada.UTC.Clock)
           return Time_Offset;

This function returns the difference between the political time and the
UTC time for its argument. The function is well-defined for each its
argument.

4.b. function To_Calendar_Time
       (Time : Ada.UTC.Time; Time_Zone : Time_Offset)
           return Time;

This function returns Ada.Calendar.Time corresponding to the argument
for the time zone offset defined by the parameter Time_Zone.

4.c. function To_UTC (Time : Ada.UTC.Time)
        return Time;

This function is equivalent to:

     To_UTC (Time, UTC_Time_Offset (Time));

4.d. function To_UTC
        (Time : Ada.UTC.Time; Time_Zone : Time_Offset)
            return Time;

This function converts UTC time to the political time with the time offset
specified by the parameter Time_Zone.

5. Implementation. Ada.Calendar.Time and Ada.UTC.Time can be implemented
as the same type. In which case Ada.Calendar.Clock could be directly
derived from Ada.UTC.Clock:

   function Ada.Calendar.Clock return Ada.Calendar.Time is
      T : Ada.UTC.Time := Ada.UTC.Clock;
   begin
      return
         Ada.Calendar.Time
            (T + Ada.Calendar.Time_Zones.UTC_Time_Offset (T));
   end Ada.Calendar.Clock;

!examples

1. Conversion to UTC time:

   T : Ada.Calendar.Time :=
          Ada.Calendar.Time_Of  02:30 CEST or CET
          (  Year    => 2008,
             Day     => 26,
             Month   => 10,
             Seconds => 2.5 * 3600.0
          );
   T_CEST : Ada.UTC.Time :=
               Ada.Calendar.Time_Zones.To_UTC (T, 60);
   T_CET  : Ada.UTC.Time :=
               Ada.Calendar.Time_Zones.To_UTC (T, 2*60);

2. Conversion from UTC. Assuming the declarations above:

Ada.Calendar.Time_Zones.To_Calendar_Time (T_CEST) = T
Ada.Calendar.Time_Zones.To_Calendar_Time (T_CET) = T

3. Composition of UTC time from year-month-day-seconds:

Ada.Calendar.Time_Zones.To_UTC
(  Ada.Calendar.Formatting.Time_Of
   (  Year        => 2008,
      Day         => 26,
      Month       => 10,
      Seconds     => 2.5 * 3600.0,
      Time_Offset => 0
   ),
   Time_Offset => 0
);

4. Splitting UTC time into year-month-day-seconds:

Ada.Calendar.Formatting.Split
(  Date        => Ada.Calendar.Time_Zones.To_Calendar_Time (T, 0),
   Year        => Year,
   Month       => Month,
   Day         => Day,
   Seconds     => Seconds,
   Leap_Second => Leap_Second,
   Time_Zone   => 0
);

****************************************************************

From: Pascal Leroy
Date: Thursday, August 28, 2008  6:02 AM

>1. Ada.Calendar.Time is defined as a political time. In the example:

>  T0, T1, T2 : Time;
>begin
>  T0 := Time_Of -- 01:30 CEST
>        (  Year    => 2008,
>           Day     => 26,
>           Month   => 10,
>           Seconds => 1.5 * 3600.0
>        );
>  T1 := T0 + 3600.0; -- +1h
>  T2 := T0 + 2.0 * 3600.0; -- +2h
>
>It is required T1 = T2. The time arithmetic is defined to satisfy this
>requirement.

Taken at face value, this statement doesn't make any sense. If
T1 = T2 then presumably T1 + 3600.0 = T2 + 3600.0, but since T1 + 3600.0
is T2, then T2 = T2 + 3600.0.  I don't think that you imply this, but
then it would be good to present an implementation model for your
proposal, since it seems to require that the time values "remember"
how they were obtained.  (And that seems fairly horrendous.)

> In particular, if a difference is calculated between two times of which one
> belongs to the overlapping hour, then this time is treated as having the
> same UTC offset as another. For example:

This doesn't help if both times belong to the overlapping hour.
And it is not clear that this is what you want in the other cases anyway.
It seems to me that you are just trying to infer a timezone that was never
provided to Time_Of in the first place. You might guess right some of the
time, but that's not better than the current state of affairs.

The only sound way to address the issues you are mentioning would be to
have variants of Time_Of et al. that take the designation of a timezone
(not only a time offset) and keep track of all the time changes that take
place in that timezone.  That was rejected long ago, because it seems
absurd to have the Ada runtime know about all this timezone stuff, which
changes periodically anyway.  This is especially true in an embedded
environment, where no-one cares about timezones.

****************************************************************

From: Dmitry A. Kazakov
Date: Friday, August 29, 2008  4:16 AM

...
> Taken at face value, this statement doesn't make any sense.  If T1 =
> T2 then presumably T1 + 3600.0 = T2 + 3600.0, but since T1 + 3600.0 is
> T2,

No, T1 + 3600.0 /= T2.

You assumed associativity of "+", which does not hold for the
political time. In general:

(T + D1) + D2 /= T + (D1 + D2)

This is the case when T = T0 and D1, D2 = 3600.0.

> then T2 = T2 + 3600.0.

T1 + 3600.0 = T2 + 3600.0

> I don't think that you imply this, but then it would be good to
> present an implementation model for your proposal, since it seems to
> require that the time values "remember" how they were obtained.  (And
> that seems fairly horrendous.)

The semantics of Time + Duration is well-defined:

A straightforward implementation of is:

1. Time is converted to UTC. This operation is unambiguous when Time
is outside the overlapping hours. When Time is in such an hour, then
for positive Duration Time is converted to the UTC after time skew.
When Duration is negative it is converted to the UTC before it.

2. Duration is added

3. Result is converted back to the political time. This operation is always
unambiguous.

>> In particular, if a difference is calculated between two times of
>> which one belongs to the overlapping hour, then this time is treated
>> as having the same UTC offset as another. For example:
>
> This doesn't help if both times belong to the overlapping hour.

In this case the difference is calculated directly considering the UTC
offsets equal.

> And it is
> not clear that this is what you want in the other cases anyway.  It
> seems to me that you are just trying to infer a timezone that was
> never provided to Time_Of in the first place.

It is true that arithmetic of political time cannot be consistently defined
without knowing the timezone. It should not surprise anyone. Political time
is garbage. The problem is not in the timezone, it is whether the time zone
offset should be derivable from the values of Time. My proposal (T1=T2) is
that it should not. Yet, an implementation is free to choose UTC, internally
and then mangle +, -, =, in accordance to the "political will" (:-))...

> The only sound way to address the issues you are mentioning would be
> to have variants of Time_Of et al. that take the designation of a
> timezone (not only a time offset) and keep track of all the time
> changes that take place in that timezone.

That would not define the semantics of Time. My proposal does it, by defining
the political time in terms of UTC. This is how the political time is defined
in the real world.

> That was rejected long ago, because it seems absurd to have the Ada
> runtime know about all this timezone stuff, which changes periodically
> anyway.  This is especially true in an embedded environment, where
> no-one cares about timezones.

Right, my proposal provides a way to define Time on the platform where UTC
is unavailable. On these Time_Offset is considered constant 0.

****************************************************************

From: Jeffery R. Carter
Date: Sunday, February 15, 2009  10:03 PM

I have to disagree with the proposal to add political time information to
Ada.Calendar. It would break a great deal of my company's code because it is
not backwards compatible.

Ada.Calendar knows nothing of time zones, political time, or daylight savings
time. This is clear from its definition in the ARM, from Ada 83 through Ada 95
and on to the current standard. A possible representation of type Time could be
simply a record of the 4 components. Time_Of checks that the day is legal for
the year and month, and handles the case of the seconds being Day_Duration'Last,
but that is all. Split simply returns the 4 components. Whether the resulting
time is meaningful in any specific political entity is unknown to the package.

Clock returns some "system time", probably external to the language. This system
time may know about some political entity and jump forward or backward, as the
ARM specifically allows. In the case where the "system time" jumps backwards,
Clock may return the same value twice. The meaning of those values is not
something that Calendar does or can know about.

The new packages added in 9.6.1 know about the system clock's UTC offset, and
how to convert times from a given offset to and from the system clock's offset,
but those offsets have no meaning relating to political time. How could they
when arbitrary offsets with no political meaning may be specified?

For example, the various Time_Of functions treat the time as having the
specified offset and return the Time value for the time converted to the system
clock's offset (a time of 12:00:00.00 with Time_Zone => 0 and UTC_Time_Offset
= -420 [MST, -0700] would give a time of 05:00:00.00). The various Split
procedures with Time_Zone parameters treat the Date as having the system clock's
 offset, and return the components of the Date converted to the specified offset.

(Calling the parameter Time_Zone seems a bit misleading, since "time zone"
usually refers to a political concept, and the parameter is really an arbitrary
offset unrelated to any such concept.)

If we want to have operations that know about political time in various political
entities and their offsets, then we really need to deal with time zones specified
as strings (since this is a malleable definition and cannot be represented by
arbitrary numeric values or an enumeration type), such as "US/Mountain" and
"Europe/Dublin". Clearly such operations do not belong in the existing packages.

****************************************************************

From: Tucker Taft
Date: Thursday, May 20, 2010  6:06 PM

On our phone call today, we had a somewhat mind-bending discussion of calendar, time zones, and daylight-savings/summer time.

We recognized that there are two
distinct strategies for representing
Calendar.Time, one where the time is kept in UTC internally, and Calendar.Split and Calendar.Time_Of worry about the "local"
time zone.  The other is that Time is represented in local time, and Calendar.Time_Of and Calendar.Split are trivial.

Here are some interesting questions:

What does "-"(Time, Time) return for times that cross a daylight/summer timezone change?  Clearly if Time is represented internally as UTC time, this is very straightforward.

If Time is represented as a local time that reflects daylight-savings/summer time, but with no internal bit indicating whether daylight/summer time was in effect when the local time was stored, then it seems clear that "-" will necessarily return a value o
f Duration that corresponds to 3600.0 seconds more or 3600.0 seconds less than the actual number of seconds that pass between the two times.
This seems highly undesirable.

This leads me to suggest that for an implementation that stores local time, during a single execution of an Ada program, all times should use the same time zone, even if daylight/summer time begins or ends during the execution.  This also means that UTC_Ti
me_Offset should return the same value independent of the Date parameter, reflecting the time zone at the moment when program execution began.  This means that Clock will have to undo the effects of any change in daylight savings/summer time since the prog
ram execution began, if the underlying O/S reflects the change immediately.  Similarly Split and Time_Of will presume the program-start timezone, even if they represent times when the timezone would be different due to daylight/summer time.

Alternatively, if Calendar.Time is represented as UTC time, then it would be feasible for UTC_Time_Offset to give different values depending on the Date parameter, and for Calendar.Time_Of and Calendar.Split to reflect the different UTC_Time_Offset based o
n the Time value represented by the parameters.  However, Time_Of would be ambiguous for 2AM-3AM on the day daylight/summer time ends, and the implementation would need to pick one or the other.  To be consistent with the implementations using local time, 
we would want to specify that when ambiguous, a UTC_Time_Offset is used by Time_Of that corresponds to its value when program execution began.

Of course even more consistent would be to have both implementation approaches always presume the time zone when program execution begins, and have UTC_Time_Offset always return the same value, independent of the Date parameter.  However, that seems to fly
 in the face of the description of UTC_Time_Offset.

The other solution is to effectively require implementations to use UTC time internally, or at least an indication of whether a given "local" time is or is not a daylight/summer time, so that "-", "<", Difference, etc., can properly reflect the true differ
ence in time.  This would then allow UTC_Time_Offset to depend on the Date parameter, as implied by it RM definition.

Uggh...

****************************************************************

From: Randy Brukardt
Date: Thursday, May 20, 2010  6:39 PM

...
> If Time is represented as a local time that reflects
> daylight-savings/summer time, but with no internal bit indicating
> whether daylight/summer time was in effect when the local time was
> stored, then it seems clear that "-" will necessarily return a value
> of Duration that corresponds to 3600.0 seconds more or 3600.0 seconds
> less than the actual number of seconds that pass between the two
> times.
> This seems highly undesirable.

Nevertheless, it's been the case in at least some Ada implementations for
decades. (It causes interesting effects in long-running programs that happen to
running when the time zone changes. For example, the heartbeat monitor on our
webserver fails each March because it waits 61 minutes (instead of 1 minute) to
send the heartbeat signal to the hardware -- so the hardware monitor assumes the
computer failed and does a reboot. Didn't see worth fixing.)

We could try to adopt rules to change that, but they would be inconsistent with
actual practice on Windows.

> This leads me to suggest that for an implementation that stores local
> time, during a single execution of an Ada program, all times should
> use the same time zone, even if daylight/summer time begins or ends
> during the execution.
> This also means that UTC_Time_Offset should return the same value
> independent of the Date parameter, reflecting the time zone at the
> moment when program execution began.  This means that Clock will have
> to undo the effects of any change in daylight savings/summer time
> since the program execution began, if the underlying O/S reflects the
> change immediately.
>  Similarly Split and Time_Of will presume the program-start timezone,
> even if they represent times when the timezone would be different due
> to daylight/summer time.

That sounds fine, except that it isn't implementable on Windows. Windows has two
time functions: GetSystemTime (which is UTC time) and GetLocalTime (which gets
the current local time). To do this, you would have to use the System (UTC) time
and do conversions after the fact -- at which point you might was well abandon
the entire local time model.

It also should be noted that the time zone model isn't even supported on Windows
95/98/ME, which is one reason that we never considered using it. (While you can
retrieve the current time zone on Windows 95/98/ME, the conversion from
SystemTime (UTC) to a specific local time is missing.) This isn't a big deal
anymore, although I would still continue to support at least Windows 98 (because
my home computer is still Windows 98 and there is no reason to update a computer
that doesn't connect to the Internet, especially as that would require replacing
my bookkeeping and database software - and I still want to use Ada at home).

> Alternatively, if Calendar.Time is represented as UTC time, then it
> would be feasible for UTC_Time_Offset to give different values
> depending on the Date parameter, and for Calendar.Time_Of and
> Calendar.Split to reflect the different UTC_Time_Offset based on the
> Time value represented by the parameters.  However, Time_Of would be
> ambiguous for 2AM-3AM on the day daylight/summer time ends, and the
> implementation would need to pick one or the other.  To be consistent
> with the implementations using local time, we would want to specify
> that when ambiguous, a UTC_Time_Offset is used by Time_Of that
> corresponds to its value when program execution began.

Sounds painful, as you would have to remember whether or not daylight savings
was active when the program started. For something that happens once per year,
I'd probably suggest just requiring one or the other results.

> Of course even more consistent would be to have both implementation
> approaches always presume the time zone when program execution begins,
> and have UTC_Time_Offset always return the same value, independent of
> the Date parameter.
> However, that seems to fly in the face of the description of
> UTC_Time_Offset.

It also would be inconsistent with current practice on Windows (at least). You
have to work hard on Windows to avoid reflecting the  daylight saving change
immediately, and I don't think anyone does that. (Use Ada.Real_Time for delays!)

> The other solution is to effectively require implementations to use
> UTC time internally, or at least an indication of whether a given
> "local" time is or is not a daylight/summer time, so that "-", "<",
> Difference, etc., can properly reflect the true difference in time.
> This would then allow UTC_Time_Offset to depend on the Date parameter,
> as implied by it RM definition.

I think this is what is required, as your other schemes end up working this way
on Windows anyway. So why bother defining them? But note that this is definitely
inconsistent with current practice on Windows. The change is not a big deal
(probably would fix more bugs than it causes), but clearly more than a pure BI
(that is, this is something that I would prefer to be clearly different between
Ada 2005 and Ada 2012 so that implementations can determine where to put the
pain for their customers).

> Uggh...

Well, now you have a better appreciation of why I've been frustrated talking to
you (and others) on this topic.

****************************************************************

From: Tucker Taft
Date: Thursday, May 20, 2010  8:32 PM

So it sounds like you agree we should
require "Calendar.Time" to preserve enough information so that UTC_Time_Offset
can be computed properly, by, for example, having a bit indicating whether or
not the value corresponds to daylight/summer time.

Or by using UTC time internally.

****************************************************************

From: Bob Duff
Date: Thursday, May 20, 2010  8:36 PM

> On our phone call today, we had a somewhat mind-bending discussion of
> calendar, time zones, and daylight-savings/summer time.

Mind-bending indeed.  The concept of "time" seems simple on the surface, but it
is surprisingly complicated!  I don't get it.

> We recognized that there are two
> distinct strategies for representing
> Calendar.Time, one where the time is kept in UTC internally, and
> Calendar.Split and Calendar.Time_Of worry about the "local"
> time zone.  The other is that Time is represented in local time, and
> Calendar.Time_Of and Calendar.Split are trivial.

I think you're mixing up two things:

    - An implementation might represent Time in UTC, or in local time.

    - An implementation might represent Time as an integer number of
      zilliseconds(*) since some epoch, or as a record containing
      something like (year, month, day, hour, minute, second).

But anyway, I think this is all a tempest in a teapot.  Hard real-time systems
that really care about time don't go around changing the system clock to match
whatever some politicians say is the local political time.  And those are the
kind of systems that really care that X - Y gives exactly 3600.0 for that
strange hour during the daylight "savings" transition.

Think about a GPS system, which cares about speed-of-light transmissions from
some satellites to some earth-bound devices.  Those folks aren't going to mess
around with political time -- they're going to use some stable standard, like
UTC.  I'll bet that satellite doesn't have any TIMEZONE or DAYLIGHT_SAVINGS
environment variables on it, being modified willy-nilly by the sysadmins or the
operating system!

(*) zillisecond = zillionth of a second.  Could be a nanosecond or a picosecond,
or whatever.  Some small fraction of a second. And "integer number of
zilliseconds" is the same as a fixed-point number of seconds, with 'Small = 1
zillisecond.  E.g. Duration.

****************************************************************

From: Randy Brukardt
Date: Thursday, May 20, 2010  9:00 PM

Well, GPS uses its own time which is subtly wrong (it doesn't use leap seconds).
But whatever.

The point here is that we all agree with your point. There are a lot of systems
that can't (or shouldn't) use local time and need access to a stable timebase
like UTC time.  The whole idea behind adding UTC_Offset was to provide *within
Ada* a *portable* way to access the stable UTC time (since the popular systems
all maintain a version of UTC time). And the problem here is that solution *does
not work* in some corner cases.

Now, clearly there are going to be some very hard real-time applications that
will need their own custom timebase (GPS seems to be a good example). But this
Ada solution is intended for the middle ground of applications that need
reliable access to UTC time to the accuracy provided by the host system (for
Windows and Unix that can be pretty good, as both systems can be hooked up to
use NTP to sync their time), but don't want to hack around with local time and
daylight savings time. One example is my web server, which is supposed to use
UTC time in its timestamps - this isn't too critical, but I'd like a portable
way to get it right.

Unless you are saying that no application should ever use the host system
version of UTC time, or anyone whose system ever runs an Ada program should have
their system timezone set to UTC time, we need to fix this problem. Or don't you
care about portable solutions any more??

P.S. There aren't any TIMEZONE or DAYLIGHT_SAVINGS environment variables on my
system either. :-) Doesn't mean anything; there is a TZ record that can be
queried with a function, and surely the local time depends on it.

****************************************************************

From: Randy Brukardt
Date: Thursday, May 20, 2010  9:02 PM

> So it sounds like you agree we should
> require "Calendar.Time" to preserve enough information so that
> UTC_Time_Offset can be computed properly, by, for example, having a
> bit indicating whether or not the value corresponds to daylight/summer
> time.
>
> Or by using UTC time internally.

Right. That's what I said during the call, that's what it says in the minutes
that I have an action item to try to do, and that's what my research into
Windows system calls after the call shows is the best plan.

****************************************************************

Questions? Ask the ACAA Technical Agent