Clock and daylight saving time corrections

Arjen Markus (24 august 2005) The clock command is a wonderful instrument, despite all its quirks, if you need to do date/time computations. I ran into one quirk the other day that is not the fault of the implementation, but rather of the complicated calendar we use in today's world: daylight saving time.

Let me explain my problem:

  • I had a starting date in october and I wanted to know the end date after N spring-neap cycles (in my approximation: 15 days and 6 hours).
  • I used the clock command to do the computation and got some date in january, 7 o'clock as the stop date/time.

Then I realised that I had crossed the date where the daylight saving time correction changes. By using the option -gmt 1 you can avoid these complications.

Here is an illustration of the effect:

 # Daylight saving problems
 #
 # No care for daylight saving time corrections ...
 #
 set day1 [clock scan "2005-10-01"]
 set day2 [clock scan "2005-11-01"]
 set number_days [expr {($day2-$day1)/86400.0}]
 puts "Number of days: $number_days"

 # Option: -gmt 1
 #
 set day1 [clock scan "2005-10-01" -gmt 1]
 set day2 [clock scan "2005-11-01" -gmt 1]
 set number_days [expr {($day2-$day1)/86400.0}]
 puts "Number of days: $number_days"

The result (Tcl 8.4):

 Number of days: 31.0416666667
 Number of days: 31.0

Of course, it depends on what you want to achieve, if you need this option or not. Date/time computations are simply very complicated.


LV A friend sent me this pointer [L1 ] which discusses the fact that a number of countries have been, and continue to, make changes in the way DST is calculated. Does anyone know whether Tcl is tracking these kinds of adjustments?

MG As far as I know, all Tcl's time functions are platform-dependent, so as long as the underlying OS knows about them, Tcl will too.

LV Certainly in Tcl 8.5, there is now a large amount of timezone information. I just don't know enough about those files to know whether Tcl software has to change, given that there seems to be discussion of things like java changes. Just was curious.

MG tends to forget 8.5 - my comments were aimed more at 8.4. I'm not sure how the additional things in 8.5 work.

KBK answers LV's question with 'Yes.' Tcl is indeed tracking time zone changes, and you can expect that it will continue to do so. Unlike Windows, it also returns correct historical time information, at least back to 1970 or so. That said, on most Unix systems you will also need to keep 'zoneinfo' up to date; it's common to omit Tcl's tzdata from an installation if the local machine has the equivalent in 'zoneinfo'. Also, the ':localtime' time zone does whatever the underlying OS and C library do.

Knowing what part of the world Larry comes from, I must caution that it does not have correct information for certain West Virginia counties from the 1960's - the rules for Daylight Saving Time there were both controversial and contradictory. I doubt that very many people there actually knew what time it was in the next town over. I certainly doubt that they changed their watches half-a-dozen times in an hour-long bus ride simply because the different towns had different rules. (If memory serves, some had yet to adopt Standard Time and set the courthouse clock to local mean solar time!)

LV Amusingly enough, I lived in WV during that time period, and know EXACTLY the situation to which Kevin refers.

The article at [L2 ] includes a section titled Inconsistent use in the U.S. which makes reference to the situation - note that I lived about 5 seconds off Route 2, about 15-20 minutes south of the bus route mentioned...


LV If the date occurs before "1970 or so" , does Tcl default back to the OS's zoneinfo or whatever? Or does it just not try?

I understand that the timezone info isn't granular enough to handle the special cases of a particular county or town which decides not to observe DST.

KBK Your understanding is incomplete. Arthur Olson, from whom I get the data, makes an effort to keep the timezone info granular enough. Indiana, for instance, is divided into locales,

  • 'America/Indiana/Indianapolis',
  • 'America/Chicago' (these two cover most of the cases),
  • 'America/New_York' (Dearborn and Ohio Counties),
  • 'America/Kentucky/Louisville' (Clark, Floyd and Harrison Counties),
  • 'America/Indiana/Vincennes' (Daviess, Dubois, Knox, Martin, and Perry Counties),
  • 'America/Indiana/Marengo' (a township in Crawford County),
  • 'America/Indiana/Petersburg' (Pike County),
  • 'Anerica/Indiana/Winamac' (Pulaski County),
  • 'America/Indiana/Knox' (Starke County), and
  • 'America/Indiana/Vevay' (Switzerland County).

Generally speaking, a locale that has, since 1970, followed the same rules as a better-known place will have that better-known place's rules applied to dates before 1970. Thus, a resident of Moundsville, West Virginia, will see his clock keeping the Daylight Saving Time rules that were in effect for New York City.


LV Is there a way to write Tcl code that could be used to answer the question "for timezone ABC, on a particular date and time, was daylight savings time in effect"?

LV Later... I thought about using

 clock format now -format {Z} -timezone $whatever

and comparing the timezone against a known value. Alas, that fails, for example, in Melbourne Austraila, where the 3 letter abbreviation for non-daylight is EST - Eastern Standard Time and for daylight savings is EST - Eastern Summer (Daylight) Time.

Yuck.

LV Even later - I tried to poke inside Tcl 8.5's clock.tcl , to invoke the internal pieces of clock to get at the date and time parsing code, but so far, I've not success getting the right pieces to play nicely with one another.

KBK For some locales and times, the question isn't even meaningful. What do you want it to return for a locale that has advanced its clock two hours (e.g. Israel right after independence, or Newfoundland in 1988)? For a locale that temporarily reverts to solar time from Standard Time (Detroit in 1902)? For things like 'Eastern War Time'? You can use the %z format group to get the time zone's offset from Greenwich, and compare that against a known value, I suppose. If the intent of your question was to determine, "what is the offset from Greenwich in a particular locale on a particular date and time?" then %z is surely the answer. (Caveat: 8.5 only!)

LV My intent is to display an indicator if the time being displayed is considered daylight savings time. The other special times is not relevant in my particular situation. I have no idea why the original specifications cared about DST. But it did, and now I am trying to rewrite code in C, providing identical output, but with the better stability and more intelligent behavior of Tcl 8.5. At this point in time, I can't, because of the lack of a dst indicator.

Alastair Davies wonders about this:

 proc is_dst {time tz} {
        set year [clock format $time -format %Y -timezone $tz]
        set midsummer [clock scan "21 06 $year" -format "%d %m %Y" -timezone $tz]
        set midwinter [clock scan "21 12 $year" -format "%d %m %Y" -timezone $tz]
        set midsummer_offset [scan [clock format $midsummer -format %z -timezone $tz] %d]
        set midwinter_offset [scan [clock format $midwinter -format %z -timezone $tz] %d]
        
        if {$midsummer_offset == $midwinter_offset} {
                return -code error "DST not in use in locale $tz"
        }
        if {$midsummer_offset < $midwinter_offset} {
                #-- In southern hemisphere
                set temp $midwinter_offset
                set midwinter_offset $midsummer_offset
                set midsummer_offset $temp
        }
        set offset [scan [clock format $time -format %z -timezone $tz] %d]
        if {$offset == $midsummer_offset} {
                return true
        } elseif {$offset == $midwinter_offset} {
                return false
        } else {
                return -code error "Something crazy happened"
        }
 }

For example,

 is_dst [clock scan now] :Europe/London
 is_dst [clock scan now] :Australia/Sydney
 is_dst [clock scan now] :Australia/Perth

 set tz ":Israel"
 set time [clock scan "20 08 1948" -format "%d %m %Y" -timezone $tz]
 is_dst $time $tz

LV Wow - thanks!

Hey, can you check

 is_dst [clock scan now] :US/Pacific

For some reason, on my tcl 8.5 system, that seems to come up saying today is daylight savings time...

Alastair Davies Sorry. I've just added a sprinkling of scan offset %d to convert the reported offsets (in the form -0700 or +0100) to decimal numbers. These seem to compare more reliably.

 % set tz ":US/Pacific"
 :US/Pacific
 % is_dst [clock scan "11 4 2007" -format "%m %d %Y" -timezone $tz] $tz
 true
 % is_dst [clock scan "11 5 2007" -format "%m %d %Y" -timezone $tz] $tz
 false

Is this the right date for the change?

LV The examples you provide here appears to be right. Does this example look peculiar to you?

 puts "LA DST Apr 2, 2006? [is_dst [clock scan "April 01, 2006 03:00 AM"] :US/Pacific]"
 puts "LA DST Apr 1, 2006? [is_dst [clock scan "April 01, 2006 03:00 AM"] :US/Pacific]"
 puts "LA DST Mar 31, 2006? [is_dst [clock scan "March 31, 2006 03:00 AM"] :US/Pacific]"
 LA DST Apr 2, 2006? false
 LA DST Apr 1, 2006? false
 LA DST Mar 31, 2006? false

I would have thought that the Apr 1 and 2 examples would be true...

Alastair Davies A few points, in no particular order, and with absolutely no authority:

  • If the time change is made in the same way as in Europe, I would expect it to be at 2am on Sunday morning, i.e. 2 April 2006 (though the actual date is different from when Europe changes).
  • I understand the clock scan command in 8.5 should usually be used with a format specifier, to invoke the new parser. (Without a format specifier I believe the old ambiguities of the 8.4 parser can cause problems.) The clock scan command can also be used with a timezone specifier, to determine to what time zone the given string is referring. If your computer is not in the :US/Pacific time zone, the result will represent the time in your own time zone, not Los Angeles.
  • Bearing both these in mind, I am encouraged by these two results. (They could be wrong, but they are at least plausible.)
 % is_dst [clock scan "04/02/2006 01:00" -format "%m/%d/%Y %H:%M" -timezone :US/Pacific] :US/Pacific
 false
 % is_dst [clock scan "04/02/2006 03:00" -format "%m/%d/%Y %H:%M" -timezone :US/Pacific] :US/Pacific
 true
  • I think there is also a typo in your entry above, which may have been all that was puzzling you. (The first two commands both refer to April 01.)

LV Ah - the typo was my problem! I was looking at the calendar, seeing the Saturday, and thinking, in my old tired brain, that the time was changing on Saturday. I am SO embarassed. Things are working wonderfully. Thanks! Maybe we should see about getting this routine into either tcllib or tcl itself!


LV Different tangent - who actually creates the names for the timezones, anyways? I mean, the longer, supposedly preferred, names are all over the map, so to speak. For instance, I see:

  • :US/Eastern
  • :Europe/Paris
  • :Portugal

Why are they named as timezones in the US, cities in Europe, and whole countries other places? If the time is the same in an entire country, naming the timezone for the country makes sense to me. So why name a city instead of a country? If Paris has different time than other parts of France, I can understand having an entry for that. But shouldn't there be a :France or perhaps :Europe/France as well?

KBK Arthur David Olson is the curator of the names. The 'canonical' time zone names follow the rule of being named with the name of the continent followed by the largest city in the locale. Thus, the three names that you give are:

  • :US/Eastern - A 'legacy' synonym for :America/New_York
  • :Europe/Paris - A name that fits the system.
  • :Portugal - A 'legacy' synonym for :Europe/Lisbon

A very few locales contain no cities that anyone far from the locale would have heard of, and in fact the largest city in the locale would often result in a name collision with a more familiar place. For this reason, they have a third level interposed. The subtrees, :America/Argentina, :America/Indiana and :America/North_Dakota all fall in this category.

There is a large group of names that don't follow these rules, but all are aliased to ones that do. They are around because earlier versions of Olson's 'zoneinfo' had them, and hence they may be expected in the TZ environment variable. The entire :US hierarchy falls in this category.

Naming the time zone after the largest city has caused controversy; in particular, a number of PRC users have loudly demanded that :Asia/Shanghai be abolished (not just aliased!) in favour of :Asia/Beijing.

The reason for choosing a city name over anything else is that few locale splits divide a city. Locales seldom if ever merge because their historic times are distinct. The only case for a locale merge would be if a place announced a change of time zone, got added to the zoneinfo database, but rescinded the change before it went into effect (but after a tzdata release). A few of the Argentine locales are there because of such examples of rescission.

The mailing list archives for the discussion of the tz/zoneinfo database are at http://news.gmane.org/gmane.comp.time.tz and there is a lot of additional information at http://www.twinsun.com/tz/tz-link.htm