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

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

!standard 9.6(24/2)          11-01-31 AI05-0119-1/01
!standard 9.6.1(41/2)
!standard 9.6.1(42/2)
!class binding interpretation 11-01-31
!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
The behavior of Ada.Calendar Time values is not specified in the presence of summer time (daylight saving time) adjustments. In particular, the results of Ada.Calendar operations on two values obtained with different effective time zones is unspecified.
!question
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.
4. 9.6.1(42) says UTC_Time_Offset "Returns ... the difference between the implementation-defined time zone of Calendar, and UTC time". This wording is ambiguous as to the sign of the "difference".
!recommendation
(See summary.)
!wording
Add after 9.6(24/2):
AARM Ramification: The behavior of these values and subprograms if the time zone changes is also implementation-defined. In particular, the changes associated with summer time adjustments (like Daylight Savings Time in the United States) should be treated as a change in the implementation-defined time zone. The language does not specify whether the time zone information is stored in values of type Time; therefore the results of binary operators are unspecified when the operands are the two values with different effective time zones. In particular, the results of "-" may differ from the "real" result by the difference in the time zone adjustment. Similarly, the result of UTC_Time_Offset (see 9.6.1) may or may not reflect a time zone adjustment.
Add after 9.6.1(42/2):
AARM Ramification: This is equivalent to subtracting the UTC-time from the local-time. In North America, the result be negative; in Europe, the result will be zero or positive.
[Editor's note: I was tasked with improving the original wording. But I can't see any way to do that without putting more detail into the wording than makes sense. The original wording implies an ordering and a sign, it just not crystal clear. But I can put that detail into an AARM note, and then the intent is clear.
Since we're not making any actual wording changes, perhaps this should be a ramification?]
!discussion
Taking the last question first:
The intent is that UTC_Time_Offset means local-time - UTC-time. In the US, this value is negative. (This is the same value that you may find in e-mail headers.)
For the other questions:
[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.
We have decided to take approach (1), essentially confirming the existing standard in that these things are unspecified. Implementations can offer additional guarantees if their users so demand.
If we were to require that these operations work without any time zone bias in all cases, we would effectively be requiring that the underlying implementation by UTC Time plus a time zone indications. (Other implementations would have problems at some boundary or other.) Beyond being incompatible with many existing implementations, this would be a fairly heavy implementation on targets that don't have much time zone support.
We could try to mitigate this problem by somehow defining that systems that don't have a concept of time zones are exempt from this requirement. But then systems that took advantage of the exemption would still have the original problem, and portability would again be reduced. Moreover, it may not help in that many embedded apps do know about time zones (my handheld GPSes do, for example) but don't provide much support for managing them.
For instance, time zone adjustments on MS-DOS systems back in the day occurred when the user adjusted the clock after a time change. Ada programs running at that time could have no way of protecting themselves from the change; there would be no way of determining the difference between such a time change and the other asjustments needed to keep the clock vaguely close to the wall time.
Even on systems with time zone support, there may not be functions to convert time values between arbitrary time zones (or even just UTC and local time). Older versions of Windows had little such functionality, for instance; if Ada required UTC time, an implementation targeting them would have to do much of the conversion work to local time itself.
As such, we decided not to add additional requirements.
[Editor's note: One could argue that on modern desktop systems, there isn't much reason not to require all times to be stored in UTC and converted on Split. One half-measure to accomplish that would be to add Implementation Advice to that effect. It still wouldn't be a requirement, so portability would not be perfect, but at least the desktop OSes would work right. Of course, that also would require breaking of existing binary time values (and quite possibly would require time values to be larger). I only suggest this because the above arguments seem weak; but I don't have a good idea of how to word an IA, either.]
!example
(See question.)
!ACATS test
No additional ACATS tests are needed.
!ASIS
No change to ASIS is needed.
!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 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.

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.

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.

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 difference 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.

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

!topic Clarification of UTC_Time_Offset
!reference 9.6.1(42)
!from Adam Beneschan 10-06-29
!discussion

I don't know if this should be added to AI05-119, or if this a simple enough
editorial comment to just go ahead and fix it now...

9.6.1(42) says UTC_Time_Offset "Returns ... the difference between the
implementation-defined time zone of Calendar, and UTC time".  To me, the English
phrase "the difference between" is somewhat ambiguous as to the sign of the
"difference".  May I suggest that a phrase like "This value is negative for time
zones west of Greenwich, and positive for time zones east of Greenwich, when
daylight savings time is not involved", or the reverse if that's what is really
intended, be added? Or something to that effect?  Perhaps the sign is obvious to
people who have worked a lot with Internet or Unix standards or the like, but I
have a feeling someone will get tripped up by it.

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

From: Dmitry A. Kazakov
Date: Wednesday, June 30, 2010  9:34 PM

It is unambiguous, provided "difference" means "-". The offset is exactly
Local_Time - UTC.

There is though a problem that the result does not tell if this difference is
now "regular" or else of the daylight saving time.

With the meaning "difference" is "-", UTC_Time_Offset depends on the current
time.

An alternative meaning could be the "regular", standard offset independent on
the current time. E.g. GMT+02:00

In both cases daylight saving time active flag is necessary to have
UTC_Time_Offset useful. Even more important is a clear statement on whether
Ada.Calendar.Time is a local political time (i.e. suffering large forward and
backward jumps) or else an UTC + constant offset time.

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

From: Randy Brukardt
Date: Wednesday, June 30, 2010  10:41 PM

It's surely true in all existing versions of Ada that Ada.Calendar.Time is
subject to jumps. That was decided early for Ada 83: see AI83-00195
(http://archive.adaic.com/standards/83com/ai-00195-ra.wj). Note that this AI was
considered a "Ramification". Nothing in Ada 95 or Ada 2005 changed that
interpretation. It's not clear that anything *can* change that; if the operator
manually changes the system time (including when a correction is applied
automatically to resync the time with a time-base), most Ada implementations
will immediately reflect that change. To avoid that, Ada implementations would
have to maintain their own clock, which seems way out of bounds (and actively
harmful on systems where the time is resynced to a supposedly more accurate
time-base).

The more interesting question is whether Ada 2012 will require that
Ada.Calendar.Time include enough information to recover the UTC time from a
value produced by Ada.Calendar.Clock. We believe that is enough to be able to
reliably recover the UTC time (a variety of wording changes will be needed,
which I am supposed to be working out in my copious spare time). It appears
impossible to completely fix this problem for values produced from Time_Of,
since there is never a way to know which of the two values is intended during
the "ambiguous time". That is generally only one hour per year, so it probably
not a serious problem.

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

From: Adam Beneschan
Date: Thursday, July 1, 2010  10:05 AM

> It is unambiguous, provided "difference" means "-". The offset is
> exactly Local_Time - UTC.

(1) My main point is that I don't think "difference" (and in particular the
phrase "difference between") means "-" in English.  I mean, it does, but not in
a consistent way.  When I talk about the difference between A and B, I could
mean A-B, B-A, or abs(A-B), and I don't think it's clear without context.
(Suppose someone were to ask me, referring to my two sons, "What's the height
difference between Mike and Steve", or "What's the height difference between
Steve and Mike"?  I guarantee you I will give the same answer to both
questions---"about 3 inches"; I will not answer "three inches" to one question
and "negative three inches" to the other.)  I'm talking about normal English
usage; if there were a standard mathematical usage, that usage would take
precedence, but I don't think there is one and I don't believe mathematicians
normally use this sort of phrasing.

In any event, I don't think it's obvious, and can envision someone interpreting
this wording to mean "the amount that must be added to time in the
implementation-defined time zone of Calendar to get the UTC time", which would
be a value of the opposite sign.

(2) Another reason I don't think your point is quite sufficient: even if
difference means "-", then the wording of 9.6.1(42) means (The
implementation-defined time zone of Calendar) - (UTC time).  Unless there's some
other definition I'm unaware of, a time zone is not a number, but is ... well I
don't know, a geographical subset of the Earth's surface area?  This is a less
important point, I suppose, because if the meaning of "difference between" were
clear, I think it would be clear what is meant here even though the wording is
slightly sloppy.

Anyway, I realize that there are issues about daylight saving time (AI05-119),
but this question of mine is independent of that issue.

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

From: Adam Beneschan
Date: Thursday, July 1, 2010  10:29 AM

> It appears impossible to completely fix this problem for values
> produced from Time_Of, since there is never a way to know which of the
> two values is intended during the "ambiguous time". That is generally
> only one hour per year, so it probably not a serious problem.

I've been wondering whether Time_Of needs to be aware that the U.S. laws about
when Daylight Savings Time starts and ends changed in 2007---or whether programs
running in parts of Indiana need to be aware that there was no DST before 2007
and there is now---or whether Time_Of on a computer running in Moscow needs to
be aware that dates before 1918 are 13 days off?  OK, I'm joking about that last
(I think it was resolved by AI95-351) but to what extent does Time_Of need to
know about the history of DST regulation changes?  Or is this just covered by
9.6(24)'s statement that "time zone" is implementation-defined, which means that
implementations can do whatever they want (i.e. either take historical changes
into account or ignore them as they choose)?  I'm guessing that the latter is
the case---is that correct?

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

From: Randy Brukardt
Date: Thursday, July 1, 2010  10:29 AM

The practical answer is "whatever the host system does". There is no intent that
Ada runtimes need to do their own time-zone manipulations, and thus whether or
not Time_Of can do historical calculations depends solely on what the OS
provides.

(For Windows, Indiana was (is?) a separate time zone, so that sort of issue
depends on whether or not the Indiana resident set their computer to use it. I
hope the Ada standard isn't going to start telling people how to set their
timezone settings!)

Note that it doesn't matter if all that is done with the Time_Of value is to
Split it later in the same (default) timezone: you'll have to get the same
answers. The issue only comes up when you start using other timezones to extract
values. It doesn't appear that anything portable can be mandated there.

OTOH, values derived from Clock, we probably can mandate that that UTC can be
reliably determined (since the timezone is known when the value is created, and
there is no historical concerns). But Time_Of is always going to be a guess at
best.

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

From: Dmitry A.. Kazakov
Date: Thursday, July 1, 2010  11:46 AM

> It's surely true in all existing versions of Ada that
> Ada.Calendar.Time is subject to jumps. That was decided early for Ada
> 83: see AI83-00195
> (http://archive.adaic.com/standards/83com/ai-00195-ra.wj). Note that
> this AI was considered a "Ramification". Nothing in Ada 95 or Ada 2005
> changed that interpretation. It's not clear that anything *can* change
> that; if the operator manually changes the system time (including when
> a correction is applied automatically to resync the time with a time-base),
> most Ada implementations will immediately reflect that change.

I think that this is an independent issue. The source of clock whether it be
machine's quartz or a user chaotically setting the system time is of no matter
to Ada. It is given. There is no other source, we don't care how reliable it is.

The actual problem is the semantics of how this source is treated within Ada. I
see no other way than to treat it as UTC (or astronomical time, or similar).

Political time requires too much overhead to implement. By this I mean precisely
the following:

   declare
       T : Time := Clock;
   begin
      delay 60.0 * 10.0;
      Put_Line (Boolean'Image (Clock > T));

when executed at 31. Oct 2010, 2:59, a computer located in Central Europe shall
print "FALSE."

Is there any Ada compiler, which indeed implements this broken semantics?
Does RM mandate this idiocy? Note, this unrelated to how Split and Time_Of are
supposed to work. They can continue to use political time, no problem with that,
if Ada.Calendar.Time meant to be UTC. If that were accepted, then the only
addition needed would be a package with Split_Into_UTC and UTC_Of in it.

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

From: Randy Brukardt
Date: Thursday, July 1, 2010  5:52 PM

> I think that this is an independent issue. The source of clock whether
> it be machine's quartz or a user chaotically setting the system time
> is of no matter to Ada. It is given.
> There is no other source, we don't care how reliable it is.

It's not at all an independent issue. You seem to be saying that the *reason*
for a time jump is significant. But that makes no sense. Back in the days of
MS-DOS, the way time zone changes were reflected was the same as in your wall
clock: you changed the time twice a year. Saying that changing the time to
reflect the time zone manually is somehow different than doing it automatically
makes no sense.

Similarly, whether the jumps occur "automatically" or "manually" is irrelevant.
If your system is connected to a time base via NTP, it probably automatically
adjusts the time periodically. How is this different (especially in terms of the
Standard) than the automatic change that occurs when Daylight Savings Time
changes? There is no practical difference.

> The actual problem is the semantics of how this source is treated
> within Ada. I see no other way than to treat it as UTC (or
> astronomical time, or similar).

That makes no sense as well. There is no requirement for an OS to even have a
way to get UTC time or turn off automatic adjustments, and even if there is, Ada
programs are likely to run on systems with those settings wrong.

> Political time requires too much overhead to implement. By this I mean
> precisely the following:
>
>    declare
>        T : Time := Clock;
>    begin
>       delay 60.0 * 10.0;
>       Put_Line (Boolean'Image (Clock > T));
>
> when executed at 31. Oct 2010, 2:59, a computer located in Central
> Europe shall print "FALSE."

No, this is clearly "implementation-defined". In fact, it ought to be
"unspecified", in that it isn't even that practical to explain when the answer
might be counter-intuitive. I have no idea when NTP is used to reset the time on
my Windows systems, for instance, only that I set it up to do so. So I can't
even "document" when the time might change, other than the useless advice that
it will happen any time the system's clock is changed. (Duh!)

If you want an monotonically-increasing time source, you need to use
Ada.Real_Time. There can be no guarantees with Calendar.

> Is there any Ada compiler, which indeed implements this broken
> semantics?

9.6(40.c) points out that Ada 83 pretty much required a direct mapping of Clock
to the results of Split. I suspect that virtually all Ada 83 compilers had this
"feature" (it's not a bug, as it is expected behavior). Compilers that haven't
changed their implementations much from the Ada 83 ones (in order to keep upward
compatibility) probably have the behavior. The IBM Rational compiler followed an
Ada 83-style model back when we were discussing this the last time, and it
probably still does.

And I know Janus/Ada follows this model. Indeed, the heartbeat monitor that
monitors our web server machine fails every November (as it waits 61 minutes
instead of 1 minute). That's not worth fixing, given that the effect is to
reboot the machine for no important reason once per year.

> Does RM mandate this idiocy? Note, this unrelated to how Split and
> Time_Of are supposed to work. They can continue to use political time,
> no problem with that, if Ada.Calendar.Time meant to be UTC. If that
> were accepted, then the only addition needed would be a package with
> Split_Into_UTC and UTC_Of in it.

This would be a royal pain on Windows. The basic Win32 time functions only
provide a means to retrieve "local time" and "system time" (the latter is
essentially UTC). There is no mechanism to convert one to the other. You can get
the local time zone information and try to figure it out, but that is complex
and error-prone.

There are some newer APIs that would help, but they would mean that no Ada code
could not run on older versions of Windows. Calendar is present in every Ada
program, so anything it requires is required of all programs (unlike, say CPU
affinities or CPU timing, which are only present if you use them). While it
makes sense to only support Ada development tools on recent systems, it doesn't
make sense to have such requirements on generated programs (there are a lot of
perfectly useful old computers out there).

Also, you are requiring a complete re-implementation of Ada.Calendar. That
clearly will have other effects: files are no longer binary-compatible (the
reason the year range was changed the way it was was so that could be maintained
between Ada 95 and Ada 2005), indeed, Ada.Calendar.Time will be larger (it will
have to include time zone information), which will break representation clauses.

So, even if such a change is mandated, it would have to be only for Ada 2012;
older versions will remain as they are.

Personally, the only thing I care about is the ability to reliably get UTC time
from Ada.Calendar.Clock. It doesn't make sense to complain about comparisons in
this one case when there are many other cases that happen just as often that
will not have an guaranteed result. (Without a guarantee for monotonically
increasing time, like the one in Ada.Real_Time, you are always building on a
house of sand.) Moreover, I don't recall any user that ever has complained about
the comparisons of Ada.Calendar, and changing it would be a substantial expense.
Use Ada.Real_Time if this is critical. Period.

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

From: Dmitry A. Kazakov
Date: Friday, July 2, 2010  3:28 AM

> It's not at all an independent issue. You seem to be saying that the
> *reason* for a time jump is significant. But that makes no sense. Back
> in the days of MS-DOS, the way time zone changes were reflected was
> the same as in your wall clock: you changed the time twice a year.
> Saying that changing the time to reflect the time zone manually is
> somehow different than doing it automatically makes no sense.

Surely it does. The question is the model of clock Ada implementation represents.
The mandated one seem to be:

Ideal-UTC-clock + [noise' +] Political skew + noise -> Ada.Calendar.Time

The proper one, must be

Ideal-UTC-clock + noise -> Ada.Calendar.Time

Your argument seems to be that because *some* noise is always added, then it is
no matter whether it be just noise or Political skew + noise. That is wrong, because
in reality the statistical behavior of the noise and Political skew are greatly
different in all aspects.

> Similarly, whether the jumps occur "automatically" or "manually" is
> irrelevant. If your system is connected to a time base via NTP, it
> probably automatically adjusts the time periodically. How is this
> different (especially in terms of the Standard) than the automatic
> change that occurs when Daylight Savings Time changes? There is no practical difference.

The practical difference is that NTP adjustments are small and well distributed. Adding
the Political skew upon them changes everything.

>> The actual problem is the semantics of how this source is treated
>> within Ada. I see no other way than to treat it as UTC (or
>> astronomical time, or similar).
>
> That makes no sense as well.

See the models above.

...
> No, this is clearly "implementation-defined". In fact, it ought to be
> "unspecified", in that it isn't even that practical to explain when
> the answer might be counter-intuitive. I have no idea when NTP is used
> to reset the time on my Windows systems, for instance, only that I set
> it up to do so. So I can't even "document" when the time might change,
> other than the useless advice that it will happen any time the system's clock is changed.
> (Duh!)

No NTP client would do 10 minutes adjustments. It makes no sense to consider
totally malfunctioning installation, like if you smashed the onboard quartz with
the hammer.

> If you want an monotonically-increasing time source, you need to use
> Ada.Real_Time. There can be no guarantees with Calendar.

That is impossible, because Ada.Real_Time.Time is not a real time in the sense,
that it is not linked to a reliable external [real] time source. As such it is
unusable for most networking/distributed/real-time applications.

> And I know Janus/Ada follows this model. Indeed, the heartbeat monitor
> that monitors our web server machine fails every November (as it waits
> 61 minutes instead of 1 minute). That's not worth fixing, given that
> the effect is to reboot the machine for no important reason once per year.

I better leave that uncommented. (:-))

>> Does RM mandate this idiocy? Note, this unrelated to how Split and
>> Time_Of are supposed to work. They can continue to use political
>> time, no problem with that, if Ada.Calendar.Time meant to be UTC. If
>> that were accepted, then the only addition needed would be a package
>> with Split_Into_UTC and UTC_Of in it.
>
> This would be a royal pain on Windows. The basic Win32 time functions
> only provide a means to retrieve "local time" and "system time" (the
> latter is essentially UTC). There is no mechanism to convert one to
> the other. You can get the local time zone information and try to
> figure it out, but that is complex and error-prone.

I see no problem. GetSystemTime and GetSystemTimeAsFileTime do the job.

The actual problem under Windows and many other OSes is an awful clock accuracy
(even when GetSystemTimeAsFileTime is used, FILETIME was 100ns resolution). I
suppose this is because Windows does not use the high-resolution CPU clock. So
if you want to get a decent implementation of Clock under Windows, you have to
use performance counters instead and periodically synchronize them with the
readings of GetSystemTime in a background task. But all this has no connection
to political vs physical time controversy.

However, considering modern CPUs, I think it is time advise Ada compilers to use
CPU clocks (ns resolution) as one of the time sources.

> Also, you are requiring a complete re-implementation of Ada.Calendar.

They must be, because the standard is broken. Once fixed, it will inevitably
break some existing implementations.

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

From: Randy Brukardt
Date: Friday, July 2, 2010  5:03 PM

> Sent: Saturday, July 03, 2010 3:28 AM

Speaking of time issues, your computer is sending messages a day ahead of time. We
received this message at about 8:30 UTC on Friday. If we were running Spam Assassin
here, we would never have seen it (SA treats messages dated in the future as almost
certain spam). I hope you're not using an Ada program for sending mail...

> Your argument seems to be that because *some* noise is always added,
> then it is no matter whether it be just noise or Political skew +
> noise. That is wrong, because in reality the statistical behavior of
> the noise and Political skew are greatly different in all aspects.

But you are claiming that there is some difference between "Political skew"
as added by a computer operating system and that added by the end user. I don't
see this difference. I have known a number of people who reset the times on
their Windows machines by hand twice a year. (I even have to do that on some of
the dual-boot systems here.) On such systems, any information about "political
skew" is going to be completely wrong. (Similarly, a heck of a lot of Windows 98
and Windows 2000 computers have the old time zone information for the US and
change the time on the wrong days. People using such old computers will manually
correct the time 4 times a year.)

And I also don't see any difference between the automatic changes made by time
zone information and the automatic changes made by NTP clock resets (or for that
matter, because of clock drift, see below).

> > Similarly, whether the jumps occur "automatically" or "manually" is
> > irrelevant. If your system is connected to a time base via NTP, it
> > probably automatically adjusts the time periodically. How is this
> > different (especially in terms of the Standard) than the automatic
> > change that occurs when Daylight Savings Time changes?
> There is no practical difference.
>
> The practical difference is that NTP adjustments are small and well
> distributed. Adding the Political skew upon them changes everything.

This is clearly not true, at least on Windows systems, because of the use of the
high-performance counter. See below.

> >> The actual problem is the semantics of how this source is treated
> >> within Ada. I see no other way than to treat it as UTC (or
> >> astronomical time, or similar).
> >
> > That makes no sense as well.
>
> See the models above.

It still doesn't make any sense, especially from a Standards perspective.
You want the language to ban using one particular reason for time jumps while
allowing all others, even though the others cause more damage to running
programs (as they aren't predictable in time nor size). That is impossible to
do, especially when there is no way to separate such changes from any other
(given that manual changes may be used to effect daylight savings changes).

...
> No NTP client would do 10 minutes adjustments. It makes no sense to
> consider totally malfunctioning installation, like if you smashed the
> onboard quartz with the hammer.

Actually, the implementation that you want for Windows essentially requires that
(see below).

> > If you want an monotonically-increasing time source, you need to use
> > Ada.Real_Time. There can be no guarantees with Calendar.
>
> That is impossible, because Ada.Real_Time.Time is not a real time in
> the sense, that it is not linked to a reliable external [real] time
> source. As such it is unusable for most
> networking/distributed/real-time applications.

If you need true external time, you need to get it from a true external source.
No computer time is accurate enough for that, and certainly Ada.Calendar.Time is
not intended for that purpose.

When I was a student, I learned that time is *never* the same on two different
computers, and it should *never* be used in a communication algorithm. If it is
used at all, it should be in an advisory/debugging function. And indeed, that is
how it is used in protocols like SMTP and HTTP -- the time is passed, but it
doesn't really matter. (Case in point, the message I'm replying to whose clock
is a day off. If the time was really significant, it couldn't have been
delivered.) Ada.Calendar is good enough for that.

Perhaps there are situations that do really need careful time information, but
those are clearly not those that computer clocks and Ada.Calendar are intended
for. I'd suggest using an NTP library for that purpose.

> > And I know Janus/Ada follows this model. Indeed, the heartbeat
> > monitor that monitors our web server machine fails every November
> > (as it waits
> > 61 minutes instead of 1 minute). That's not worth fixing, given that
> > the effect is to reboot the machine for no important reason once per year.
>
> I better leave that uncommented. (:-))

Two weeks work to fix one glitch per year makes no sense. It's the same reason
that I stopped doing updates to my spam filter -- the 5-10 extra messages that
would be blocked take only a couple of minutes per day to process and discard --
spending two weeks or more of work time to save 2 minutes per day would be
senseless.

...
> > This would be a royal pain on Windows. The basic Win32 time
> > functions only provide a means to retrieve "local time" and "system
> > time" (the latter is essentially UTC). There is no mechanism to
> > convert one to the other. You can get the local time zone
> > information and try to figure it out, but that is complex and error-prone.
>
> I see no problem. GetSystemTime and GetSystemTimeAsFileTime do the
> job.

My point is that there is no sane way to get the local time from the SystemTime.
Your claim that Split without a time zone parameter should still work as it does
currently implies that is possible and necessary (since it has to return the
local time, not the system time).

...
> However, considering modern CPUs, I think it is time advise Ada
> compilers to use CPU clocks (ns resolution) as one of the time
> sources.

I don't think this can be done in the Standard, as this is a quality of
implementation problem. Moreover, I doubt that most readers of Implementation
Advice would have a clue what "CPU clocks" means. We can't mention specific
operating systems in IA. Moreover, I don't think there is any Linux/Unix
counterpart to the performance counter of Windows, so I don't think there is a
way to even do this on those systems.

In any case, all of the Windows implementations that we tested for Claw used the
performance counter except for the Rational compiler (and the Windows version of
the Rational compiler no longer exists anyway - even if you had a copy, the
license keys wouldn't work and it wouldn't run).

Note that using the performance counter has the effect of causing large jumps at
random intervals. The way the Janus/Ada implementation works is that we "base"
the performance counter with the local time at start up. When clock is called,
we use the base plus the performance counter in order to produce the result
time. However, if that result differs more than 5 minutes from the  local time
as returned from the OS, we then "rebase" the clock and return the local time
instead. We do this so that clock drift, NTP adjustments, manual adjustments,
and daylight saving time changes are all reflected in the result time. This is
important for long running programs - Ada.Calendar needs to reflect the correct
(as seen by the computer) time. The net effect of this means that there will be
random jumps of at least 5 minutes periodically for a long running program. (We
do have a minimum time between checks for rebasing, in order to improve the
performance of Ada.Calendar.Clock. So programs that only run a few minutes -
like ACATS tests - will never see a rebasing.)

Obviously, the 5 minute number is arbitrary. It could be more or less, but you
have to have such a number. If you rebased always, of course the effect would be
to ignore the performance counter completely. If you never rebased, long-running
programs like our web server (which is intended to run months at a time) would
never get the effect of NTP clock settings and also could get far away from the
actual time.

Our experiments on Windows 98 and Windows 2000 systems back when this was
implemented showed that clock drift was a serious problem; we saw machines where
rebasing happened ever few hours as the local time and the performance counters
drifted apart rapidly. Not sure if more modern machines are any better in this
respect, but I would guess not (and NTP clock resets could make the problem even
more severe).

The rules you are suggesting would make it impossible to use the performance
counter, as programs that run more than a few hours have to be able to rebase
the clock. But that rebasing would by definition violate the model that you set
out. Wording to require the model that you want would almost certainly make
rebasing wrong as well -- which would be very bad.

> > Also, you are requiring a complete re-implementation of Ada.Calendar.
>
> They must be, because the standard is broken. Once fixed, it will
> inevitably break some existing implementations.

OK, but then it is necessary to consider the impact on customers. For instance,
if rebasing of times is banned, then long-running applications cannot depend on
Ada.Calendar to retrieve local or UTC time. (The time could drift too far away
to be meaningful - I have a temperature gauge that includes a clock that I never
change - it has drifted 40 minutes away from the correct time in just over a
year of operation. Most computer clocks are about that accurate in the long haul
-- that's why we have NTP.)

In any case, this discussion with you has been very enlightening. I was in fact
in favor of some sort of fix to allow retrieving of UTC time reliably. But this
discussion has convinced me that that is a fool's game - it is impossible to
make a meaningful requirement that also does not prevent important
implementation strategies. As such, I don't plan to waste time attempting to
write language to mandate anything, and will recommend No Action on AI05-0119-1.

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

From: Dmitry A. Kazakov
Date: Saturday, July 3, 2010  4L57 AM

> Speaking of time issues, your computer is sending messages a day ahead
> of time. We received this message at about 8:30 UTC on Friday.

It happens sporadically, I don't know why and when.

> ...
>>> It's not at all an independent issue. You seem to be saying that the
>>> *reason* for a time jump is significant. But that makes no sense.
>>> Back in the days of MS-DOS, the way time zone changes were reflected
>>> was the same as in your wall clock: you changed the time twice a year.
>>> Saying that changing the time to reflect the time zone manually is
>>> somehow different than doing it automatically makes no sense.
>>
>> Surely it does. The question is the model of clock Ada implementation
>> represents. The mandated one seem to be:
>>
>> Ideal-UTC-clock + [noise' +] Political skew + noise ->
>> Ada.Calendar.Time
>>
>> The proper one, must be
>>
>> Ideal-UTC-clock + noise -> Ada.Calendar.Time
>>
>> Your argument seems to be that because *some* noise is always added,
>> then it is no matter whether it be just noise or Political skew +
>> noise. That is wrong, because in reality the statistical behavior of
>> the noise and Political skew are greatly different in all aspects.
>
> But you are claiming that there is some difference between "Political skew"
> as added by a computer operating system and that added by the end user.

Certainly yes. The end user is responsible for setting the clock. Whatever he
does this is *the* UTC source the program must deal with. Political skew per
definition and intent is a *different* time.

> And I also don't see any difference between the automatic changes made
> by time zone information and the automatic changes made by NTP clock
> resets (or for that matter, because of clock drift, see below).

As above, these are different times, apples and oranges.

...
>>> That makes no sense as well.
>>
>> See the models above.
>
> It still doesn't make any sense, especially from a Standards perspective.
> You want the language to ban using one particular reason for time
> jumps while allowing all others, even though the others cause more
> damage to running programs (as they aren't predictable in time nor size).

Not at all. What I want is time to be defined in terms of UTC. If you want to
keep it political you must either throw away time arithmetic because it is
inconsistent. Note that any alternative, like to add some fields to Time object
in order to fix arithmetic, would mean making it UTC.

...
> If you need true external time, you need to get it from a true
> external source. No computer time is accurate enough for that, and
> certainly Ada.Calendar.Time is not intended for that purpose.
>
> When I was a student, I learned that time is *never* the same on two
> different computers, and it should *never* be used in a communication
> algorithm. If it is used at all, it should be in an advisory/debugging
> function. And indeed, that is how it is used in protocols like SMTP
> and HTTP
> -- the time is passed, but it doesn't really matter. (Case in point,
> the message I'm replying to whose clock is a day off.

If time is different, how do you know that it was day off? (:-))

Though any two asynchronous agents have different time, at the same time (no pun
intended), any two communicating agents cannot have arbitrary times.

> Perhaps there are situations that do really need careful time
> information, but those are clearly not those that computer clocks and
> Ada.Calendar are intended for. I'd suggest using an NTP library for that
> purpose.

I see no reason why Ada.Calendar should not use the best available time source.

>>> And I know Janus/Ada follows this model. Indeed, the heartbeat
>>> monitor that monitors our web server machine fails every November
>>> (as it waits
>>> 61 minutes instead of 1 minute). That's not worth fixing, given that
>>> the effect is to reboot the machine for no important reason once per year.
>>
>> I better leave that uncommented. (:-))
>
> Two weeks work to fix one glitch per year makes no sense.

It makes a lot of sense for industrial system, for which Ada must be the
language of choice. I cannot remember any system we installed, which didn't use
UTC stamps. Political time is no starter.

...
>> I see no problem. GetSystemTime and GetSystemTimeAsFileTime do the
>> job.
>
> My point is that there is no sane way to get the local time from the
> SystemTime.

Why should you care? Your point seem to be that Clock may deliver any rubbish.

> Your claim that Split without a time zone parameter should still work
> as it does currently implies that is possible and necessary (since it
> has to return the local time, not the system time).

I see no problem with that. As you said earlier, Ada RTL would use system
services, the way how they work or do not work is not our business. As for
Windows, Split from UTC can be implemented as FileTimeToLocalFileTime +
FileTimeToSystemTime.

> Note that using the performance counter has the effect of causing
> large jumps at random intervals. The way the Janus/Ada implementation
> works is that we "base" the performance counter with the local time at
> start up. When clock is called, we use the base plus the performance
> counter in order to produce the result time. However, if that result
> differs more than 5 minutes from the  local time as returned from the
> OS, we then "rebase" the clock and return the local time instead. We
> do this so that clock drift, NTP adjustments, manual adjustments, and
> daylight saving time changes are all reflected in the result time.
> This is important for long running programs - Ada.Calendar needs to reflect
> the correct (as seen by the computer) time.
> The net effect of this means that there will be random jumps of at
> least 5 minutes periodically for a long running program. (We do have a
> minimum time between checks for rebasing, in order to improve the
> performance of Ada.Calendar.Clock. So programs that only run a few
> minutes - like ACATS tests - will never see a rebasing.)

We do it differently. Basically it is a digital filter with takes performance
counter readings and the system time (UTC). The filter is run each second or so.
It has a window of at least 100 weighted readings. The output of the filter is
the base to add to the performance counter in order to get FILETIME. So far we
didn't experienced jumps.

> Obviously, the 5 minute number is arbitrary. It could be more or less,
> but you have to have such a number. If you rebased always, of course
> the effect would be to ignore the performance counter completely. If
> you never rebased, long-running programs like our web server (which is
> intended to run months at a time) would never get the effect of NTP
> clock settings and also could get far away from the actual time.

Right, this is because the quartz has poor quality. We measured its drift using
the above technique, it was about 5 microseconds per second between two
identical machines.

Note that even if the CPU and BIOS quartz were perfect, you could not use your
technique, because during reading performance counter and system time the Ada
task might get preempted. In our filter we read performance counter twice and
use the difference as the weight to the reading (which itself is a mean of two).
It is a bit complicated, but doable.

>>> Also, you are requiring a complete re-implementation of Ada.Calendar.
>>
>> They must be, because the standard is broken. Once fixed, it will
>> inevitably break some existing implementations.
>
> OK, but then it is necessary to consider the impact on customers. For
> instance, if rebasing of times is banned,

Not at all. I would mandate periodical rebasing if the implementation uses more
than one time source, e.g. CPU and BIOS clocks.

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

Questions? Ask the ACAA Technical Agent