in reply to Re^2: Date::Manip and daylight savings
in thread Date::Manip and daylight savings

I'm afraid I have to disagree here. I fail to see why 'switch to DateTime' is the correct answer.

The fact of the matter is that 1 hour before 1:00 AM on a certain date was also 1:00 AM. Date::Manip is doing it right. The reason that the information doesn't look right is that the date is only partially being printed out (i.e. no timezone information).

You could always print out the dates as timestamps (seconds since the epoch) or you could print the date in UTC time (which doesn't use daylight saving time), or there are alternate answers depending on what you're really trying to do.

As for the second question (i.e. subtract one day), the answer is a bit trickier. The problem really is 'what do you mean by 1 day ago'. If you say 'the same time the day before', that's ambiguous twice a year. When you move the clock back (as we just did), the time 1:00 AM to 2:00 AM happens twice, so if you start at 1:30 AM the day after and move back 1 day, do you mean 1:30 AM in standard time or saving time? Worse, when you move the clock forward, you bypass 1:00 AM to 2:00 AM, so if you start at 1:30 AM the following day and move back 1 day, if you specify 1:30 AM, you have an invalid date.

Date::Manip defines a day as 24 hours which DOES lead to unexpected results... but it DOES lead to unambiguous and correct results.

It would be possible to come up with another definition for '1 day'... and I'm definitely considering it (just as soon as I have a definition which is meaningful and useful)..

So, again, I'm sorry. The correct answer is NOT to switch to DateTime. The correct answer is to figure out what you really want, and then do it. I assure you that both Date::Manip and DateTime are flexible enough to do what you want.

Replies are listed 'Best First'.
Re^4: Date::Manip and daylight savings
by BrowserUk (Patriarch) on Nov 07, 2011 at 16:02 UTC

    Bravo!

    More simply put, if the OP uses DateTime and does the same thing, he'll get the same result.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re^4: Date::Manip and daylight savings
by ikegami (Patriarch) on Nov 07, 2011 at 21:41 UTC

    Date::Manip defines a day as 24 hours which DOES lead to unexpected results... but it DOES lead to unambiguous and correct results.

    For those interested, DateTime has no such false equivalences. DateTime can

    • Add one year, which is not necessarily the same as adding 365 days (which it can also do).
    • Add one month, which is not necessarily the same as adding 30 days (which it can also do).
    • Add one day, which is not necessarily the same as adding 24 hours (which it can also do).
    • Add one minute, which is not necessarily the same as adding 60 seconds (which it can also do).

    On the other hand,

    • A year is always 12 months.
    • A week is always 7 days.
    • An hour is always 60 minute.
    • A second is always 1,000,000,000 nanoseconds.

    The passage I quoted makes it sound like Date::Manip doesn't correctly handle some or all of those. Please let me know if it can.

      Add one day, which is not necessarily the same as adding 24 hours (which it can also do).

      From DateTime docs.

      When math crosses a daylight saving boundary, a single day may have more or less than 24 hours.

      For example, if you do this:

      my $dt = DateTime->new( year => 2003, month => 4, day => 5, hour => 2, time_zone => 'America/Chicago', ); $dt->add( days => 1 );

      then you will produce an invalid local time, and therefore an exception will be thrown.

      However, this works:

      my $dt = DateTime->new( year => 2003, month => 4, day => 5, hour => 2, time_zone => 'America/Chicago', ); $dt->add( hours => 24 );
      and produces a datetime with the local time of "03:00".

      If all this makes your head hurt, there is a simple alternative. Just convert your datetime object to the "UTC" time zone before doing date math on it, and switch it back to the local time zone afterwards. This avoids the possibility of having date math throw an exception, and makes sure that 1 day equals 24 hours. Of course, this may not always be desirable, so caveat user!

        you will produce an invalid local time, and therefore an exception will be thrown.

        As it should. Returning an invalid local time (2003-04-06T02:00:00) would be wrong.

        Are you implying that Date::Manip behaves differently?

        Just convert your datetime object to the "UTC" time zone before doing date math on it, and switch it back to the local time zone afterwards.

        That's bad advice. It's just a very expensive way of doing $dt->add( hours => 24 );, which is very different than adding a day.

        use feature qw( say );; use DateTime; my $dt = DateTime->new( year => 2003, month => 4, day => 5, hour => 2, time_zone => 'America/Chicago', ); $dt->set_time_zone('UTC'); $dt->add( days => 1 ); $dt->set_time_zone('America/Chicago'); say $dt; # ERROR: 2003-04-06T03:00:00

        My question went unanswered. Is Date::Manip only able to add 24 hours, or is it also able to add one day?

Re^4: Date::Manip and daylight savings
by ChrisDennis (Sexton) on Nov 07, 2011 at 17:28 UTC
    Thanks for your reply, SBECK.

    Yes, a full date/time needs to include the timezone. So why does Date::Manip just return a string that has no timezone in it? Is there a way to make it append the timezone?

    And yes, the second question depends on what I mean by 'one day ago'. In this case, I mean 'the same time yesterday'. I don't think that's ambiguous: if I'm supposed to turn up to work at 8am on Friday, and do the same on Monday after the change to or from summer time, it's up to me to make the adjustment. In both cases, it's assumed that we're talking about 'local time'.

    I hoped that Date::Manip would do that if I used 'approximate' mode, but apparently not.

    Is there another option to get Date::Manip to do what I want?

    My definition of '1 day' is: 24 hours, unless the period spans the change to/from daylight saving, in which case it's either 23 or 25 hours (in most countries).

    If I want to go back 24 hours, I can use a delta such as '-0:0:-0:0:24:0:0'. But if I want to go back 1 day, then I feel that '-0:0:-0:1:0:0:0' should do the right thing.

    cheers

    Chris

      If you'll switch to using the Object Oriented interface of Date::Manip, there won't be any problem. The functional interface is limited by being backward compatible with the old version of Date::Manip (which didn't work well with timezones). Look at the man page for Date::Manip for information on how to use it. Then, you would write:

      #!/usr/bin/perl use 5.10.1; use strict; use warnings; use Date::Manip::Date; my $date1 = new Date::Manip::Date; my @dates = ('2011103003:00:00', '2011103002:00:00', '2011103001:00:00', '2011103000:00:00'); my $delta1 = $date1->new_delta(); $delta1->parse('-0:0:-0:0:1:0:0'); foreach my $date (@dates) { $date1->parse($date); my $date2 = $date1->calc($delta1); print "date: ",$date1->printf('%g')," ", "date2: ",$date2->printf('%g'),"\n"; }

      If you want different information, use different printf formats.

      The 'one day ago' question cannot be defined to be 'the same time yesterday' because that's not guaranteed to exist. The definition that I'm leaning to (when/if I implement it) is:

      The same time yesterday with the same offest as today. If that doesn't exist, then the same time yesterday in the other offset. If that doesn't exist, then 24 hours in the past.

      A definition like that is the only way to handle all of the daylight saving time changes, and I'm strongly considering implementing it (in approximate mode only). It probably gives the results that people expect most of the time (though that one time when the third case kicks in is going to confuse people... but daylight saving time is by definition confusing :-).

      I won't use the 23/24/25 hour definition because that's even less well defined unless you're strictly talking about midnight to midnight calculations. If you do a 1:30 AM to 1:30 AM day, the rules would be non-intuitive (though you could certainly express the calculation in those terms).

      One way to get what you want is to do the calculation and then explicitly set the time... but be careful. You can't set the time if to an invalid time in your timezone.

        Thanks for that sample code -- I can see that I'll have to grapple with the OO interface, and code some special cases for when the target time doesn't exist.

        You're right about my definition of a day -- I'd forgotten about the missing hour in the spring.

        cheers

        Chris