Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Using zoneinfo/clock format to display localised time.

236 views
Skip to first unread message

Alex Collins

unread,
May 8, 2009, 7:04:24 AM5/8/09
to
I've been experimenting with allowing customers to view our web site
with all times in their time zone. While JavaScript provides a
mechanism to convert a Date to the localised time (toLocaleString), I
want to be able to allow the customer to choose their timezone from a
drop-down. I've come across the following complexities:

- DST can change at just a few days notice, so and DST data must be
kept up to date.
- There's a massive number of zones (over 200?) to keep up to date.
- To display a time, you need to know whether that time was in DST.
Think about how the time on a boarding card for a flight is always the
time in your destination.
- Zoneinfo has more zones (in zone.tab) that TCL8.5's 'clock format'.
How do you get a list of zones that TCL supports?
- 'clock' in TCL8.5 is 30x slower that in TCL8.4.

Kevin Kenny

unread,
May 8, 2009, 9:01:36 AM5/8/09
to
Alex Collins wrote:

> - DST can change at just a few days notice, so and DST data must be
> kept up to date.

> - There's a massive number of zones (over 200?) to keep up to date.

Yes indeed. There are several layers by which you can accomplish
this:
(1) On systems that have zoneinfo, Tcl uses it. (Alas,
Windows systems lack it.)
(2) On systems that lack zoneinfo, you can update manually
either by going to the latest Tcl patch release or by
grabbing the tzdata files from the CVS HEAD. We try to
keep them in sync with Olson.
(3) If for whatever reason you can't go after the tzdata from
Tcl sources, you can rebuild it yourself using the
'tclZIC.tcl' script in the 'tools' directory of a Tcl
source distribution.

> - To display a time, you need to know whether that time was in DST.

Of course. That's what zoneinfo (or tzdata) tell you.

> Think about how the time on a boarding card for a flight is always the
> time in your destination.

That's why there's now a -timezone option on [clock format];
airline ticketing is a classic example of a multilocale application.

> - Zoneinfo has more zones (in zone.tab) that TCL8.5's 'clock format'.

Tcl's [clock] command has exactly, precisely, the timezones that
zoneinfo does. They are built from the same sources. If you discover
an exception, log a bug.

> How do you get a list of zones that TCL supports?

There are a few built-in ones like :UTC and :localtime. Any
POSIX time zone specifier is also a time zone for Tcl, so there
is an open-ended set of strings like:
<MEZ>-01:00:00<MESZ>-02:00:00,M3.5.0/02:00:00,M10.5.0/01:00:00
The named timezones with historical information all correspond
to files in tzdata, so
ls -R $::tcl_library/tzdata
will find the lot of them.

> - 'clock' in TCL8.5 is 30x slower that in TCL8.4.

Yes, it is. The 8.4 [clock] didn't do any of this stuff, and
was written in C. The 8.5 version does all of it, and is written
in Tcl. Because that's what its implementors (me, mostly) had
time for. Life is full of tradeoffs. (How many people scan and
format times of day in inner loops, anyway?)

--
73 de ke9tv/2, Kevin

Alex Collins

unread,
May 8, 2009, 9:20:22 AM5/8/09
to
I was going to use TCL to generate a JS file which contains a list of
time-zones (probably not all of them). This JS file will contain
something similar to TZDate, I imagine something like the offset from
UTC, and the times of DST changes over a period of years (2000-2030)
for you chosen time-zone. This is what I've come up with so far (note
that it's somewhat naive, e.g. the DST changeovers are incorrect):

#! /opt/tcl8.5/bin/tclsh8.5

package require Tcl 8.5

namespace eval tz {
variable INIT 0
}

# Not all of these zones will be supported by TCL clock.
proc tz::get_zones {} {
init
return [array names tcl::clock::TZData]
}

proc tz::init {} {
variable INIT

if {$INIT} {return}

set INIT 1


_load_zones
}

# TCL loads its timezones lazily, so we force it to load them now
proc tz::_load_zones {} {
# force auto-loading
catch {clock scan}
foreach path $tcl::clock::ZoneinfoPaths {
set file [file join $path zone.tab]
if {[file exists $file]} {break}
}

set c [open $file]
set zones [list]
while {[gets $c l] >= 0} {
if {[string match #* $l]} {
continue
}
if {![regexp {^.. [^ ]+ ([^ ]+)} $l {} tz]} {
continue
}
if {[catch {
tcl::clock::SetupTimeZone :$tz
} msg]} {
puts stderr $msg
}
}
close $c
}

# Get the offset, without DST.
proc tz::get_offset {tz} {
set midwinter [clock scan "21 12" -format "%d %m" -timezone $tz]
set midwinter_offset [scan [clock format $midwinter -format %z -
timezone $tz] %d]
# lets ignore that fact that it might be summer in, for example,
Australia
return $midwinter_offset
}

proc tz::get_dst_changeovers {tz y} {
set midsummer [clock scan "21 06 $y" -format "%d %m %Y" -timezone
$tz]
set midsummer_offset [scan [clock format $midsummer -format %z -
timezone $tz] %d]
set midwinter [clock scan "21 12 $y" -format "%d %m %Y" -timezone
$tz]
set midwinter_offset [scan [clock format $midwinter -format %z -
timezone $tz] %d]

if {$midsummer_offset == $midwinter_offset} {
return [list]
}

if {$midsummer_offset < $midwinter_offset} {
#-- In southern hemisphere
set temp $midwinter_offset
set midwinter_offset $midsummer_offset
set midsummer_offset $temp
}

set dpm [list {} 31 [expr {$y % 4 == 0 ? 29 : 28}] 31 30 31 30 31 31
30 31 30 31]

set state 0
set last_dst 0
set times [list]
for {set m 1} {$m < 13} {incr m} {
for {set d 1} {$d < [lindex $dpm $m]} {incr d} {
set time [clock scan "$y-$m-$d 12:00:00" -timezone $tz]
set offset [scan [clock format $time -format %z -timezone $tz] %d]
set dst [expr {$offset == $midsummer_offset}]

if {$last_dst == $dst} {
continue
}

set dst [expr {$offset == $midsummer_offset}]

switch $state {
0 {
lappend times $time
set state 1
}
1 {
lappend times $time
set state 2
}
}

set last_dst $dst
}
}
return $times
}


foreach tz [lsort [tz::get_zones]] {
set e [list o:[tz::get_offset $tz]]
for {set y 2009} {$y < 2010} {incr y} {
if {[catch {
set times [tz::get_dst_changeovers $tz $y]
if {[llength $times] > 0} {
lappend e [format {%s:[%s]} $y [join $times ,]]
}
} msg]} {
puts "//$msg"
}
}
puts "tz.D\[\"$tz\"]={[join $e ,]};"
}

dkf

unread,
May 8, 2009, 9:42:39 AM5/8/09
to
On 8 May, 14:20, Alex Collins <alex....@gmail.com> wrote:
> I was going to use TCL to generate a JS file which contains a list of
> time-zones (probably not all of them). This JS file will contain
> something similar to TZDate, I imagine something like the offset from
> UTC, and the times of DST changes over a period of years (2000-2030)
> for you chosen time-zone. This is what I've come up with so far (note
> that it's somewhat naive, e.g. the DST changeovers are incorrect):

Predicting times of DST changes for two decades ahead of time is a
fool's errand. The issue is that politicians all over the world like
changing the rules, often with only very short notice. Since it's a
legal/political issue and not a technical issue, there is no fix
possible for this; we've just got to make do (and vote them out).

Donal.

0 new messages