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 "%02d", $_ } $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 also submit two time-stamp strings as input to this program.
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.