Home > Net >  How to subtract dates in perl and convert it in minutes and hours?
How to subtract dates in perl and convert it in minutes and hours?

Time:09-17

Every time I tried to find the difference of these date strings, there is an error. I wonder if you could help me this.

my $datecreated = '2021-09-06 04:52:38';
my $dateresolved = '2021-09-06 04:52:48';

my $time_elapsed= $dateresolved - $datecreated;
print $time_elapsed;

And I want to convert the result into minutes and hours.

CodePudding user response:

These two timestamps are mere strings. In order to get the duration between these two moments in time ("subtract" them) one needs to build date-time objects from them, in a library that knows how to then find duration between them. One good choice is DateTime

use warnings;
use strict;
use feature 'say';

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

my ($ts1, $ts2) = (@ARGV == 2) 
    ? @ARGV : ('2021-09-05 04:52:38', '2021-09-01 04:52:48');

my $strp = DateTime::Format::Strptime->new(
    pattern => '%F %T', time_zone => 'floating', on_error => 'croak'
);    
my ($dt1, $dt2) = map { $strp->parse_datetime($_) } $ts1, $ts2;

# Get difference in hours and minutes (seconds discarded per question)
my ($hrs, $min) = delta_hm($dt1, $dt2);
say "$hrs hours and $min minutes";

# Or (time-stamp hh:mm in scalar context)
my $ts_hm = delta_hm($dt1, $dt2);
say $ts_hm;

# To get wanted units (hours minutes here) best use a delta_X
sub delta_hm {
    my ($dt1, $dt2) = @_;
    my ($min, $sec) = $dt1->delta_ms($dt2)->in_units('minutes', 'seconds');
    my $hrs = int( $min / 60 );
    $min = $min % ($hrs*60) if $hrs;

    return (wantarray)    # discard seconds
        ? ($hrs, $min)
        : join ':', map { sprintf "d", $_ } $hrs, $min;
}

The hard-coded input time-stamps here are different than the ones in the question; those would make an hour minute difference a zero, since they differ only in seconds! (Is that intended?) One can submit two time-stamp strings as input though.

Note that a generic duration object makes it harder to convert to any particular desired units

One cannot in general convert between seconds, minutes, days, and months, so this class will never do so. Instead, create the duration with the desired units to begin with, for example by calling the appropriate subtraction/delta method on a DateTime.pm object.

So above I use delta_ms since minutes are easily converted to hours minutes. Seconds are discarded as the question implies (if that is in fact unintended add them in the routine).

But for more general uses one can do

use DateTime::Duration;

my $dur = $dt1->subtract_datetime($dt2);

# Easy to extract parts of the duration, like
say "Hours: ", $dur->hours, " and minutes: ", $dur->minutes;  # NOT conversion

# This leaves out possible longer units (days, months, years)

Can do this with the core Time::Piece as well

use warnings;
use strict;
use feature 'say';

use Time::Piece;

my ($ts1, $ts2) = (@ARGV) 
    ? @ARGV : ('2021-09-05 04:52:38', '2021-09-01 04:52:48');

my ($dt1, $dt2) = map { Time::Piece->strptime($_, "%Y-%m-%d %T") } $ts1, $ts2; 
# In older module versions the format specifier `%F` (`%Y-%m-%d`) may fail 
# so I spell it out here; the %T (for %H:%M:%S) should always be good
# For local times (not UTC) better use Time::Piece::localtime->strptime

my $delta = $dt1 - $dt2; 
# say $delta->pretty;

my $hrs = int( $delta->hours );  
my $min = int($delta->minutes) - ($hrs//=0)*60;
say "$hrs:$min"; 

This is much simpler, but watch out for occasional tricky (error-inducing) API of Time::Piece.

Note, while Time::Piece is core, succinct, and much lighter (and correct!), the DateTime is far more rounded and powerful, also with an ecosystem of extensions.

CodePudding user response:

Use Time::Piece which is a standard part of the Perl library since 2007.

#!/usr/bin/perl

use strict;
use warnings;

use Time::Piece;

# Define the format of your inputs
my $format = '%Y-%m-%d %H:%M:%S';

# Convert your date strings into Time::Piece objects
my $datecreated  = Time::Piece->strptime('2021-09-06 04:52:38', $format);
my $dateresolved = Time::Piece->strptime('2021-09-06 04:52:48', $format);

# Time::Piece objects can be subtracted from each other.
# This gives the elapsed time in seconds.
my $time_elapsed = $dateresolved - $datecreated;

# Do the calculations to displace the elapsed time in hours,
# minutes and seconds.
printf "dh:dm:ds\n",
       $time_elapsed->hours,
       $time_elapsed->minutes % 60,
       $time_elapsed->seconds % 60;
  • Related