Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Playing with time (or, a lament to POSIX::strftime's %Z)

by baphomet (Pilgrim)
on Dec 23, 2002 at 21:58 UTC ( [id://221980]=perlquestion: print w/replies, xml ) Need Help??

baphomet has asked for the wisdom of the Perl Monks concerning the following question:

Excellent Monks!

Last night, as I was toiling away at one of my current Everything Engine projects, I came upon an older item in my to do list that, while at first glance appearing quite simple, has now consumed (at least the anal-retentive part of) my mind ;)

The task is this: given a date and time in GMT, I want to be able to convert that date to an arbitrary time zone, respecting daylight savings and strange (i.e. not simply +1 or -1) hour offsets, and pretty-print the result for human consumption.

The method I hit upon (that doesn't employ Date::Manip, at any rate) goes something like this:

sub format_date { my ($epoch, $string, $timezone) = @_; local %ENV; $ENV{TZ} = $timezone || 'GMT'; return strftime($string, localtime($epoch)); }

This works, for the most part, and plays nicely with Time::ZoneInfo. However! POSIX::strftime's %Z and %z tokens are sometimes wrong. For instance, given a date during daylight savings and one during winter time, %Z is replaced with the standard time abbreviation (i.e. for America/Chicago %Z renders CST for both dates, which would be correct for America/Regina) and %z with only the standard time offset.

Time::CTime::strftime, meanwhile, simply regurgitates the value of %ENV{TZ}.

Is there a better way to be going about these date calculations? Ideally, I'd like to be able to display EST/EDT, NZST/NZDT, etc. just like date(1) ... one way would be to gut the zoneinfo files manually for their innards, another to build a table of EST -> EDT conversions... however, the first seems not incredibly efficient while the later seems incredibly kludgy.

Any suggestions, or fine rants about time ;), would be appreciated.

baphomet.

Update: Well, it looks like I neglected to notice Class::Date. Using the to_tz() method, you get results similar to what I've used above, except that Class::Date objects keep some better information about the date's time zone. For instance,

sub format_date { my ($isodate, $string, $timezone) = @_; my $gmtdate = Class::Date->new($isodate, 'GMT'); my $tzdate = $gmtdate->to_tz($timezone); return $tzdate->strftime($string); }

will return proper results for strftime's %Z. You can also access $tzdate->tz_offset to grab GMT offset, $tzdate->tz to grab the base timezone and $tzdate->tzdst to grab the "real" timezone accounting for DST as best as zoneinfo can.

Replies are listed 'Best First'.
Re: Playing with time (or, a lament to POSIX::strftime's %Z)
by seattlejohn (Deacon) on Dec 24, 2002 at 00:53 UTC
    As waswas-fng explains, this problem is much harder than it first appears. To the best of my knowledge, there is no strictly algorithmic way to compute the local time correctly for any arbitrary place on earth. In some countries, e.g. Israel, the start and end dates of daylight savings time are not directly computable, because they are decided upon each year by some sort of panel (though I suppose you could watch for their announcements and update your lookup tables). In other cases locations that do have "rules" for daylight savings time bend those rules because of special events.

    That's on top of the fact that individual countries may begin and end daylight savings time at different points in the year (and don't forget that summer and winter in the southern hemisphere are opposite what they are in the northern)... and begin and end dst at different times during the night (e.g. 1am vs. 2am). Oh, and there are some really arcane rules within countries as well. In the U.S. for example, Hawaii and Arizona don't observe dst, except for the portion of Arizona that is part of the Navajo reservation, which does. In Indiana, different counties are in different times zones; some observe dst, and some don't. There's some underlying logic to it, but without knowing precisely where a user is located relative to a basically arbitrary line on a map, you're going to have a tough time working it all out!

    (And in the way-OT department, here's my personal rant about time, since you asked: the Washington State Ferry schedule reads, "Noon is noted as 12:00 a.m. and one minute past noon as 12:01 p.m.; midnight is noted as 12:00 p.m. and one past midnight as 12:01 a.m." Isn't the opposite the way virtually everyone else in the world handles noon and midnight?)

            $perlmonks{seattlejohn} = 'John Clyman';

Re: Playing with time (or, a lament to POSIX::strftime's %Z)
by waswas-fng (Curate) on Dec 23, 2002 at 23:17 UTC
    Blah I touched on this at my old job -- it gets really ugly, really quick, hehe just check out this quote
    "Jordan In 1999, Jordan decided to implement summer time all year round."

    I don't know if you have dug in deap to see how evil all of this timezone and daylight savings gets but this site has more info: Offsite link webexhibits.org
    Anyways I really never came up with a _good_ solution to this problem I would love to see what you come up with.

    -Waswas

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://221980]
Approved by Mr. Muskrat
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (8)
As of 2024-04-23 10:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found