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

I currently use the method subtract_datetime to determine the remaining time available. In some cases the results returned are correct. In some cases the results returned are way off.

Below is how it is being used.

<p>my $endDate = DateTime->new(year => 2015, month => 02, day => 24, t +ime_zone => 'America/New_York');
# the DateTime object, $endDate, normally comes from a database. But I created a DateTime object for this example. #

<p>my $dt1 = DateTime->now( 'time_zone' => 'America/New_York' ); my $duration_obj = $endDate->subtract_datetime($dt1) if $user_obj;</p> <p>print "Expires in: " . $duration_obj->months() . " months, " . $dur +ation_obj->weeks() . " weeks, " . $duration_obj->days() . " days";</p +>

As I indicated, sometimes the results are correct and sometimes it isn't. Is there something I am doing wrong? Or is there another method I can use with DateTime to return consistent results?

***EDITED***

So, if $endDate = "2015-02-24", the result I am getting is 5 months, 2 weeks, 0 days. Whereas, the result should be 0 months, 0 weeks, 5 days, based on today's date (2015-02-19).

However, if $endDate = "2015-06-16' the result is: 3 months, 3 weeks, 3 days. That is correct.

Replies are listed 'Best First'.
Re: Problem with DateTime subtract_datetime
by Anonymous Monk on Feb 18, 2015 at 21:44 UTC

    Could you give a concrete example where this does not produce the expected output so we can reproduce the problem?

    The DateTime documentation for the "subtraction" methods require careful reading. For example, from subtract_datetime (emphasis mine): "The returned duration may have deltas for months, days, minutes, seconds, and nanoseconds." (note there is no mention of weeks). And DateTime::Duration objects have conversion methods to convert between different measures of time, and conversions aren't always possible (see the module's documentation). Personally I try to stay away from this kind of math.

    Also, what kind of difference are you looking for? A "logical" difference, like "2003-03-15 minus 2003-02-15 = 1 month" (that's the example from subtract_datetime, note how the answer is not given in days or weeks), or a "absolute" difference, that is, an exact difference between two points in time (in seconds/nanoseconds, which could then be converted to other measures)? Because if it's the latter, you need subtract_datetime_absolute:

    my $dt1 = DateTime->from_epoch( epoch=>1403785930, time_zone=>'Europe/London' ); my $dt2 = DateTime->new( year=>2014, month=>6, day=>26, hour=>14, minute=>33, second=>03, nanosecond=>400e6, time_zone=>'Europe/Paris' ); my $diff_sec = $dt1->subtract_datetime_absolute($dt2) ->in_units('nanoseconds')/1e9; print "$diff_sec s\n"; __END__ -53.4 s
Re: Problem with DateTime subtract_datetime
by poj (Abbot) on Feb 18, 2015 at 22:01 UTC
    Try
    #!perl use strict; use DateTime; my $endDate = DateTime->new( year => 2015, month => 03, day => 24, time_zone => 'America/New_York' ); my $dt1 = DateTime->now( 'time_zone' => 'America/New_York' ); my $dur = $endDate->subtract_datetime($dt1); my ($m,$w,$d) = $dur->in_units( 'months','weeks','days' ); print "Expires in: $m months, $w weeks, $d days";
    poj

      poj,

      Thanks for the sample.

      I modified your sample a little and was surprised with the results. See below: (Note: I have never used the 'in-units' before!)

      use strict; use DateTime; my $endDate = DateTime->new( year => 2015, month => 03, day => 30, time_zone => 'America/New_York' ); my $dt1 = DateTime->now( 'time_zone' => 'America/New_York' ); my $dur = $endDate->subtract_datetime($dt1); my ($m,$w,$d) = $dur->in_units( 'months','weeks','days' ); print "Expires in: $m months, $w weeks, $d days\n"; ($w,$d) = $dur->in_units( 'weeks','days' ); print "Expires in: $w weeks, $d days\n"; ($d) = $dur->in_units( 'days' ); print "Expires in: $d days\n"; __END__ Results: Expires in: 1 months, 1 weeks, 3 days Expires in: 1 weeks, 3 days Expires in: 10 days

      Could this be the source of the OP's discrepancies?

      Regards...Ed

      "Well done is better than well said." - Benjamin Franklin

        If so, the issue that months cannot be converted into days, since not all months have the same number of days. He would find $endDate->delta_days($dt1) more appropriate to his needs than $endDate->subtract_datetime($dt1)
Re: Problem with DateTime subtract_datetime
by ikegami (Patriarch) on Feb 18, 2015 at 21:31 UTC
    • Please fix your formatting.
    • Please fix you description of when the problem occurs to be more precise than "sometimes".
    • Please fix your description of the problem to be more specific than "isn't correct" by by specifying both the output you obtained and the output you desire.
Re: Problem with DateTime subtract_datetime
by runrig (Abbot) on Feb 18, 2015 at 21:40 UTC
    Seems to work okay for me. Give an example that "doesn't" work. Possibly use the 'in_units()' method to return something less confusing?

      So, if $endDate = "2015-02-24", the result I am getting is 5 months, 2 weeks, 0 days. Whereas, the result should be 0 months, 0 weeks, 5 days.

      However, if $endDate = "2015-06-16' the result is: 3 months, 3 weeks, 3 days. That is correct.

        I get the correct answer, although it's '4 days 11 hours'. Perhaps you should be using 'today()' instead of 'now()' ? Also, try to write an example that doesn't use today() or now(), and uses fixed dates, so that the example will behave the same tomorrow.
        Please note, my response is based on today's date (2015-02-19).
Re: Problem with DateTime subtract_datetime
by Anonymous Monk on Feb 18, 2015 at 22:01 UTC

    Maybe you just need better formatting of your DateTime::Duration?

    use DateTime; use DateTime::Format::Human::Duration; my $endDate = DateTime->new(year => 2015, month => 02, day => 24, time_zone => 'America/New_York'); my $now = DateTime->now( 'time_zone' => 'America/New_York' ); my $durfmt = DateTime::Format::Human::Duration->new(); print $durfmt->format_duration_between($now, $endDate), "\n"; __END__ 5 days, 6 hours, 58 minutes, and 30 seconds
Re: Problem with DateTime subtract_datetime
by phildeman (Scribe) on Feb 19, 2015 at 18:52 UTC

    Thanks for trying to assist me with the DateTime's method, subtract_datetime.

    My code was working fine. The problem was with the database call. I was querying the database, based on a user ID,
    to obtain the endDate on the user. I was also limiting the result set to 1.

    I found there were users that had multiple rows, and the query results were returning the first instance of the result set.
    Apparently, each instance of the same user had a different endDate.

    But, thanks again for your help.

    BTW, you can use accessors to get months, weeks, and days from duration, as I did in the sample code.