Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make DateTime::Duration output only in days?

Tags:

perl

This code finds the difference between today and a fixed date.

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;

use DateTime ();
use DateTime::Duration ();
use DateTime::Format::Strptime ();

my $date = "23/05-2022";

my $parser = DateTime::Format::Strptime->new(
    pattern     => '%d/%m-%Y',
    time_zone   => 'local',
    );

$date = $parser->parse_datetime($date);

my $today = DateTime->today(time_zone=>'local');

my $d = DateTime::Duration->new($today - $date);

print Dumper $d->delta_days;

The problem is that is only outputs -22 days.

If I do print Dumper $d; I can see the -130 months as well.

$VAR1 = bless( {
                 'seconds' => 0,
                 'minutes' => 0,
                 'end_of_month' => 'preserve',
                 'nanoseconds' => 0,
                 'days' => -22,
                 'months' => -130
               }, 'DateTime::Duration' );

How do I get it to output the result in days?

Doing

print Dumper $d->delta_days + $d->delta_months*30;

doesn't seam like an elegant solution.

like image 333
Sandra Schlichting Avatar asked Jul 01 '11 14:07

Sandra Schlichting


2 Answers

From a quick read of the DateTime module doc, I don't believe that

DateTime::Duration->new($today - $date)

will do what you expect. I believe you need to use

$dur = $today->subtract_datetime($date)

The type of $dur is not immediately clear from the docs, however.

like image 27
Jim Garrison Avatar answered Sep 28 '22 08:09

Jim Garrison


At first you need to do the correct subtraction. There exists delta_md, delta_days, delta_ms and subtract_datetime_absolute. Depending on which unit you later want, you need to pick the right subtraction. The problem is that not every unit is convertible later without time_zone information. Thats the reason why you need to pick the correct delta method.

For example a day can have 23 Hours or 24 or 25 Hours, depending on the time zone. Because of that, you need to specify how the subtraction should work. Because you want the days later, the subtraction need to focus on days, rather focus on hours. Don't use the overload feature, because it only does a best fit.

That means you need to do a delta_days subtraction.

my $dur = $date->delta_days($today);

Now $dur is a DateTime::Duration object. You need to knew that it always tries to best fit the days, weeks, years, months if possible. That means your days will split in weeks and days. Because this conversion is always a constant.

If you don't want this "best fit" you need to call the in_units method and convert it only to days.

my $days = $dur->in_units('days');

But like i said before in_units only can do a conversion where it is possible. A call with in_units('hours') will not work on this object and just return a zero because you cant convert days to hours. If you want hours for example, you need to do a delta_ms, and on this object you can call in_units('hours')

The complete example:

#!/usr/bin/env perl
use 5.010;
use strict;
use warnings;

use DateTime;
use DateTime::Format::Strptime;

my $date = "23/05-2022";
my $parser = DateTime::Format::Strptime->new(
    pattern     => '%d/%m-%Y',
    time_zone   => 'local',
);
$date = $parser->parse_datetime($date);

my $today = DateTime->new(
    day   => 1,
    month => 7,
    year  => 2011,
    time_zone => 'local'
);  

my $dur = $date->delta_days($today);
say "Weeks:          ", $dur->weeks;
say "Days:           ", $dur->days;
say "Absolute Days:  ", $dur->in_units('days');
say "Absolute Hours: ", $date->delta_ms($today)->in_units('hours');

The output of this program is:

Weeks:          568
Days:           3
Absolute Days:  3979
Absolute Hours: 95496

And just for info:
1) You don't need to load DateTime::Duration its get loaded with DateTime.
2) You dont need (). These modules are OOP and don't export/import anything.

like image 83
David Raab Avatar answered Sep 28 '22 08:09

David Raab