develooper Front page | perl.datetime | Postings from June 2012

working around nonexistent DateTimes (thanks to DST)

Thread Next
From:
Thomas Klausner
Date:
June 26, 2012 17:46
Subject:
working around nonexistent DateTimes (thanks to DST)
Message ID:
20120627004548.GA4256@xps.plix
Hi!

(long story at the top, suggested patch attached, grep for 'the patch' 
if you're in a hurry..)

Recently I again stumble upon the old problem/issue of non-existent 
datetimes caused by DST. As you all probably know, once a year a lot of 
countries just "steal" an hour. This hardly causes any problems, because 
it happens in the wee hours of the night.

But not in America/Sao Paulo (or Cairo, FWIW).

The problem with Sao Paulo is that the choose to remove the hour from 
0:00 to 0:59. Which makes this explode:

~$ perl -MDateTime -E 'say 
DateTime->new(year=>2012,month=>10,day=>21,time_zone=>"America/Sao_Paulo")'
Invalid local time for date in time zone: America/Sao_Paulo

Calling new without an hour defaults to hour=>0, which just doesn't 
exist on this particlar day.
This issue was brought up several times on this list in the past years, 
and the answer was "then use UTC, and convert later":

Now this is not always possible (eg I need to compare local timezone 
values coming from Perl with local time zone values stored in a DB, and 
not using the local time zone would be a hassle).

But what's really annoying is this inconsistency:

(note that we now start with 2012-10-20)
~$ perl -MDateTime -E 'say 
DateTime->new(year=>2012,month=>10,day=>20,time_zone=>"America/Sao_Paulo")->add(days=>1)'
Invalid local time for date in time zone: America/Sao_Paulo

But:
~$ perl -MDateTime -E 'say 
DateTime->new(year=>2012,month=>10,day=>20,time_zone=>"America/Sao_Paulo")->add(hours=>24)'
2012-10-21T01:00:00

I know that day math is complex, and adding days is different from 
adding hours (~seconds). But I still find it annoying to have to work 
around crazy corner cases (and basically having to pack every DateTime 
call into an eval)

Anyway, I couldn't sleep tonight (no, I'm not writing from America/Sao 
Paulo..), so I took a rather deep look into DateTime::TimeZone and after 
lots of clueless poking I can up with something that seems to work:

the patch:
When going through all the DST-changes (aka $tz->{spans}) in 
DateTime::TimeZone::_spans_binary_search I checked if the spans did not 
connect. If the didn't and if the local time ($seconds) falls between 
the gap, I just return the later span. Resulting in a sort of hackish 
workaround that sort of works (according to the also attached test case)

The rest of the test suite (DateTime and DateTime::TimeZone) mostly 
works, only the tests checking for DST-related exceptions fail.

With my patch, we now get:
~$ perl -MDateTime -E 'say 
DateTime->new(year=>2012,month=>10,day=>21,time_zone=>"America/Sao_Paulo")'
2012-10-21T01:00:00

Adding/Subtracting seconds or days behave the same.

The only potential deal-breaker is the fact that if you now create a new 
DateTime that does not exists, you'll get the next valid time instead of 
an exception (which does sound like a feature to me, but backwards
compability might have other ideas about that...)

Oh, and requesting a specific non-existed time also has strange results:
~$ perl -MDateTime -E 'say 
DateTime->new(year=>2012,month=>10,day=>21,hour=>0,minute=>30,time_zone=>"America/Sao_Paulo")'
2012-10-21T01:30:00

So, what do you think?

Totally crazy? Or any chance that this might be included in 
DateTime::TimeZone

Greetings,
domm

PS: Regarding Cairo: It seems that after beeing totally crazy and 
changing DST 4 times a year in 2010 (due to Ramadan), they now stopped 
doing DST altogether and thus went completely sane...
http://www.timeanddate.com/news/time/egypt-ends-dst-2010.html

-- 
#!/usr/bin/perl                              http://domm.plix.at
for(ref bless{},just'another'perl'hacker){s-:+-$"-g&&print$_.$/}


Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About