Search code examples
datetimeperl

Perl's datetime not returning correct differences in time


Inspired from https://perlmaven.com/datetime, I am attempting to find the number of hours of difference between 2024-01-03T19:00:00 and 2024-01-07T16:00:00 (93 hours).

#!/usr/bin/env perl

use 5.038;
use warnings FATAL => 'all';
use autodie ':default';
use DDP;
use Devel::Confess 'color';
use DateTime;

sub str_to_date ($date) {
    if ($date =~ m/^(\d+) # year
    \-(\d{1,2})           # month
    \-(\d{1,2})           # day
    T
    (\d+)                        # hour
    :
    (\d+)                        # minutes
    :
    (\d+)                        # seconds                  
    /x) {
        return DateTime -> new(
        year  => $1,
        month => $2,
        day   => $3,
        hour    => $4,
        minute=> $5,
        second=> $6,
    );
    } else {
        die "$date failed regex.";
    }
} # later dates are "greater"
my $wed_date = str_to_date('2024-01-03T19:00:00');
say $wed_date->day;
my $sun_date = str_to_date('2024-01-07T16:00:00');
my $diff = $sun_date - $wed_date; # returns a duration object

p $diff;
say $diff->clock_duration->in_units('hours'); # 21, but should be 93
say $diff->in_units('hours'); # 21 again
say $diff->hours; # 21 again
say $sun_date->delta_days($wed_date)->in_units('hours'); # 0
p $diff->deltas;

all of the methods that I have found from https://metacpan.org/pod/DateTime::Format::Duration are giving incorrect answers.

Alternatively, if I remove the parsing,

#!/usr/bin/env perl
use 5.038;
use warnings FATAL => 'all';
use autodie ':default';
use DDP;
use Devel::Confess 'color';
use DateTime;

my $wed_date = DateTime->new(
    year => 2024,
    month=>1,
    day=>3,
    hour=>19
);#str_to_date('2024-01-03T19:00:00');
my $sun_date = DateTime->new(
    year=>2024,
    month=>1,
    day=>7,
    hour=>16#my $sun_date = str_to_date('2024-01-07T16:00:00');
);#
say $wed_date->day;

my $diff = $sun_date - $wed_date;

p $diff;
say $diff->clock_duration->in_units('hours'); # 21, but should be 93
say $diff->in_units('hours'); # 21 again
say $diff->hours; # 21 again
say $sun_date->delta_days($wed_date)->in_units('hours'); # 0
p $diff->deltas; # 10

the answers are identical.

the DateTime::Duration object appears to be ignoring the days in between, and only focusing on the difference between the hours.

How can I get the correct # of hours between two dates?


Solution

  • Use ->delta_ms instead of ->subtract_datetime (the method invoked by subtraction).


    A duration measured in days can't be converted into hours, since not all days have the same number of hours. In fact, only these conversions are safe:

    • weeks ⇔ days
    • hours ⇔ minutes
    • seconds ⇔ nanoseconds

    As such, ->in_units( 'hours' ) only converts the hour and minute components of the duration into hours.

    $sun_date - $wed_date returns a duration of 3 days and 1260 minutes.
    ->in_units( 'hours' ) returns int( 0 + 1260/60 ) = 21 hours.

    The solution is to create a duration that consists of hours and/or minutes instead of days.

    $sun_date->delta_ms( $wed_date ) returns a duration of 5580 minutes.
    ->in_units( 'hours' ) returns int( 0 + 5580/60 ) = 93 hours.