!standard 9.6(24) 08-10-17 AI05-0119-1/00 !standard 9.6.1(41/2) !standard 9.6.1(42/2) !class Amendment 08-10-17 !status work item 08-10-17 !status received 08-08-05 !priority Medium !difficulty Hard !subject Package Calendar, Daylight Savings Time, and UTC_Offset !summary (See proposal.) !problem Ada.Calendar time is not well-defined in the presence of daylight saving time adjustments. 1. Ada.Calendar.Time_Of is ill-defined within the hour when the clock is adjusted backwards. The same year-month-day-seconds quadruple may denote two different dates. For example: year => 2008, month => 10, day => 26, seconds => 2.5 * 3600.0 may refer in Central Europe to 02:30 before the clock adjustment (02:30 CEST) or else a date 1 hour later, after the clock adjustment (02:30 CET). 2. The semantics of Ada.Calendar.Time is not defined. The following example illustrates the case: T0, T1, T2 : Time; begin T0 := Time_Of -– 01:30 CEST ( Year => 2008, Day => 26, Month => 10, Seconds => 1.5 * 3600.0 ); T1 := T0 + 3600.0; -- +1h T2 := T0 + 2.0 * 3600.0; -- +2h The Standard does not clarify whether T1 = T2 or not. A similar question can be asked about the time retrieved by Ada.Calendar.Clock at the times T1 and T2. (The answer may not be the same.) 3. Ada.Calendar.Time_Zones.UTC_Offset function is ill-defined because of the issue of 2. If T1 = T2, then UTC_Offset is broken because T1 is CEST and T2 is CET. These have different offsets to the UTC time, +2h and +1h correspondingly. !proposal ** TBD ** !wording ** TBD ** !discussion [Editor's note: To think about this in the US, use the time November 02, 2008 01:30 AM as the clock will be adjusted at 2:00 AM that day.] The basic problem is that a number of characteristics of Ada.Calendar were left unspecified. This is problematical, because it means that applications that depend on the clock (especially those that require UTC time) are not portable and may break occassionally when the clock is adjusted (depending on the implementation model. A number of fixes are possible: (1) Confirm the standard that these things are undefined. This surely won't break anything, but it also means that there is no way to write some applications in Ada (Ada.Real_Time does not have sufficient range to be used as a replacement). (2) Change the definition of Ada.Calendar.Time to ensure that some indication of the time zone and adjustment is included. Then operations like these can be defined unambigously. But that changes the representation of Ada.Calendar.Time on many implementations. (3) Define that Ada.Calendar.Time is UTC_Time, and provide a function to get local time. That's what Ada 83 ought to have done, but it seems too incompatable to change now. (4) Add wording to 9.6.1(41/2) to specify which result is returned in ambiguous cases. But that really doesn't help much, because there is then an hour of UTC time that can never occur. (5) Create a parallel universe of a UTC_Time package which works right. But that seems amazingly heavy. (6) A simplified proposal like the one found in the e-mail attached here. But that means that for most operations, UTC.Time has to be converted to Calendar.Time (since UTC.Time does not have constructors or splitters), which would clutter programs. There probably are even more approaches necessary. But first we have to decide whether to actually fix this problem. !example !ACATS test !appendix From: Dmitry A. Kazakov Date: Tuesday, August 5, 2008 3:57 AM 1. What is the result (according to the language reference manual) of: UTC_Time_Offset (Ada.Calendar.Time_Of (2008, 26, 10, 2.5*60.0*60.0)); called on a machine with the time zone Amsterdam, Berlin, Bern... Note. 26.10.2008 is the day when the clock will be adjusted 1h backward at 03:00. 2. What is the result of Ada.Calendar.Split applied to Ada.Calendar.Clock called: 2.a. at 26.10.2008 02:30 before clock adjustment; 2.b. one hour later (at 26.10.2008 02:30 after clock adjustment). ------------------- UTC_Time_Offset and Time_Of are ambiguous as defined in the RM. RM 9.6.1 42/2: "Returns, as a number of minutes, the difference between the implementation-defined time zone of Calendar, and UTC time, at the time Date." It is not a function. RM 9.6 24/2: "The functions Year, Month, Day, and Seconds return the corresponding values for a given value of the type Time, as appropriate to an implementation-defined time zone; the procedure Split returns all four corresponding values. Conversely, the function Time_Of combines a year number, a month number, a day number, and a duration, into a value of type Time." Split cannot be "converse," because it is irreversible. **************************************************************** From: Jeffrey R. Carter Date: Wednesday, August 6, 2008 2:16 PM > 1. What is the result (according to the language reference manual) of: > > UTC_Time_Offset (Ada.Calendar.Time_Of (2008, 26, 10, > 2.5*60.0*60.0)); > > called on a machine with the time zone Amsterdam, Berlin, Bern... This depends on the definition of the user-defined procedure UTC_Time_Offset. Presuming "use Ada.Calendar.Time_Zones;" and no user-defined procedure UTC_Time_Offset, a compiler error message, since Ada.Calendar.Time_Zones.UTC_Time_Offset is a function, and this is a procedure call. Presuming further that this is a fragment and does represent a function call, Constraint_Error should be raised at the point of the call, because 26 is not a value of Ada.Calendar.Month_Number. **************************************************************** From: Christoph Grein Date: Wednesday, August 6, 2008 11:47 PM I think your answer is a bit saucy, at least a bad joke. The question is absolute clear for any non-malevolent person :-( The RM is unclear about this, and the AARM (42.b/2, c/2) admits this. **************************************************************** From: Jeffery R. Carter Date: Thursday, August 7, 2008 2:09 PM > I think your answer is a bit saucy, at least a bad joke. > The question is absolute clear for any non-malevolent person :-( Perhaps. Is it unreasonable to expect precision from someone who is asking the ARG for precision? > The RM is unclear about this, and the AARM (42.b/2, c/2) admits this. I'm working on an application that deals with times in different time zones, though we never have to deal with times when the clock changes. I've found that understanding and reasoning about the definitions for dealing with time zones in the ARM requires careful attention, but the conclusions I've reached have agreed with the GNAT implementation so far. **************************************************************** From: Pascal Leroy Date: Thursday, August 7, 2008 3:06 AM > RM 9.6.1 42/2: > "Returns, as a number of minutes, the difference between the > implementation-defined time zone of Calendar, and UTC time, at the time > Date." > > It is not a function. I think that 42/2 is fine because UTC_Time_Offset *is* a function. But 44/2 could be worded better because Difference is *not* a function. Perhaps "Computes the difference between...". > RM 9.6 24/2: > > "The functions Year, Month, Day, and Seconds return the corresponding > values for a given value of the type Time, as appropriate to an > implementation-defined time zone; the procedure Split returns all four > corresponding values. Conversely, the function Time_Of combines a year > number, a month number, a day number, and a duration, into a value of type > Time." > > Split cannot be "converse," because it is irreversible. I think you are reading too much in the word "conversely". I don't believe that it was intended to imply reversibility. **************************************************************** From: Dmitry A. Kazakov Date: Thursday, August 7, 2008 3:30 PM > I think that 42/2 is fine because UTC_Time_Offset *is* a function. > But 44/2 could be worded better because Difference is *not* a > function. Perhaps "Computes the difference between...". That does not compute either. Because "the difference" implies that there is exactly one result for each argument, which is equivalent of being a mathematical function. ... > I think you are reading too much in the word "conversely". I don't > believe that it was intended to imply reversibility. If that is true, then the text becomes meaningless: "four corresponding values" of what? How they correspond etc. Basically the texts are inconsistent. Either: 1. Ada.Calendar.Time is defined to have different values (in the sense of the operation "=") at 26.10.2008 02:30 before the clock skew and after it. In this case Time_Of cannot be defined as it was, without specifying with of two dates is meant. 2. Ada.Calendar.Time has same values at both times, then Time_Of is well-defined but UTC_Time_Offset is not a function of the argument. Now which variant is mandatory? And what is the semantics of ill-defined things? P.S. As for existing implementations. GNAT under Windows seems to use the variant 1, with Time_Of returning the time after the skew. **************************************************************** From: Adam Beneschan Date: Thursday, August 7, 2008 7:01 PM > > I think that 42/2 is fine because UTC_Time_Offset *is* a function. > > But 44/2 could be worded better because Difference is *not* a > > function. Perhaps "Computes the difference between...". > > That does not compute either. Because "the difference" implies that > there is exactly one result for each argument, which is equivalent of > being a mathematical function. But there *is* one "difference". The part that returns "the difference" is a mathematical function. The result of this function is then broken into three parts and returned as three OUT parameters. I don't see the problem---it seems clear to me. And Ada-Comment regulars are aware that I'm a major nit-picker whenever I see wording in the RM that I think doesn't say what it means, or is ambiguous. But this paragraph doesn't bother me at all, although I can see why "Returns the difference" appears a little odd since Difference isn't an Ada function. ... > > I think you are reading too much in the word "conversely". I don't > > believe that it was intended to imply reversibility. > > If that is true, then the text becomes meaningless: "four > corresponding values" of what? How they correspond etc. It means that Split returns four OUT parameters, "Year", "Month", "Day", and "Seconds"; and these OUT parameters are set to the same values that the functions of the same names would return. Thus, Split (Date, Y, M, D, S) sets Y := Year(Date), M := Month(Date), and so on. That's what "corresponding" means. It has nothing whatsoever to do with the second sentence of paragraph 24, nor of the Time_Of function. Your issues regarding how this package works around Daylight Savings Time changes point out a real problem. But the questions about wording that you're asking above don't have anything to do with this issue. **************************************************************** From: Dmitry A. Kazakov Date: Friday, August 8, 2008 3:09 AM ... >> That does not compute either. Because "the difference" implies that >> there is exactly one result for each argument, which is equivalent of >> being a mathematical function. > > But there *is* one "difference". No, I meant UTC_Time_Offset, which depends on the definition of the political time. The customary, naive, definition seen in texts refers to "clock adjustment." This reads to me as 26.10.2008 02:30 would be exactly the same Berlin time before and after the clock skew. Otherwise, I don't know what the [political] clock does show. In my previous post it was denoted as the variant 2. The variant 1 referred to a hidden UTC clock which is inconsistent with Time_Of. [As well as with political time, in general. I greatly welcome GNAT's implementation dumping political time, but was it the intention of RM? I thought it wished to preserve political time...] ... > It means that Split returns four OUT parameters, "Year", "Month", > "Day", and "Seconds"; and these OUT parameters are set to the same > values that the functions of the same names would return. Thus, Split > (Date, Y, M, D, S) sets Y := Year(Date), M := Month(Date), and so on. > That's what "corresponding" means. It has nothing whatsoever to do > with the second sentence of paragraph 24, nor of the Time_Of function. How so? The wording is: "The functions Year, Month, Day, and Seconds return the corresponding values for a given value of the type Time ..." -- 9.6 24/2 I.e. the "correspondence" is to be between Year, Month, Day, Seconds and Time. Now, do the quadruple (y, m, d, s) unambiguously denote a political time date or not? RM is silent about that, though the wording let suggest that it does [=variant 2!]. Observe, that the quadruple does not [always] denote an UTC time [because seconds there are "political"], which is inconsistent with "the difference" allegedly returned by UTC_Time_Offset, etc. > Your issues regarding how this package works around Daylight Savings > Time changes point out a real problem. But the questions about > wording that you're asking above don't have anything to do with this > issue. My question was about the semantics. Actually, I don't care much about wording so long I can understand the meaning. Reading RM I was perplexed what is the expected behavior of UTC_Time_Offset, Time_Of and Split. P.S. The problem is quite technical. How to convert Time to UTC time and reverse? **************************************************************** From: Adam Beneschan Date: Friday, August 8, 2008 2:24 PM > How so? The wording is: > > "The functions Year, Month, Day, and Seconds return the > corresponding values for a given value of the type Time ..." -- 9.6 > 24/2 > > I.e. the "correspondence" is to be between Year, Month, Day, Seconds > and Time. Now, do the quadruple (y, m, d, s) unambiguously denote a > political time date or not? OK, I see. The word "corresponding" appears twice in this paragraph, and I thought you were asking about the use of the word in the phrase "four corresponding values", not the other use of the word. I'm beginning to think the RM is using this word too much, especially since a different issue that came up on this list today also involves how that same word is interpreted. I suggest that "corresponding" be added to the Glossary (Annex N), with the definition "We assume you know what we mean". :) :) Anyway, I was answering the question I thought you were asking, but I don't have an answer for the question you really wanted to know about. I'll have to leave that to others, for now. **************************************************************** From: Dmitry A. Kazakov Date: Thursday, August 14, 2008 10:31 AM !summary The proposal is to define the semantics of political time and to add UTC time. The proposal reuses as much as possible of the existing packages descending from Ada.Calendar. Ada.Calendar.Clock can be derived from UTC clock. !problem In presence of daylight saving: 1. Ada.Calendar.Time_Of is ill-defined within the hour when the clock is adjusted backwards. The same year-month-day-seconds quadruple may denote two different dates. For example: year => 2008, month => 10, day => 26, seconds => 2.5 * 3600.0 may refer in Central Europe to 02:30 before the clock adjustment (02:30 CEST) or else a date 1 hour later, after the clock adjustment (02:30 CET). 2. The semantics of Ada.Calendar.Time is not defined. The following example illustrates the case: T0, T1, T2 : Time; begin T0 := Time_Of – 01:30 CEST ( Year => 2008, Day => 26, Month => 10, Seconds => 1.5 * 3600.0 ); T1 := T0 + 3600.0; -- +1h T2 := T0 + 2.0 * 3600.0; -- +2h RM does not clarify whether T1 = T2 or not. 3. Ada.Calendar.Time_Zones.UTC_Offset function is ill-defined because of the issue of 2. If T1 = T2, then UTC_Offset is broken because T1 is CEST and T2 is CET. These have different offsets to the UTC time, +2h and +1h correspondingly. !proposal 1. Ada.Calendar.Time is defined as a political time. In the example: T0, T1, T2 : Time; begin T0 := Time_Of -- 01:30 CEST ( Year => 2008, Day => 26, Month => 10, Seconds => 1.5 * 3600.0 ); T1 := T0 + 3600.0; -- +1h T2 := T0 + 2.0 * 3600.0; -- +2h It is required T1 = T2. The time arithmetic is defined to satisfy this requirement. In particular, if a difference is calculated between two times of which one belongs to the overlapping hour, then this time is treated as having the same UTC offset as another. For example: T0, T1, T2 : Time; begin T0 := Time_Of – 01:30 CEST ( Year => 2008, Day => 26, Month => 10, Seconds => 1.5 * 3600.0 ); T1 := Time_Of – 02:30 CEST or CET ( Year => 2008, Day => 26, Month => 10, Seconds => 2.5 * 3600.0 ); T2 := Time_Of – 03:30 CET ( Year => 2008, Day => 26, Month => 10, Seconds => 3.5 * 3600.0 ); T2 – T1 = 3600.0, here T1 is considered CET as T2 is. T1 – T0 = 3600.0, T1 here is considered CEST. 2. Ada.Calendar.Time_Zones.UTC_Offset propagates Time_Error when the argument is ambiguous, like: Time_Of – 02:30 CEST or CET? ( Year => 2008, Day => 26, Month => 10, Seconds => 2.5 * 3600.0 ); 3. The following package is introduced: package Ada.UTC is type Time is private; function Clock return Time; function "+" (Left : Time; Right : Duration) return Time; function "+" (Left : Duration; Right : Time) return Time; function "-" (Left : Time; Right : Duration) return Time; function "-" (Left : Time; Time : Time) return Duration; function "<" (Left, Right : Time) return Boolean; function "<="(Left, Right : Time) return Boolean; function ">" (Left, Right : Time) return Boolean; function ">="(Left, Right : Time) return Boolean; end Ada.UTC; The package provides UTC clock, time and operations on it. 4. The package Ada.Calendar.Time_Zones contains four new functions: 4.a. function UTC_Time_Offset (Date : Ada.UTC.Time := Ada.UTC.Clock) return Time_Offset; This function returns the difference between the political time and the UTC time for its argument. The function is well-defined for each its argument. 4.b. function To_Calendar_Time (Time : Ada.UTC.Time; Time_Zone : Time_Offset) return Time; This function returns Ada.Calendar.Time corresponding to the argument for the time zone offset defined by the parameter Time_Zone. 4.c. function To_UTC (Time : Ada.UTC.Time) return Time; This function is equivalent to: To_UTC (Time, UTC_Time_Offset (Time)); 4.d. function To_UTC (Time : Ada.UTC.Time; Time_Zone : Time_Offset) return Time; This function converts UTC time to the political time with the time offset specified by the parameter Time_Zone. 5. Implementation. Ada.Calendar.Time and Ada.UTC.Time can be implemented as the same type. In which case Ada.Calendar.Clock could be directly derived from Ada.UTC.Clock: function Ada.Calendar.Clock return Ada.Calendar.Time is T : Ada.UTC.Time := Ada.UTC.Clock; begin return Ada.Calendar.Time (T + Ada.Calendar.Time_Zones.UTC_Time_Offset (T)); end Ada.Calendar.Clock; !examples 1. Conversion to UTC time: T : Ada.Calendar.Time := Ada.Calendar.Time_Of – 02:30 CEST or CET ( Year => 2008, Day => 26, Month => 10, Seconds => 2.5 * 3600.0 ); T_CEST : Ada.UTC.Time := Ada.Calendar.Time_Zones.To_UTC (T, 60); T_CET : Ada.UTC.Time := Ada.Calendar.Time_Zones.To_UTC (T, 2*60); 2. Conversion from UTC. Assuming the declarations above: Ada.Calendar.Time_Zones.To_Calendar_Time (T_CEST) = T Ada.Calendar.Time_Zones.To_Calendar_Time (T_CET) = T 3. Composition of UTC time from year-month-day-seconds: Ada.Calendar.Time_Zones.To_UTC ( Ada.Calendar.Formatting.Time_Of ( Year => 2008, Day => 26, Month => 10, Seconds => 2.5 * 3600.0, Time_Offset => 0 ), Time_Offset => 0 ); 4. Splitting UTC time into year-month-day-seconds: Ada.Calendar.Formatting.Split ( Date => Ada.Calendar.Time_Zones.To_Calendar_Time (T, 0), Year => Year, Month => Month, Day => Day, Seconds => Seconds, Leap_Second => Leap_Second, Time_Zone => 0 ); **************************************************************** From: Pascal Leroy Date: Thursday, August 28, 2008 6:02 AM >1. Ada.Calendar.Time is defined as a political time. In the example: > T0, T1, T2 : Time; >begin > T0 := Time_Of -- 01:30 CEST > ( Year => 2008, > Day => 26, > Month => 10, > Seconds => 1.5 * 3600.0 > ); > T1 := T0 + 3600.0; -- +1h > T2 := T0 + 2.0 * 3600.0; -- +2h > >It is required T1 = T2. The time arithmetic is defined to satisfy this >requirement. Taken at face value, this statement doesn't make any sense. If T1 = T2 then presumably T1 + 3600.0 = T2 + 3600.0, but since T1 + 3600.0 is T2, then T2 = T2 + 3600.0. I don't think that you imply this, but then it would be good to present an implementation model for your proposal, since it seems to require that the time values "remember" how they were obtained. (And that seems fairly horrendous.) > In particular, if a difference is calculated between two times of which one > belongs to the overlapping hour, then this time is treated as having the > same UTC offset as another. For example: This doesn't help if both times belong to the overlapping hour. And it is not clear that this is what you want in the other cases anyway. It seems to me that you are just trying to infer a timezone that was never provided to Time_Of in the first place. You might guess right some of the time, but that's not better than the current state of affairs. The only sound way to address the issues you are mentioning would be to have variants of Time_Of et al. that take the designation of a timezone (not only a time offset) and keep track of all the time changes that take place in that timezone. That was rejected long ago, because it seems absurd to have the Ada runtime know about all this timezone stuff, which changes periodically anyway. This is especially true in an embedded environment, where no-one cares about timezones. **************************************************************** From: Dmitry A. Kazakov Date: Friday, August 29, 2008 4:16 AM ... > Taken at face value, this statement doesn't make any sense. If T1 = > T2 then presumably T1 + 3600.0 = T2 + 3600.0, but since T1 + 3600.0 is > T2, No, T1 + 3600.0 /= T2. You assumed associativity of "+", which does not hold for the political time. In general: (T + D1) + D2 /= T + (D1 + D2) This is the case when T = T0 and D1, D2 = 3600.0. > then T2 = T2 + 3600.0. T1 + 3600.0 = T2 + 3600.0 > I don't think that you imply this, but then it would be good to > present an implementation model for your proposal, since it seems to > require that the time values "remember" how they were obtained. (And > that seems fairly horrendous.) The semantics of Time + Duration is well-defined: A straightforward implementation of is: 1. Time is converted to UTC. This operation is unambiguous when Time is outside the overlapping hours. When Time is in such an hour, then for positive Duration Time is converted to the UTC after time skew. When Duration is negative it is converted to the UTC before it. 2. Duration is added 3. Result is converted back to the political time. This operation is always unambiguous. >> In particular, if a difference is calculated between two times of >> which one belongs to the overlapping hour, then this time is treated >> as having the same UTC offset as another. For example: > > This doesn't help if both times belong to the overlapping hour. In this case the difference is calculated directly considering the UTC offsets equal. > And it is > not clear that this is what you want in the other cases anyway. It > seems to me that you are just trying to infer a timezone that was > never provided to Time_Of in the first place. It is true that arithmetic of political time cannot be consistently defined without knowing the timezone. It should not surprise anyone. Political time is garbage. The problem is not in the timezone, it is whether the time zone offset should be derivable from the values of Time. My proposal (T1=T2) is that it should not. Yet, an implementation is free to choose UTC, internally and then mangle +, -, =, in accordance to the "political will" (:-))... > The only sound way to address the issues you are mentioning would be > to have variants of Time_Of et al. that take the designation of a > timezone (not only a time offset) and keep track of all the time > changes that take place in that timezone. That would not define the semantics of Time. My proposal does it, by defining the political time in terms of UTC. This is how the political time is defined in the real world. > That was rejected long ago, because it seems absurd to have the Ada > runtime know about all this timezone stuff, which changes periodically > anyway. This is especially true in an embedded environment, where > no-one cares about timezones. Right, my proposal provides a way to define Time on the platform where UTC is unavailable. On these Time_Offset is considered constant 0. **************************************************************** From: Jeffery R. Carter Date: Sunday, February 15, 2009 10:03 PM I have to disagree with the proposal to add political time information to Ada.Calendar. It would break a great deal of my company's code because it is not backwards compatible. Ada.Calendar knows nothing of time zones, political time, or daylight savings time. This is clear from its definition in the ARM, from Ada 83 through Ada 95 and on to the current standard. A possible representation of type Time could be simply a record of the 4 components. Time_Of checks that the day is legal for the year and month, and handles the case of the seconds being Day_Duration'Last, but that is all. Split simply returns the 4 components. Whether the resulting time is meaningful in any specific political entity is unknown to the package. Clock returns some "system time", probably external to the language. This system time may know about some political entity and jump forward or backward, as the ARM specifically allows. In the case where the "system time" jumps backwards, Clock may return the same value twice. The meaning of those values is not something that Calendar does or can know about. The new packages added in 9.6.1 know about the system clock's UTC offset, and how to convert times from a given offset to and from the system clock's offset, but those offsets have no meaning relating to political time. How could they when arbitrary offsets with no political meaning may be specified? For example, the various Time_Of functions treat the time as having the specified offset and return the Time value for the time converted to the system clock's offset (a time of 12:00:00.00 with Time_Zone => 0 and UTC_Time_Offset = -420 [MST, -0700] would give a time of 05:00:00.00). The various Split procedures with Time_Zone parameters treat the Date as having the system clock's offset, and return the components of the Date converted to the specified offset. (Calling the parameter Time_Zone seems a bit misleading, since "time zone" usually refers to a political concept, and the parameter is really an arbitrary offset unrelated to any such concept.) If we want to have operations that know about political time in various political entities and their offsets, then we really need to deal with time zones specified as strings (since this is a malleable definition and cannot be represented by arbitrary numeric values or an enumeration type), such as "US/Mountain" and "Europe/Dublin". Clearly such operations do not belong in the existing packages. **************************************************************** From: Tucker Taft Date: Thursday, May 20, 2010 6:06 PM On our phone call today, we had a somewhat mind-bending discussion of calendar, time zones, and daylight-savings/summer time. We recognized that there are two distinct strategies for representing Calendar.Time, one where the time is kept in UTC internally, and Calendar.Split and Calendar.Time_Of worry about the "local" time zone. The other is that Time is represented in local time, and Calendar.Time_Of and Calendar.Split are trivial. Here are some interesting questions: What does "-"(Time, Time) return for times that cross a daylight/summer timezone change? Clearly if Time is represented internally as UTC time, this is very straightforward. If Time is represented as a local time that reflects daylight-savings/summer time, but with no internal bit indicating whether daylight/summer time was in effect when the local time was stored, then it seems clear that "-" will necessarily return a value 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. ****************************************************************