Version 1.8 of ai12s/ai12-0336-1.txt

Unformatted version of ai12s/ai12-0336-1.txt version 1.8
Other versions for file ai12s/ai12-0336-1.txt

!standard 9.6.1(6/2)          19-12-02 AI12-0336-1/04
!standard 9.6.1(35/2)
!standard 9.6.1(40/2)
!standard 9.6.1(41/2)
!standard 9.6.1(42/3)
!standard 9.6.1(90/2)
!standard 9.6.1(91/2)
!class binding interpretation 19-06-04
!status Amendment 1-2012 19-07-03
!status ARG Approved 9-0-1 19-06-14
!status work item 19-06-04
!status received 19-05-30
!priority Low
!difficulty Easy
!qualifier Omission
!subject Meaning of Time_Offset
!summary
Time_Offset means the time difference from UTC time.
Local_Time_Offset means Calendar time - UTC time.
Time_Zone = 0 means the time offset of UTC.
!question
(1) 9.6.1(42/3) says the return value of UTC_Time_Offset is a number of minutes, the result of subtracting the implementation-defined time zone of Calendar from UTC time, at [a specific time].
This is in direct contradiction to the second paragraph of the !discussion in AI05-0119: "The intent is that UTC_Time_Offset means local-time - UTC-time.".
It's also contrary to the ramification of 9.6.1(42.a.1/3).
Is the normative wording correct? (No.)
!recommendation
(See Summary.)
!wording
Replace 9.6.1(6/2) with:
function Local_Time_Offset (Date : Time := Clock) return Time_Offset;
function UTC_Time_Offset (Date : Time := Clock) return Time_Offset renames Local_Time_Offset;
Add after 9.6.1(35/2):
function Local_Image (Date : Time; Include_Time_Fraction : Boolean := False) return String is (Image (Date, Include_Time_Fraction, Local_Time_Offset (Date)));
Modify 9.6.1(40/2):
Type Time_Offset represents {for a given locality at a given moment} the number of minutes {the local time is, at that moment, ahead (+) or behind (-) Coordinated Universal Time (abbreviated UTC). Redundant[ The Time_Offset for UTC is zero]} [difference between UTC the implementation-defined time zone used by Calendar and another time zone].
Modify 9.6.1(41/2):
function {Local_Time_Offset}[UTC_Time_Offset] (Date : Time := Clock) return Time_Offset;
Modify 9.6.1(42/3):
Returns, as a number of minutes, {the Time_Offset of}[the result of subtracting] the implementation-defined time zone of Calendar [from UTC time], at the time Date. If the time zone of the Calendar implementation is unknown, then Unknown_Zone_Error is raised.
Delete the second sentence of AARM 9.6.1(42.a/2).
Modify 9.6.1(90/2):
The implementation-defined time zone of package Calendar may, but need not, be the local time zone. {Local}[UTC]_Time_Offset always returns the difference relative to the implementation-defined time zone of package Calendar. If {Local}[UTC]_Time_Offset does not raise Unknown_Zone_Error, UTC time can be safely calculated (within the accuracy of the underlying time-base).
Modify 9.6.1(91/2):
Calling Split on the results of subtracting Duration({Local}[UTC]_Time_Offset*60) from Clock provides the components (hours, minutes, and so on) of the UTC time. In the United States, for example, {Local}[UTC]_Time_Offset will generally be negative.
!discussion
As the questioner states, AI05-0119-1 clearly states that UTC_Time_Offset means local-time - UTC-time. The wording change from that AI resulted in "...the result of subtracting the implementation-defined time zone of Calendar, and UTC time". During Ada 2005 review, Canada noted that the wording was still ambigious, so ", and" was changed to "from". But apparently no one noticed that this is backwards of the intent.
Note that Ada is strongly typed, and so should the definition of Ada: a "time" and a "time zone" are not the same thing. Subtracting time zones is not well-defined, so we have to talk about subtracting times in particular time zones.
----
In researching this question, we tried a program provided by the questioner on all of the Ada 2005 compilers that we are aware of. All of the compilers that implement Ada.Calendar.Formatting except one (the least used one, still in beta) treated Time_Offset = 0 as UTC time.
Enforcing the RM as written would therefore break any existing code that uses Ada.Calendar.Formatting (with the exception of the author's program that triggered this discussion).
We therefore redefine Time_Offset to match the compilers, and add versions of some of the functions that provide the originally intended functionality.
We used the prefix "Local_" to represent times for the "implementation-defined time zone of Calendar". Formally, since the time zone of Calendar need not be local, the correct name would be something like:
Implementation_Defined_Time_Zone_of_Calendar_Image (...)
But this appears to have a small usability problem. :-) The original intent was that Image would do this, and we don't want to have to write too much more to get that original intent.
We renames UTC_Time_Offset to Local_Time_Offset, as that better represents the meaning of the function with the change in the definition of Time_Offset. This name is used throughout 9.6.1 Note that the actual result of the function is unchanged. For compatibility, a renaming of Local_Time_Offset named UTC_Time_Offset is also added.
We also added Local_Image, to provide the image of the Ada.Calendar time without having to call Local_Time_Offset.
We considered defining Local_xxx versions of all of the other subprograms in Ada.Calendar.Formatting that take Time_Zone parameters.
For Value and Time_Of, there is a problem: the correct time zone to use could depend on the result of the function -- which of course depends on the time zone passed in. For most uses, this is unlikely to be an issue, so we could define a version based on the current time zone, but that could get incorrect answers near a time zone change. (To get certain correct answers, one could iterate until the result was the same for two successive iterations, but such an iteration is not guaranteed to terminate, so it's impractical to use it in the language definition).
We did not define new versions of Split in part because we aren't defining most of the other routines (as noted above), and simply because there are three versions with a bunch of parameters each, so these definitions would add quite a bit of text to the package. Local_Year, Local_Month, and so on don't seem useful enough to define.
======
Note again that this change is inconsistent with the Ada 2012 definition. So far as we are aware, only the beta 3.2.1 Janus/Ada follows the Ada 2012 definition, so this inconsistency is not going to arise in practice.
!corrigendum 9.6.1(6/2)
Replace the paragraph:
function UTC_Time_Offset (Date : Time := Clock) return Time_Offset;
by:
function Local_Time_Offset (Date : Time := Clock) return Time_Offset;
function UTC_Time_Offset (Date : Time := Clock) return Time_Offset renames Local_Time_Offset;
!corrigendum 9.6.1(35/2)
Insert after the paragraph:
function Image (Date : Time; Include_Time_Fraction : Boolean := False; Time_Zone : Time_Zones.Time_Offset := 0) return String;
the new paragraph:
function Local_Image (Date : Time; Include_Time_Fraction : Boolean := False) return String is (Image (Date, Include_Time_Fraction, Local_Time_Offset (Date)));
!corrigendum 9.6.1(40/2)
Replace the paragraph:
Type Time_Offset represents the number of minutes difference between the implementation-defined time zone used by Calendar and another time zone.
by:
Type Time_Offset represents for a given locality at a given moment the number of minutes the local time is, at that moment, ahead (+) or behind (-) Coordinated Universal Time (abbreviated UTC). The Time_Offset for UTC is zero.
!corrigendum 9.6.1(41/2)
Replace the paragraph:
function UTC_Time_Offset (Date : Time := Clock) return Time_Offset;
by:
function Local_Time_Offset (Date : Time := Clock) return Time_Offset;
!corrigendum 9.6.1(42/2)
Replace the paragraph:
Returns, as a number of minutes, the result of subtracting the implementation-defined time zone of Calendar from UTC time, at the time Date. If the time zone of the Calendar implementation is unknown, then Unknown_Zone_Error is raised.
by:
Returns, as a number of minutes, the Time_Offset of the implementation-defined time zone of Calendar, at the time Date. If the time zone of the Calendar implementation is unknown, then Unknown_Zone_Error is raised.
!corrigendum 9.6.1(90/2)
Replace the paragraph:
NOTES
36 The implementation-defined time zone of package Calendar may, but need not, be the local time zone. UTC_Time_Offset always returns the difference relative to the implementation-defined time zone of package Calendar. If UTC_Time_Offset does not raise Unknown_Zone_Error, UTC time can be safely calculated (within the accuracy of the underlying time-base).
by:
NOTES
36 The implementation-defined time zone of package Calendar may, but need not, be the local time zone. Local_Time_Offset always returns the difference relative to the implementation-defined time zone of package Calendar. If Local_Time_Offset does not raise Unknown_Zone_Error, UTC time can be safely calculated (within the accuracy of the underlying time-base).
!corrigendum 9.6.1(91/2)
Replace the paragraph:
37 Calling Split on the results of subtracting Duration(UTC_Time_Offset*60) from Clock provides the components (hours, minutes, and so on) of the UTC time. In the United States, for example, UTC_Time_Offset will generally be negative.
by:
37 Calling Split on the results of subtracting Duration(Local_Time_Offset*60) from Clock provides the components (hours, minutes, and so on) of the UTC time. In the United States, for example, Local_Time_Offset will generally be negative.
!ASIS
No ASIS effect.
!ACATS test
An ACATS C-Test should be constructed to check that Time_Zone = 0 means Calendar time. One way to do that is to compare the result of Ada.Calendar.Split to Ada.Calendar.Formatting.Split with Time_Zone = 0, as well as Ada.Calendar.Image with Time_Zone = 0.
!appendix

!topic Confusing definition of UTC Time
!reference Ada 2012 AARM 9.6.1 (42/3)
!from Simon Wright 2019-05-30
!keywords UTC
!discussion

First off, let me clarify I'm not addressing the complex questions of
the changeover to/from daylight-saving or leap seconds, addressed at
length in AI05-0119.

(42/3) says the return value of UTC_Time_Offset is a number of
minutes, the result of subtracting the implementation-defined time
zone of Calendar from UTC time, at [a specific time].

This is in direct contradiction to the second paragraph of the
!discussion in AI05-0119: "The intent is that UTC_Time_Offset means
local-time - UTC-time.".

It's also contrary to the ramification of (42.a.1/3).

At the moment, I'm on British Summer Time, and the time is 18:01. In
GMT that would be 17:01. GNAT thinks the UTC_Time_Offset is +60, which
makes sense to me since we put the clocks forward in March.

As a side issue, aren't "implementation-defined time zone of Calendar"
and "UTC Time" incommensurate? I'm pretty sure I know what is meant,
"the time returned by Calendar.Clock (whose time zone is
implementation-defined)".

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

From: Tucker Taft
Sent: Thursday, May 30, 2019  1:55 PM

Interesting!  This appears to be due to an "editorial" shift in wording as 
part of AI05-0269-1 from "subtracting A and B" to "subtracting A from B" 
which inverts the meaning.  It would be interesting to know what most 
compilers do here (at least the ones that don't always return zero!).

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

From: Randy Brukardt
Sent: Friday, May 31, 2019  12:07 AM

Janus/Ada, at least, returns a negative number here in Wisconsin. I would 
hope that implementers read the AARM note and match that (sounds like GNAT
does that, anyway). But the wording does seem backwards. Sigh - another AI.

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

From: Tucker Taft
Sent: Friday, May 31, 2019  6:43 AM

Actually, I described the change incorrectly. It went from "the difference 
between A and B" to "subtracting A from B." Unfortunately "difference between" 
is itself somewhat ambiguous, but in any case, the final wording is 
unambiguous, but wrong ... ;-)

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

From: Randy Brukardt
Sent: Tuesday, June 4, 2019  8:59 PM

...
> As a side issue, aren't "implementation-defined time zone of Calendar"
> and "UTC Time" incommensurate? I'm pretty sure I know what is meant, 
> "the time returned by Calendar.Clock (whose time zone is 
> implementation-defined)".

I think any good Ada compiler would reject an expression between two different 
types. Unfortunately, there's no ARM compiler.

Anyway, the better question is how to repair it. My first thought was to 
describe it as the difference between two time zones:

Modify 9.6.1(42/3):

Returns, as a number of minutes, the result of subtracting {the time zone of 
UTC time from} the implementation-defined time zone of Calendar[ from UTC 
time], at the time Date. If the time zone of the Calendar implementation is
unknown, then Unknown_Zone_Error is raised. 

...but this is a bit weird because most of us think of time zones as names 
like UTC or BST or CDT. What is the result of subtracting "UTC" - "CDT"?
(-300 is the intended answer, but there doesn't seem to be an operator).

So it seems like we need a bigger change:

Modify 9.6.1(42/3):

Returns, as a number of minutes, the result of subtracting {the UTC time 
corresponding to Date from} the {time in} the implementation-defined time zone
of Calendar[, at the time]{ corresponding to} Date. If the time zone of the 
Calendar implementation is unknown, then Unknown_Zone_Error is raised. 

[Recall that "Date" is the parameter to UTC_Time_Offset.]

Is this latter wording OK, or should we do something else???

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

From: Tucker Taft
Sent: Tuesday, June 4, 2019  9:26 PM

> Is this latter wording OK, or should we do something else???

This doesn't work for me.  You are implying that the value of time is time-zone
dependent.  That is not true on any Unix-like system.  The value of "time" is 
the same on all Unix systems at any given moment, being defined as the number 
of seconds since some particular moment in history (e.g. Jan 1, 1970, GMT).

I think we need to talk about the number of minutes since the local midnight, 
say, and do the computation in terms of that value, perhaps modulo 24*60.

We probably should start by defining what we mean by "time zone" since the 
term is used repeatedly.  If that definition is described, for the purposes of 
this section, as a number of minutes the local time is ahead of (+) or 
behind (-) UTC, then I think the rest becomes trivial.

Here is my attempt:

Modify 9.6.1(40/2):

{For the purposes of this subclause, the /time zone/ for a given locality is 
defined in terms of the number of minutes the local time is ahead (+) or 
behind (-) UTC.}  Type Time_Offset represents the number of minutes 
difference between the implementation-defined time zone used by Calendar and 
another time zone.

Now we can modify 9.6.1(42/3):

Returns, as a number of minutes, [the result of subtracting] the 
implementation-defined time zone of Calendar [from UTC time], at the time 
Date. If the time zone of the Calendar implementation is unknown, then 
Unknown_Zone_Error is raised. 

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

From: Randy Brukardt
Sent: Tuesday, June 4, 2019  10:50 PM

...
> This doesn't work for me.  You are implying that the value of time is 
> time-zone dependent.  That is not true on any Unix-like system.  The 
> value of "time" is the same on all Unix systems at any given moment, 
> being defined as the number of seconds since some particular moment in 
> history (e.g. Jan 1, 1970, GMT).

I've never understood this argument. We can say "time" is whatever we want it 
to be, it doesn't necessarily have anything to do with how it is represented 
on a particular system. Moreover, even on systems that use a raw counter, you
still have to know what time zone the counter represents, or you couldn't 
possibly split the value properly. 1042 seconds since a base date is not a 
time, 1042 since some date in UTC is a time.

Put another way, the time zone for a time value might be implicit, or it might 
be determined some other way, but there has to be one for any value of time.

Be that as it may, I know that this argument is non-terminating, so let's move 
on.

> I think we need to talk about the number of minutes since the local 
> midnight, say, and do the computation in terms of that value, perhaps 
> modulo 24*60.
> 
> We probably should start by defining what we mean by "time zone" since 
> the term is used repeatedly.  If that definition is described, for the 
> purposes of this section, as a number of minutes the local time is 
> ahead of (+) or behind (-) UTC, then I think the rest becomes trivial.
> 
> Here is my attempt:
> 
> Modify 9.6.1(40/2):
> 
> {For the purposes of this subclause, the /time zone/ for a given 
> locality is defined in terms of the number of minutes the local time 
> is ahead (+) or behind (-) UTC.}  Type Time_Offset represents the 
> number of minutes difference between the implementation-defined time 
> zone used by Calendar and another time zone.

The (minor) problem here is that type Time_Offset and the virtual type time 
zone are very different for no obvious reason. Obviously, whomever read this
at AdaCore interpreted it to be the former (which one can read fairly easily
on Linux) and never really looked at the actual words (or decided the apply 
the Dewar rule without telling anyone).

One has to wonder if we really should have these two separate declarations, 
especially since it appears that only Janus/Ada does this right (that is, 
following the RM definition). Times would work slightly better if UTC was the 
default time zone (getting UTC wouldn't depend on the time zone, which is 
error-prone). Of course, the practical results wouldn't change much (whatever 
the underlying system does would still be used).

A second question is whether we need to define UTC. I presume we're using the 
term from some other standard, but here it just appears out of the blue. We 
don't ever define it in the AARM notes. An undefined term is supposed to be 
in the dictionary or the mathematical reference. I can't check the dictionary 
(don't have that one), but it's not in the mathematical reference. It's of 
course defined in Wikipedia, but we can't use that in the RM. At a minimum, 
we ought to use the full English name "Coordinated Universal Time" once, so 
people can look it up.

> Now we can modify 9.6.1(42/3):
> 
> Returns, as a number of minutes, [the result of subtracting] the 
> implementation-defined time zone of Calendar [from UTC time], at the 
> time Date. If the time zone of the Calendar implementation is unknown, 
> then Unknown_Zone_Error is raised.

I guess I don't like that this definition no longer has any (obvious) 
relationship to UTC. OTOH, if we changed Time_Offset to the GNAT definition, 
this would always return 0 (and we'd need to add Local_Time_Offset to the 
spec). But I don't have a better idea, so I suppose I should shut up.

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

From: Joey Fish
Sent: Wednesday, June 5, 2019  4:03 AM

>This doesn't work for me.  You are implying that the value of time is 
>time-zone dependent.  That is not true on any Unix-like system.

Why is this an issue? Why should the Ada standard's notion of time be at all 
linked to Unix? [or Windows, or VMS, or anything?]

In fact, some of the biggest warts are due to importing and accommodating 
unix/c follies, with anonymous access types leading the charge.

>I think we need to talk about the number of minutes since the local 
>midnight, say, and do the computation in terms of that value, perhaps 
>modulo 24*60. 

I'm not sure that is a good idea. (I'm currently working night-shift, so I 
come in and work before midnight and finish after midnight; so while it's 
simple to say "I worked 10 hours" denoting them in "minutes since [local] 
midnight" wouldn't be very nice.)

I do know ISO 8601 was recently updated, perhaps looking at that would be 
helpful.

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

From: Thomas Quinot
Sent: Wednesday, June 5, 2019  4:27 AM

> Modify 9.6.1(40/2):
> 
> {For the purposes of this subclause, the /time zone/ for a given locality 
> is defined in terms of the number of minutes the local time is ahead (+) 
> or behind (-) UTC.}  Type Time_Offset represents the number of minutes 
> difference between the implementation-defined time zone used by Calendar 
> and another time zone.

However keep in mind that if you understand time zone as a single UTC offset 
then this depends not just on the locality but also on the specific point in 
time, due to daylight saving time issues. (IOW the UTC offset is a function 
of both the locality **and the point in tiem**).

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

From: Tucker Taft
Sent: Wednesday, June 5, 2019  7:12 AM

>> Modify 9.6.1(40/2):
>> 
>> {For the purposes of this subclause, the /time zone/ for a given locality 
>> is defined in terms of the number of minutes the local time is ahead (+) 
>> or behind (-) UTC.}  Type Time_Offset represents the number of minutes 
>> difference between the implementation-defined time zone used by Calendar 
>> and another time zone.
> 
> However keep in mind that if you understand time zone as a single UTC 
> offset then this depends not just on the locality but also on the 
> specific point in time, due to daylight saving time issues. (IOW the 
> UTC offset is a function of both the locality **and the point in 
> tiem**).

Good point.  That should probably be more explicit, as it is in the 
definition of UTC_Time_Offset.  Perhaps:

{For the purposes of this subclause, the /time zone/ for a given locality at 
a given moment is defined in terms of the number of minutes the local time is,
at that moment, ahead (+) or behind (-) UTC.} 

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

From: Dirk Craeynest
Sent: Wednesday, June 5, 2019  4:32 AM

> > We probably should start by defining what we mean by "time 
> > zone" since the term is used repeatedly.  If that definition 
> > is described, for the purposes of this section, as a number 
> > of minutes the local time is ahead of (+) or behind (-) UTC, 
> > then I think the rest becomes trivial.
> >
> > Here is my attempt:
> >
> > Modify 9.6.1(40/2):
> >
> > {For the purposes of this subclause, the /time zone/ for a 
> > given locality is defined in terms of the number of minutes 
> > the local time is ahead (+) or behind (-) UTC.}  Type 
> > Time_Offset represents the number of minutes difference 
> > between the implementation-defined time zone used by Calendar 
> > and another time zone.

> The (minor) problem here is that type Time_Offset and the virtual 
> type time zone are very different for no obvious reason. [...]

The definition proposed above is not what is commonly understood by "time 
zone" (a region with uniform time), but is a value: the time offset in that
region versus UTC.

Hence using UTC_Time_Offset or UTC_Offset for this value is probably more 
appropriate.  And that value could then be of type Time_Offset...

(See also https://en.wikipedia.org/wiki/List_of_UTC_time_offsets.)

About:
> Times would work slightly better if UTC was the default time zone

Note that strictly speaking UTC is not a time zone: it is a time standard 
(Coordinated Universal Time).

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

From: Tucker Taft
Sent: Wednesday, June 5, 2019  3:00 PM

> Hence using UTC_Time_Offset or UTC_Offset for this value is probably 
> more appropriate.  And that value could then be of type Time_Offset...

I was trying to minimize wording changes here, since we are just fixing a bug.
The existing wording uses the term "time zone" without defining it, and that 
has led to some confusion. We could reword this whole section probably, but I 
don't think that is justified to just fix this current bug.

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

!topic Time_Zone parameter to Calendar.Formatting subprograms
!reference Ada 2012 AARM 9.6.1
!from Simon Wright 2019-05-30
!keywords UTC
!discussion

Many of the subprograms (e.g. Hour) take a Time_Zone parameter which
defaults to 0.

It's been suggested that this default means "in the local time zone".

GNAT's treatment is to regard 0 as meaning "in UTC". This seems
reasonable, since Time_Offset'(0) is what you get when calling
UTC_Time_Offset in time zone GMT.

Neither ARM nor AARM seem to define the behaviour.

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

From: Tucker Taft
Sent: Thursday, May 30, 2019  1:49 PM

Doesn't 9.6.1(40/2) answer this question?  Here it is:

     Type Time_Offset represents the number of minutes difference between 
     the implementation-defined time zone used by Calendar and another time 
     zone.

If a value of this type is zero, that implies you are interested in an answer 
that is relevant to the time zone used by Calendar.  And as you suggest, that 
is determined by calling UTC_Time_Offset.

So what additional words would you expect to see in the RM?

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

From: Simon Wright
Sent: Thursday, May 30, 2019  3:18 PM

>Doesn't 9.6.1(40/2) answer this question?  Here it is:
>
>     Type Time_Offset represents the number of minutes difference between 
>     the implementation-defined time zone used by Calendar and another time 
>     zone.
>
>If a value of this type is zero, that implies you are interested in an answer 
>that is relevant to the time zone used by Calendar.  And as you suggest, that 
>is determined by calling UTC_Time_Offset.

Given the first sentence, not sure the second is relevant?

I do see what you mean, eventually, but both I and GNAT's implementors seem to
have got it wrong. In my case, perhaps because being in BST at the moment, the
60 minutes difference between BST and UTC is the same as the 60 minutes added 
when we went to daylight savings. Or perhaps, UTC being universal, I convinced
myself that a Time_Zone parameter of 0 must mean UTC.

If I run the attached program on GNAT (macOS & Linux) I get

$ date
Thu 30 May 2019 20:55:48 BST
$ ./time_zone
UTC_Time_Offset:  60
Time_Zone default: 2019-05-30 19:55:52
Time_Zone  offset: 2019-05-30 20:55:52

so to get local time I had to use Time_Zone => UTC_Time_Offset (Now).

>So what additional words would you expect to see in the RM?

Add to 9.6.1(40/2) "Thus, when 0 is passed as the Time_Zone parameter in 
subprograms below, this means to use the time zone used by Calendar."

Alternatively, add to 9.6.1(53/2) "When 0 is passed as the Time_Zone parameter 
in this and in subprograms below, this means to use the time zone used by 
Calendar."

--- Attached program:

with Ada.Calendar.Formatting;
with Ada.Calendar.Time_Zones;
with Ada.Text_IO;
procedure Time_Zone is
   Now : constant Ada.Calendar.Time := Ada.Calendar.Clock;
   Offset : constant Ada.Calendar.Time_Zones.Time_Offset
     := Ada.Calendar.Time_Zones.UTC_Time_Offset (Now);
begin
   Ada.Text_IO.Put_Line
     ("UTC_Time_Offset: " & Offset'Image);
   Ada.Text_IO.Put_Line
     ("Time_Zone default: "
        & Ada.Calendar.Formatting.Image (Now));
   Ada.Text_IO.Put_Line
     ("Time_Zone  offset: "
        & Ada.Calendar.Formatting.Image (Now, Time_Zone => Offset));
end Time_Zone;

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

From: Tucker Taft
Sent: Thursday, May 30, 2019  5:16 PM

>>Doesn't 9.6.1(40/2) answer this question?  Here it is:
>>
>>     Type Time_Offset represents the number of minutes difference between 
>>     the implementation-defined time zone used by Calendar and another time 
>>     zone.
>>
>>If a value of this type is zero, that implies you are interested in an answer 
>>that is relevant to the time zone used by Calendar.  And as you suggest, that 
>>is determined by calling UTC_Time_Offset.
>
>Given the first sentence, not sure the second is relevant?

I am not sure what the above refers to.  There are three sentences above, one 
copied from the reference manual, and two that were my conclusions based on 
that.   What did you think was not relevant?

>I do see what you mean, eventually, but both I and GNAT's implementors seem to
>have got it wrong. In my case, perhaps because being in BST at the moment, the
>60 minutes difference between BST and UTC is the same as the 60 minutes added 
>when we went to daylight savings. Or perhaps, UTC being universal, I convinced
>myself that a Time_Zone parameter of 0 must mean UTC.

But it seems pretty clear from the description that a time zone parameter of 0 
means "Calendar's timezone."  Of course, what does it mean to talk about 
Calendar's timezone if time (as returned by Calendar.Clock) is internally 
*always* number of seconds since some absolute time in the past?  Well it is 
defined by what "Calendar.Split" does.  An invariant should be that 
Calendar.Split and Calendar.Formatting.Split return essentially the same thing 
when the Time_Offset parameter is zero.

>If I run the attached program on GNAT (macOS & Linux) I get
>
>
>$ date
>Thu 30 May 2019 20:55:48 BST
>$ ./time_zone
>UTC_Time_Offset:  60
>Time_Zone default: 2019-05-30 19:55:52
>Time_Zone  offset: 2019-05-30 20:55:52
>
>so to get local time I had to use Time_Zone => UTC_Time_Offset (Now).

That seems a bit weird.  Of course what the system "date" program does is 
another question -- it need not match what Calendar.Split does.  But based on 
your program, it looks like Calendar.Formatting.Image when given an offset of 
zero gives UTC, even though the UTC_Time_Offset indicates that Calendar.Split 
gives BST.  So somebody is lying!

>So what additional words would you expect to see in the RM?
>
>Add to 9.6.1(40/2) "Thus, when 0 is passed as the Time_Zone parameter in 
>subprograms below, this means to use the time zone used by Calendar."

So, to be clear, this is formally redundant with what is there now -- you just 
are suggesting it would help clarify the meaning?

>Alternatively, add to 9.6.1(53/2) "When 0 is passed as the Time_Zone 
>parameter in this and in subprograms below, this means to use the time zone 
>used by Calendar."

So again, you are not saying there is anything wrong, but are suggesting that 
this extra wording would be helpful for understanding.  Am I understanding you 
correctly?

<time_zone.adb>

There does seem to be a bug in the RM 9.6.1(42/3) (from your other Ada Comment) 
about the definition of UTC_Time_Offset which should be <calendar-time> - UTC, 
rather than UTC - <calendar-time>, and perhaps also a bug in GNAT, in that if 
Time_Zone => 0, then you should be getting the same info as returned by 
Calendar.Split when you call Calendar.Formatting.Image/Split/etc.

Clearly not too many folks are using this Calendar.Formatting package!

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

From: Randy Brukardt
Sent: Friday, May 31, 2019  12:05 AM

> That seems a bit weird.  Of course what the system "date" program does 
>is another question -- it need not match what Calendar.Split does.  But 
>based on your program, it looks like Calendar.Formatting.Image when 
>given an offset of zero gives UTC, even though the UTC_Time_Offset 
>indicates that Calendar.Split gives BST.  So somebody is lying!

This is indeed the exact problem. This came up with Simon was trying to use 
my GNAT grading tools on his Linux box. One of the tools is called "Tstamp"
which just outputs a formatted time stamp to Standard Output. Simon was 
having problems because the time reported by the tests (via the ACATS Report 
package didn't match that of Tstamp.

The ACATS Report package originated in Ada 83 and thus uses Ada.Calendar.Split 
to display a time value.

When I wrote Tstamp, since it only needed to work with GNAT, I simplified my 
life and used Ada.Calendar.Formatting.Image. On Windows, I got the same result
for each (although I'm not certain which *compiler* I used to compile Tstamp).
But that isn't true on Linux, apparently.

>>>	So what additional words would you expect to see in the RM?

>>Add to 9.6.1(40/2) "Thus, when 0 is passed as the Time_Zone parameter 
>>in subprograms below, this means to use the time zone used by Calendar."

>So, to be clear, this is formally redundant with what is there now -- 
>you just are suggesting it would help clarify the meaning?

I couldn't convince myself that it was "formally redundant" as you claim, and 
I obviously failed to convince Simon. In any event, someone didn't understand 
it and it would help to say this.

Note that this case could be (and should be!) tested by an ACATS test, but I 
would want to be sure that we all agree that the RM says Time_Zone => 0 means
Calendar's time zone before spending the effort on it. (Essentially, call 
Calendar.Split on a time, and make sure that Calendar.Formatting.Split with 
the default time zone of 0 gets the same answers, as well as
Calendar.Formatting.Image.)

...
>There does seem to be a bug in the RM 9.6.1(42/3) (from your other Ada
>Comment) about the definition of UTC_Time_Offset which should be 
><calendar-time> - UTC, rather than UTC - <calendar-time>, and perhaps 
>also a bug in GNAT, in that if Time_Zone => 0, then you should be
>getting the same info as returned by Calendar.Split when you call 
>Calendar.Formatting.Image/Split/etc.
>
>Clearly not too many folks are using this Calendar.Formatting package!

The ACATS grading tools do use it. It would be good for it to work the same on 
all compilers (and certainly on all GNATs), else the grading tools will be 
wonky.

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

From: Simon Wright
Sent: Friday, May 31, 2019  7:50 AM

...
>>>If a value of this type is zero, that implies you are interested in an answer 
>>>that is relevant to the time zone used by Calendar.  And as you suggest, that 
>>>is determined by calling UTC_Time_Offset.
>>
>>Given the first sentence, not sure the second is relevant?
>
>I am not sure what the above refers to.  There are three sentences above, one 
>copied from the reference manual, and two that were my conclusions based on 
>that.   What did you think was not relevant?

I meant the 2nd and 3rd sentences, sorry. I think the conclusion of the 
discussion (so far) is that if (2nd sentence) I want answers relevant to 
Calendar's time zone I just supply an offset of 0, (3rd sentence) no need to 
invoke UTC_Time_Offset at all.

...
>So again, you are not saying there is anything wrong, but are suggesting that 
>this extra wording would be helpful for understanding.  Am I understanding 
>you correctly?

Yes, exactly.

>Clearly not too many folks are using this Calendar.Formatting package!

Or maybe GNAT users, including myself, are being lazy & adopting a "suck it 
and see" approach.

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

From: Tucker Taft
Sent: Friday, May 31, 2019  3:11 PM

...
>I meant the 2nd and 3rd sentences, sorry. I think the conclusion of the 
>discussion (so far) is that if (2nd sentence) I want answers relevant to
>Calendar's time zone I just supply an offset of 0, (3rd sentence) no need 
>to invoke UTC_Time_Offset at all.

No need to do so, but if you wanted to find out what was the timezone of 
package Calendar, you could use UTC_Time_Offset to do so,

...
>>So again, you are not saying there is anything wrong, but are suggesting 
>>that this extra wording would be helpful for understanding.  Am I 
>>understanding you correctly?

>Yes, exactly.

OK, thanks for clarifying.

>>Clearly not too many folks are using this Calendar.Formatting package!

>Or maybe GNAT users, including myself, are being lazy & adopting a "suck it 
>and see" approach.

Could be!

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

From: Randy Brukardt
Sent: Tuesday, June 4, 2019  9:36 PM

>	If I run the attached program on GNAT (macOS & Linux) I get
>
>	$ date
>	Thu 30 May 2019 20:55:48 BST
>	$ ./time_zone
>	UTC_Time_Offset:  60
>	Time_Zone default: 2019-05-30 19:55:52
>	Time_Zone  offset: 2019-05-30 20:55:52
>
>	so to get local time I had to use Time_Zone => UTC_Time_Offset
(Now).

If I run this program on Janus/Ada on Windows, I get:

D:\Testing\Win\console>time
The current time is: 21:11:22.97
Enter the new time:

D:\Testing\Win\console>tz
UTC_Time_Offset: -300
Time_Zone default: 2019-06-04 21:11:28
Time_Zone  offset: 2019-06-05 02:11:28

But if I run the program using GNAT, I get:

D:\Testing\Win\console>time
The current time is: 21:15:00.35
Enter the new time:

D:\Testing\Win\console>tz
UTC_Time_Offset: -300
Time_Zone default: 2019-06-05 02:15:02
Time_Zone  offset: 2019-06-04 21:15:02

Wonder what ObjectAda does?

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

From: Randy Brukardt
Sent: Wednesday, June 5, 2019  6:31 PM

> Wonder what ObjectAda does?

I sent PTC and Irvine this program (being the only other Ada 2005 compilers I 
know of).

Irvine responded that they don't implement Ada.Calendar.Formatting.

PTC sent a bunch of test results (in pretty screen grabs that I can't 
reasonably reproduce here). They pointed out that Simon had used the 
Object'Image, which is only a Ada 2012/TC1 feature. After fixing that 
problem, they reported the following results (hand keyed from the graphics, 
so if they don't make sense, blame me, not PTC).

For ObjectAda 10.1:

[...]> date
Wed Jun  5 10:29:48 PDT 2019
[...]> time_zone.exe

UTC_Time_Offset: -420
Time_Zone default: 2019-06-05 17:29:52
Time_Zone  offset: 2019-06-05 10:29:52

----

For ApexAda v5.2:

[...]$ date
Wed Jun  5 12:49:48 EDT 2019
[...]$ ./timezone
UTC_Time_Offset: -480
Time_Zone default: 2019-06-05 12:49:55
Time_Zone  offset: 2019-06-05 04:49:55

They also ran a copy of the ObjectAda program in France, giving:

[...]> date
Wed Jun  5 10:29:48 PDT 2019
[...]> time_zone.exe

UTC_Time_Offset: 120
Time_Zone default: 2019-06-05 08:49:07
Time_Zone  offset: 2019-06-05 10:49:07

=========================================================

Conclusion: I'm apparently the only one that actually read the RM definition 
and implemented it exactly. Everyone else implemented the Time_Zone value as 
being an offset from UTC, or didn't implement it at all.

Given the disruption inherent in forcing almost every compiler to change to 
the intended definition (which would break any existing code that is expecting 
a particular result, other than mine!), I think we have no choice but to 
revise the definition of Time_Offset to match that Tucker called "time zone".

This is annoying in that the intent was that the default for Image would be 
the local time (not UTC). Short of adding additional routines, though, I don't
see a way to do that and also change the RM meaning of Time_Offset.

So, I would propose the following changes:

(1) Change the definition of Time_Offset to match that Tucker used for 
    "time zone". We probably could drop that definition.
(2) Add a renaming of UTC_Time_Offset called Local_Time_Offset (which is a 
    better name for this concept after the inversion of the meaning of 
    Time_Offset).
(3) Add Local_Image, which would be defined to be Image (Time, 
    Include_Time_Fraction, Local_Time_Offset(Time));

Perhaps we should add a matching Local_Value, but that's problematical to 
define as the time offset to use possibly depends on the result. (It's much 
less likely to be a problem in practice, so maybe some English definition is
enough.)

I don't think it pays to add Local_ versions of all of the Time_Of and Split 
routines, as there are five of them (and the definition of Local_Time_Of 
would have the same problem as Local_Value).

What do you all think???

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

From: Joey Fish
Sent: Thursday, June 6, 2019  3:49 AM

> This is annoying in that the intent was that the default for Image would be
> the local time (not UTC). Short of adding additional routines, though, I
> don't see a way to do that and also change the RM meaning of Time_Offset.

I think the image should be in local time, isn't that [messing around with UTC 
and such] what the formatting/time_zones/conversion children of Ada.Calendar 
are about?

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

From: Tucker Taft
Sent: Thursday, June 6, 2019  12:53 PM

Perhaps we could add a default for UTC_Time_Offset/Local_Time_Offset to be 
Calendar.Clock?  Then you can just write "Local_Time_Offset" and use it as a 
default on a renaming of some of these routines to be Local_*.

Too bad this was so unclear to begin with, but it sounds like there couldn't 
have been much use of these features so far, given the semi-random results 
that are produced.

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

From: Randy Brukardt
Sent: Thursday, June 6, 2019  3:10 PM

> Perhaps we could add a default for
> UTC_Time_Offset/Local_Time_Offset to be Calendar.Clock?

That *is* the default to UTC_Time_Offset.

> Then
> you can just write "Local_Time_Offset" and use it as a default on a 
> renaming of some of these routines to be Local_*.

I thought of that, but it would be wrong for Local_Image and the like, as it 
wouldn't take into account time zone changes (i.e. summer/daylight saving
time) for some times. For *inbound* times, it seems nasty to get the time 
zone wrong just so we can write a renaming. After all, the reason for having 
a time parameter is to avoid that sort of glitch. (And since Clock is 
relatively expensive, it probably would be faster to use the passed in time
-- not that that would often matter.)

We could do that for *outbound* items (Value and Time_Of), should we try to 
support those.
 
> Too bad this was so unclear to begin with, but it sounds like there 
> couldn't have been much use of these features so far, given the 
> semi-random results that are produced.

Well, all of the compilers 'cept mine do the same thing (and mine hasn't been 
released yet), so its probably the case that most users got a surprising 
answer once, changed their code, and moved on. This probably never would have 
shown up had I not created a library that actually followed the RM and then 
used it in a publicly posted program. When someone (Simon) tried to use the 
program on GNAT, then the difference showed up, and Simon was good enough to 
raise it here (I don't know that I would have).

So it's quite possible this stuff is used a decent amount, just with a 
different expectation than the RM has.

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

From: Randy Brukardt
Sent: Thursday, June 6, 2019  8:33 PM

>>This is annoying in that the intent was that the default for Image would be
>>the local time (not UTC). Short of adding additional routines, though, 
>>I don't see a way to do that and also change the RM meaning of Time_Offset.
		
>I think the image should be in local time, isn't that [messing around 
>with UTC and such] what the formatting/time_zones/conversion children 
>of Ada.Calendar are about?

True, but we also have an obligation to standardize existing practice when 
possible. Do we want to break everyone's code in this area?

I'm suggesting that we split the baby by defining Local_Image to give the 
Calendar result and leave the existing Image to do what it does on all of the 
existing compilers except mine. (And note that Image is already in the 
"formatting/time_zones/conversion children of Ada.Calendar"; we're not talking 
about any change to Ada.Calendar.) It's not perfect but it is the least 
disruptive option. (It also would allow Ada runtimes to represent Time 
primarily in UTC time internally; I suspect that is the cause of this
confusion.)

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

From: Joey Fish
Sent: Friday, June 7, 2019  8:37 PM

>>>This is annoying in that the intent was that the default for Image would be
>>>the local time (not UTC). Short of adding additional routines, though, I
>>>don't see a way to do that and also change the RM meaning of Time_Offset.

>>I think the image should be in local time, isn't that [messing around with 
>>UTC and such] what the formatting/time_zones/conversion children of 
>>Ada.Calendar are about?

>True, but we also have an obligation to standardize existing practice when
>possible. Do we want to break everyone's code in this area?

Given the ARG's stance on backwards-compatibility, to the point of not 
eliminating features which were absolutely mistakes, yes.

One of the big failings of the SQL standard is they made so many of the 
features optional —in the name of existing implementations— so as to make the 
standard useless WRT portability for any non-trivial construction.

Given the ARG's goal of correctness, yes.

That implementations made a mistake is regrettable, and something that should 
be clarified to avoid in the future, but that is not the fault of the 
standard, is it?

(I mean if X many people misread "you shall not paint your truck red", 
dropping the 'not' and painting the truck red — is that the fault of the 
instructions?)

>I'm suggesting that we split the baby by defining Local_Image to give the
>Calendar result and leave the existing Image to do what it does on all of
>the existing compilers except mine. (And note that Image is already in the
>"formatting/time_zones/conversion children of Ada.Calendar"; we're not
>talking about any change to Ada.Calendar.) It's not perfect but it is the
>least disruptive option. (It also would allow Ada runtimes to represent Time
>primarily in UTC time internally; I suspect that is the cause of this
>confusion.)

Is there anything preventing that now?

I mean it could be represented internally as a monotonic time-stamp, with 
adjustments done in the image function, right?

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

From: Tucker Taft
Sent: Saturday, June 8, 2019  7:58 AM

>>... (It also would allow Ada runtimes to represent Time
>>primarily in UTC time internally; I suspect that is the cause of this
>>confusion.)

>Is there anything preventing that now?

No, in fact most Ada run-times on Unix-like systems already do exactly that.

>I mean it could be represented internally as a monotonic time-stamp, with 
>adjustments done in the image function, right?

Yes, that is how it is done in most Ada run-times that I have ever worked on.

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

From: Randy Brukardt
Sent: Monday, June 10, 2019  11:40 PM

>Yes, that is how it is done in most Ada run-times that I have ever 
>worked on.

That doesn't work well on Windows, however, as Windows gives 
Year:Month:Day:Hr:Min:Sec, and converting that into a "monotonic" value is 
rather complicated because of leap-years. Doing that on every call to Clock
is potentially too expensive. (There's a trade-off between the cost of 
Clock/Split and the cost of ordering operators; which makes the most sense 
depends on how often Clock is called vs. the operators.)

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

From: Joey Fish
Sent: Tuesday, June 11, 2019  10:24 AM

> That doesn't work well on Windows, however, as Windows gives
> Year:Month:Day:Hr:Min:Sec, and converting that into a "monotonic" value is
> rather complicated because of leap-years. Doing that on every call to Clock
> is potentially too expensive.

Indeed, which is why having monotonic time be the default/internal time-format 
makes more sense. (Though, I suppose you could argue that the PC-clock, which 
has been part of the PC since its inception, is dedicated hardware with an 
internal wall-clock format that handles all the messy stuff like leap-year and 
daylight-saving time and, thus, that provides for a natural internal/default 
for wall-clock date/time handling.) — Besides the fact that (a) formatting 
changes dependent on usage [eg "0800" vs "8:00 am"], and (b) things like 
daylight-savings time are put in place by legislation, and thus can be altered 
far easier than altering the silicon.
 
>(There's a trade-off between the cost of
>Clock/Split and the cost of ordering operators; which makes the most sense
>depends on how often Clock is called vs. the operators.)

I wonder if anyone's done any real analysis of clock vs. operators usage rates.

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

From: Jean-Pierre Rosen
Sent: Thursday, November 28, 2019  8:19 AM

I just came across this declaration in Ada.Calendar.Formatting:

function Local_Image (Date : Time;
                         Include_Time_Fraction : Boolean := False;
                         Time_Zone  : Time_Zones.Time_Offset := 0) return String is
      (Image (Date, Include_Time_Fraction, Local_Time_Offset (Date)));

The Time_Zone parameter is not included in the implementation, and this 
function seems to be intended to remove the nuisance of specifying the time 
zone. Cut and paste error?

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

From: Tucker Taft
Sent: Monday, December 2, 2019  7:17 AM

I would agree that the Time_Zone formal parameter should be omitted from the 
profile of Local_Image, since it is never used and is misleading since it 
implies that Local_Image is showing UTC time, when it is in fact showing the 
time in Ada.Calendar's time-zone.

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

From: Randy Brukardt
Sent: Monday, December 2, 2019  9:41 PM

It *is* omitted from the AI's !wording section, but the !corrigendum section 
and the draft RM include it. Almost certainly a cut-and-paste error (doubled,
so I didn't detect it using Pascal's matching tools).

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

From: Jean-Pierre Rosen
Sent: Tuesday, December 3, 2019  12:03 AM

> It *is* omitted from the AI's !wording section, but the !corrigendum 
> section and the draft RM include it.
Not in draft 23, that's where I saw it. It doesn't matter anyway, since 
everybody agrees.

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

From: Randy Brukardt
Sent: Tuesday, December 3, 2019  7:08 PM

???

"It" here is the junk time zone parameter. "It" is in draft 23, since as you 
say, that's where you saw it. But "it" isn't in the AI's !wording section, so 
the text the ARG approved did not have "it".

Since the !corrigendum and the draft RM both contain the same mistake, the 
matching tool (built by Pascal years ago) that normally would (and does) find 
such typos didn't catch it.

In any case, it's fixed in my working draft and I repaired the AI as well.

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

Questions? Ask the ACAA Technical Agent