Re: How to Compute Hours In a Day?
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?
[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?
[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
How to Compute Hours In a Day?
How do I compute the number of hours in a day in a specific time zone? Let's say I want to iterate the days of the year from Jan 1 thru Dec 31 and print the number of hours in each day in the America/New_York time zone. I expect 363 days to have 24 hours, one day to have 23 hours, and one day to have 25 hours. I want to know how best to compute these numbers of hours in each day. Jim Monty
Re: How to Compute Hours In a Day?
[EMAIL PROTECTED] wrote: How do I compute the number of hours in a day in a specific time zone? Try this: use DateTime; for(my $today = DateTime-new(year=2007, time_zone=$ARGV[0]); $today-year == 2007; ) { (my $tomorrow = $today-clone)-add(days=1); print $today, , ($tomorrow-epoch - $today-epoch)/3600, \n; $today = $tomorrow; } It ought to be possible to do the interval calculation using DateTime::Duration instead of -epoch, but the behaviour of DT::D is unreasonably confusing and I couldn't get it to work. (Strangely, I got a version that worked fine for America/New_York, where all the days are integer numbers of hours, but lost the fractional hour of 2007-12-09 in America/Caracas, which was 24.5 hours long.) Beware that the above code assumes that midnight exists every day. We recently had a thread http://www.nntp.perl.org/group/perl.datetime/2008/10/msg7086.html about midnight not existing in some timezones, specifically America/Sao_Paulo. -zefram
Re: How to Compute Hours In a Day?
Zefram schreef: It ought to be possible to do the interval calculation using DateTime::Duration instead of -epoch, but the behaviour of DT::D is unreasonably confusing and I couldn't get it to work. (Strangely, I got a version that worked fine for America/New_York, where all the days are integer numbers of hours, but lost the fractional hour of 2007-12-09 in America/Caracas, which was 24.5 hours long.) DateTime cannot handle fractional leap hours. See the source code of subtract_datetime(), section This is a gross hack, where the length of the leap hour is hardcoded in the lines $bigger_min -= 60 $bigger_min += 60 This should probably be changed, but datetime math is scary. As a workaround, convert the time to UTC. (This does not work in all cases, but for this purpose, it does. use DateTime; for my $doy (340..345) { # The default time is 00:00:00 my $day = DateTime-from_day_of_year( year = 2007, day_of_year = $doy, time_zone = 'America/Caracas') or die; # To find the end of the day, add 1 day to get 00:00:00 tomorrow my $end_of_day = $day-clone-add( days = 1 ); # Workaround for Venezuela. $day-set_time_zone('UTC'); $end_of_day-set_time_zone('UTC'); # Standard subtraction would result in a duration of 1 day, so we # use delta_ms() which results in a duration without the days # component. It is expressed entirely in minutes, which can be # converted to hours. my $hours = $end_of_day-delta_ms($day)-delta_minutes / 60; print $day, has $hours hours.\n; } Eugene
Re: How to Compute Hours In a Day?
Zefram wrote: [EMAIL PROTECTED] wrote: How do I compute the number of hours in a day in a specific time zone? Try this: Thanks for the working example. It's helpful. It ought to be possible to do the interval calculation using DateTime::Duration instead of -epoch, but the behaviour of DT::D is unreasonably confusing and I couldn't get it to work. (Strangely, I got a version that worked fine for America/New_York, where all the days are integer numbers of hours, but lost the fractional hour of 2007-12-09 in America/Caracas, which was 24.5 hours long.) Beware that the above code assumes that midnight exists every day. We recently had a thread http://www.nntp.perl.org/group/perl.datetime/2008/10/msg7086.html about midnight not existing in some timezones, specifically America/Sao_Paulo. It's funny you should mention that thread. It's what inspired my little exercise, which I thought was going to be easier than it is. Here's what I *really* want to do: 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 outliers I'm after. So I'm looking for the best, most canonical, most true-to-the-spirit-of-DateTime way to accomplish this using, presumably, DateTime::Duration and possibly DateTime::Format::Duration. Jim Monty
Re: How to Compute Hours In a Day?
[EMAIL PROTECTED] 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 outliers 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. Eugene
Re: How to Compute Hours In a Day?
On Mon, 20 Oct 2008, Eugene van der Pijll wrote: DateTime cannot handle fractional leap hours. See the source code of subtract_datetime(), section This is a gross hack, where the length of the leap hour is hardcoded in the lines $bigger_min -= 60 $bigger_min += 60 This should probably be changed, but datetime math is scary. If you write some failing tests, I might be able to fix it. -dave /* http://VegGuide.org http://blog.urth.org Your guide to all that's veg House Absolute(ly Pointless) */