Re: How to Compute Hours In a Day?

2008-10-20 Thread jim . monty
Eugene van der pijll wrote:
Jim Monty schreef:
 I want to iterate the time zones of the world to generate a report of
 all days that aren't exactly 24 hours. I want to handle
 America/Caracas and America/Sao_Paulo correctly. You mentioned
 2007-12-09 was 24.5 hours long in Venezuela. This is precisely the
 kind of outlier I'm after.

 If you want to handle the Sao Paulo situation correctly, measure the
 intervals between two middays (12:00), instead of between midnights (0:00).

 It should show the same non-24 hour days, except when there are two DST
 changes in one day. Which is unlikely, but not impossible, given that
 DST is defined by politicians.

It seems there's no one-size-fits-all workaround for all time zones in all 
years.

Here's my first shot at a script. It combines Zefram's suggestion to use 
$dt-epoch() and Eugene's recommendation to measure the intervals at noon 
instead of at midnight. Next, I'd like to refactor it using...um...I'm not 
sure, but something other than $dt-epoch().

    #!perl
    
    use strict;
    use warnings;
    
    use DateTime;
    
    @ARGV or die Usage: perl $0 year ...\n;
    
    for my $year (@ARGV) {
    for my $time_zone (DateTime::TimeZone-all_names()) {
    my $today = DateTime-from_day_of_year(
    year    = $year,
    day_of_year = 1,
    hour    = 12,
    time_zone   = $time_zone,
    );
    
    (my $yesterday = $today-clone())-subtract( days = 1 );
    my $hours = ($today-epoch() - $yesterday-epoch()) / 3_600;
    my $year_today = $today-year();
    
    while ($year_today == $year) {
    (my $tomorrow = $today-clone())-add( days = 1 );
    
    my $date = $today-date();
    
    if ($hours != 23  $hours != 24  $hours != 25) {
    printf(%-10s  %-4s  %s\n, $date, $hours, $time_zone);
    }
    
    $hours = ($tomorrow-epoch() - $today-epoch()) / 3_600;
    $today = $tomorrow;
    $year_today = $today-year();
    }
    }
    }
    
    exit 0;
    
    __END__


    C:\perl oddhours.pl 2007
    2007-12-09  24.5  America/Caracas
    2007-03-11  22    America/Indiana/Winamac
    2007-03-25  24.5  Australia/Lord_Howe
    2007-10-28  23.5  Australia/Lord_Howe
    
    C:\

It's easy enough to find stranger and even more problematic dates.

    C:\perl oddhours.pl 1961
    1961-01-01  23.74583  Africa/Dar_es_Salaam
    1961-08-10  23.5  Asia/Seoul
    
    C:\perl oddhours.pl 1967
    Invalid local time for date in time zone: Africa/Casablanca
    
    C:\

Since this is my first effort to do something quasi-useful with DateTime using 
DateTime math, all feedback, criticisms and suggestions are most welcome.

Jim Monty



Re: How to Compute Hours In a Day?

2008-10-20 Thread Eugene van der Pijll
[EMAIL PROTECTED] schreef:
 Here's my first shot at a script. It combines Zefram's suggestion to
 use $dt-epoch() and Eugene's recommendation to measure the intervals
 at noon instead of at midnight. Next, I'd like to refactor it
 using...um...I'm not sure, but something other than $dt-epoch().

I think the script in my first mail comes close; I'd start there.

 It's easy enough to find stranger and even more problematic dates.
 
     C:\perl oddhours.pl 1961
     1961-01-01  23.74583  Africa/Dar_es_Salaam

In the 19th century, there were no time standards, and each city used
its own local time. At some point, times within a country were
standardized; often to an integer offset with GMT, sometimes first to
the local time of the capital.

Check out 1883, when the U.S. moved from local time to standard time
zones. That results in a lot of strange day lengths. Apparently,
Tanganyika moved to standard time in 1961.

     C:\perl oddhours.pl 1967
     Invalid local time for date in time zone: Africa/Casablanca

And so there are time zones that change at noon... Those wacky
politicians...

In Morocco in 1967, and only in that year, DST started on June 3, 12:00.

Eugene


Re: How to Compute Hours In a Day?

2008-10-20 Thread Zefram
[EMAIL PROTECTED] wrote:
    C:\perl oddhours.pl 2007
    2007-12-09  24.5  America/Caracas
    2007-03-11  22    America/Indiana/Winamac
    2007-03-25  24.5  Australia/Lord_Howe
    2007-10-28  23.5  Australia/Lord_Howe

Interesting, Australia/Lord_Howe does a half-hour DST shift every year.

    1961-01-01  23.74583  Africa/Dar_es_Salaam

That's the switch from an unaligned +02:44:45 to the regular +03:00.
Curiously, it went to +02:44:45 *from* +03:00 earlier, in 1948, and
before that (until 1931) it had used +02:37:08.  Strong odour of politics.

    C:\perl oddhours.pl 1967
    Invalid local time for date in time zone: Africa/Casablanca

Wow, someone *does* switch at noon.  Or did, once.  A quick look at the
tzdata files shows a handful of other noon switches, all one-offs.

I think you're going to have to give up the idea of using a fixed local
time each day as your reference point.  You need a function that finds
the earliest point that lies within a given calendar date in local time,
which will usually (but not always) be labelled 00:00.  It's probably
safer to work in UTC and convert to local, rather than work with local
time-of-day directly, to avoid trouble with ambiguous times of day
(there might be two instances of local 00:00 in one day).

This is still assuming that the extent of each local calendar day
is contiguous.  If the clocks go back an hour at 00:30 local time,
it would break your model.  The Alaskan zones stretch the model to
the limit, having in 1867 a jump back by 24 hours at 24:00 local time,
yielding a 48-hour day.  (Subjectively it was treated as two consecutive
Fridays, which would have had the same calendar date were it not for the
simultaneous switch from the Julian calendar to the Gregorian calendar.)
There are also some Pacific islands that have jumped across the IDL in
the other direction, skipping a calendar date altogether.

Anyway, have a go with this function:

sub day_start() {
my($zone, $y, $m, $d) = @_;
my $p = DateTime-new(time_zone=UTC, year=$y, month=$m, day=$d, 
hour=12);
(my $rd) = $p-utc_rd_values;
while(do { (my $l = $p-clone)-set_time_zone($zone); 
[$l-local_rd_values]-[0] } = $rd) {
$p-subtract(hours=1);
}
while(do { (my $l = $p-clone)-set_time_zone($zone); 
[$l-local_rd_values]-[0] }  $rd) {
$p-add(hours=1);
}
my $q;
while(1) {
($q = $p-clone)-subtract(minutes=1);
last if do { (my $l = $q-clone)-set_time_zone($zone); 
[$l-local_rd_values]-[0] }  $rd;
$p = $q;
}
while(1) {
($q = $p-clone)-subtract(seconds=1);
last if do { (my $l = $q-clone)-set_time_zone($zone); 
[$l-local_rd_values]-[0] }  $rd;
$p = $q;
}
return $p;
}

-zefram