minute_end - minute_start
Example: I start timing something at 3:50 and it stops at 4:10. Just looking at minutes difference, the difference is 20 minutes, which is what I want. However, 50-10 is 40. How do I account for this situation? I'm sure it's an if statement but I don't know how to start.
Perhaps you should be keeping track of things using time from the very beginning?
However, you can parse those strings using Time::Piece to calculate the difference:
use strict;
use warnings;
use Time::Piece;
my $start = Time::Piece->strptime('3:50', "%H:%M");
my $stop = Time::Piece->strptime('4:10', "%H:%M");
my $diff = $stop - $start;
print $diff->minutes;
Outputs:
20
This is rough, but probably adequate.
my $start_time = time;
# ...
my $end_time = time;
my $seconds_run = $end_time - $start_time;
my $minutes_run = $seconds_run / 60;
For much more precise timing, see Time::HiRes.
You can get assign current localdates like this, I modify miller's version:
use strict;
use warnings;
use Time::Piece;
use POSIX qw(strftime);
my $time1 = strftime("%H:%M", localtime(time));
# do something or wait
my $time2 = strftime("%H:%M", localtime(time));
my $start = Time::Piece->strptime($time1, "%H:%M");
my $stop = Time::Piece->strptime($time2, "%H:%M");
my $diff = $stop - $start;
print $diff->minutes;
I found the answer to my question, and it's simple. It's what I was trying to think of doing originally but it didn't click until I ran across it while browsing for an answer.
If I do minute_end - minute_start and the answer is negative, I just add 60 minutes to get the correct answer.
i.e.:
10 - 50 = -40
-40 + 60 = 20
Related
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.
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).
For more general uses one can do
use DateTime::Duration;
my $dur = $dt1->subtract_datetime($dt2);
# Easy to extract parts (components) of the duration
say "Hours: ", $dur->hours, " and minutes: ", $dur->minutes; # NOT conversion
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.
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 "%02dh:%02dm:%02ds\n",
$time_elapsed->hours,
$time_elapsed->minutes % 60,
$time_elapsed->seconds % 60;
I want to subtract two timestamps in Perl. I converted them to unix-time via the function below and convert the unix timestamp back to how it was. In the example below the result is 01:20:00 instead of 00:20:00
(I think it has sth to do with the start of the unix timestamp 1.1.1970 01:00:00 but not sure how to resolve it)
Any idea? Many thanks for your help in advance.
use POSIX qw( strftime );
use Time::Local qw( timelocal );
sub to_epoch {
$_ = shift;
my #a = split /\W+/, $_;
my $b = timelocal($a[5],$a[4],$a[3],$a[2],$a[1],$a[0]);
return $b;
}
my $h_end = "2018.11.12 00:50:00";
my $h_start = "2018.11.12 00:30:00";
my $duration = to_epoch($h_end) - to_epoch($h_start);
my $convert_back = POSIX::strftime("%H:%M:%S", localtime($duration));
print $convert_back , "\n";
Ouptut: 01:20:00
It works for me. But I think that's because I'm in GMT and you're in CET (GMT+1).
The flaw is in your final step. You are confusing two concepts - a point in time and a duration.
You correctly convert your two points in time to Unix epoch numbers and then you subtract those numbers to get the number of seconds between them. That number is a duration. And you want to convert that duration into a human-readable format. Using localtime() and POSIX::strtime() is not the way to do that. POSIX::strftime() and localtime() deal with points in time, not durations.
The number you get is 1,200. By passing that to localtime() you are saying "what is the epoch number 1,200 when converted to a date and time in my local timezone?" 1,200 is 20 past midnight on Jan 1st 1970 GMT. But in your local, Frankfurt, timezone, it's 20 past 1am. Which is why you're getting 1:20 and I'm getting 0:20.
There are a couple of ways to fix this. You can do the conversion manually.
my $duration = 1_200;
my $mins = int($duration/60);
my $secs = $duration % 60;
Or you can use a proper date/time handling module like DateTime (along with its associated module DateTime::Duration).
It might work if you use timegm() and gmtime() in place of timelocal() and localtime() - but I really don't recommend this approach as it perpetuates the confusion between points in time and durations.
Update: A version using DateTime.
#/usr/bin/perl
use strict;
use warnings;
use DateTime;
use DateTime::Format::Strptime;
my $h_end = '2018.11.12 00:50:00';
my $h_start = '2018.11.12 00:30:00';
my $date_p = DateTime::Format::Strptime->new(
pattern => '%Y.%m.%d %H:%M:%S'
);
my $duration = $date_p->parse_datetime($h_end)
- $date_p->parse_datetime($h_start);
printf '%02d:%02d:%02d', $duration->in_units('hours', 'minutes', 'seconds');
1200, the value of $duration, signifies the following when treated as a epoch timestamp
1970-01-01T01:20:00+01:00
^^^^^^^^
The solution is to replace
strftime("%H:%M:%S", localtime($duration));
with
strftime("%H:%M:%S", gmtime($duration));
This gives
1970-01-01T00:20:00Z
^^^^^^^^
Of course, this is still a hack. You're not suppose to be passing a duration to gmtime. Use an appropriate module instead.
use DateTime::Format::Strptime qw( );
my $format = DateTime::Format::Strptime->new(
pattern => '%Y.%m.%d %H:%M:%S',
on_error => 'croak',
);
my $h_end = $format->parse_datetime('2018.11.12 00:50:00');
my $h_start = $format->parse_datetime('2018.11.12 00:30:00');
my $dur = $h_end - $h_start;
printf "%02d:%02d:%02d\n", $dur->in_units(qw( hours minutes seconds ));
By the way,
timelocal($a[5],$a[4],$a[3],$a[2],$a[1],$a[0])
should be
timelocal($a[5],$a[4],$a[3],$a[2],$a[1]-1,$a[0])
I have browse SO for last 30 mins but couldn't get answer, so decided to post question.
I cannot use Time::Piece or Date::Parse module as suggested in some answers.
I'm trying to subtract two times and get result in seconds
use warnings;
use strict;
$tod = `date "+%H:%M:%S"`; chomp $tod; #record time of the day to variable
#Sample time 16:55:44
my startTime = $tod;
Insert records;
my endTime = $tod;
my totalTime = $startTime - $endTime;
Error:
Arguemnt "16:55:14" isn't numeric in subtraction (-)
Thanks,
You can use the time function:
my $start = time;
sleep 3;
my $end = time;
print $end - $start, ' seconds elapsed';
In general, it's best to avoid executing external commands with backticks or system when you can do the same thing with pure Perl.
If you want better than one-second resolution, you can use Time::HiRes as described in Is there a better way to determine elapsed time in Perl?
Time::Piece has come with Perl since 5.10 which was seven years ago, I'm a bit dubious about your claim to not be able to use it and it's really going to cause a lot of unnecessary pain in the long term to not use modules. You can check these things using Module::CoreList. Anyhow...
You can convert the "HH:MM:SS" time of day into seconds since midnight. Fortunately you're using 24 hour time which makes this simpler.
sub time_of_day_to_seconds {
my $tod = shift;
my($hour, $min, $sec) = split /:/, $tod;
my $total_seconds = $sec;
$total_seconds += $min * 60;
$total_seconds += $hour * 60 * 60;
return $total_seconds;
}
If you cannot avoid to get the time as a string, you can use DateTime::Format::Strptime in combination with Datetime::Duration.
Bear in mind you will have to install it since it is not in the core modules...anyway here an example:
use warnings;
use strict;
use DateTime::Format::Strptime;
my $strp = DateTime::Format::Strptime->new( pattern => '%T', );
my $diff = $strp->parse_datetime("23:23:12") - $strp->parse_datetime("23:23:10");
print join('-',$diff->deltas()); #months-0-days-0-minutes-0-seconds-2-nanoseconds-0
Hope this helps!
I would like a simple way to get yesterday's date (local time) as a string in a Perl script. Preferably, I would like to do this with no module dependencies, since we have a bare bones Perl 5.12 installation.
So, something like:
my $yesterdays_date=...; # Puts for example '301011' into $yesterdays_date,
# if today is October 31st, 2011
Time::Piece is core:
use Time::Piece;
use Time::Seconds qw(ONE_DAY);
my $yesterday = localtime() - ONE_DAY();
print $yesterday->strftime('%d%m%y'), "\n";
If you're concerned about daylight savings, you can normalize the current time to near noon:
use Time::Piece;
use Time::Seconds qw(ONE_DAY ONE_HOUR);
my $today = localtime;
my $yesterday = $today + ONE_HOUR * ( 12 - $today->hour ) - ONE_DAY;
print $yesterday->strftime("%d%m%y"), "\n";
If you can live with dependencies, then use DateTime:
use DateTime;
print DateTime->now->subtract(days => 1)->strftime('%d%m%y'), "\n";
If you're willing to go with dependencies, DateTime will generally do anything you need.
use strict;
use warnings;
use 5.012;
use DateTime;
say DateTime->now->subtract(days => 1)->strftime('%d%m%y');
Just subtract 24 hours (24 hours * 60 minutes * 60 seconds) from current time and get localtime:
say scalar localtime(time - 60*60*24);
# Sun Oct 30 21:04:30 2011
Note, that localtime returns time in string format only in scalar context.
If you need to generate 'DDMMYY' you can just use data structure returned by list context:
my #tm = localtime(time - 60*60*24);
my $date = sprintf("%02d%02d%2d", $tm[3], $tm[4]+1, $tm[5] + 1900);
# 30102011
You can use the POSIX module thusly:
perl -MPOSIX=strftime -le 'print strftime "%m%d%y",localtime(time-(60*60*24))'
I had assumed it would be as simple as $ENV{TZ}, but the TZ environment variable is not set, and yet the date command still knows I am in EDT, so there must be some other way of determining timezone (other than saying chomp(my $tz = qx/date +%Z/);).
use POSIX;
print strftime("%Z", localtime()), "\n";
If you want something more portable than POSIX (but probably much slower) you can use DateTime::TimeZone for this:
use DateTime::TimeZone;
print DateTime::TimeZone->new( name => 'local' )->name();
use POSIX;
localtime();
my ($std, $dst) = POSIX::tzname();
tzname() gives you access to the POSIX global tzname - but you need to have called localtime() for it to be set in the first place.
If you just need something like +05:30 (UTC+5.5/India time), you may use the following code.
my #lt = localtime();
my #gt = gmtime();
my $hour_diff = $lt[2] - $gt[2];
my $min_diff = $lt[1] - $gt[1];
my $total_diff = $hour_diff * 60 + $min_diff;
my $hour = int($total_diff / 60);
my $min = abs($total_diff - $hour * 60);
print sprintf("%+03d:%02d", $hour, $min);
This answer is inspired by Pavel's answer above.
Maybe this faster:
my #lt = localtime(12*60*60);
my #gt = gmtime(12*60*60);
$tz = #lt[2] - #gt[2];
$tz_ = sprintf("%+03d:00", $tz); # for +02:00 format
Use: returns server time zone offset from UTC in hours.
This builds on the above examples and will work with DST
my $hour_seconds = 60 * 60;
my $day_seconds = 24 * $hour_seconds;
#what is today's time at 00:00
my $right_now = time();
my $days = $right_now / $day_seconds;
my $years = $right_now / ($day_seconds * 365);
my $days_rounded = sprintf("%d", $days);
my $today_seconds_at_midnight = $days_rounded * $day_seconds;
my #lt = localtime($today_seconds_at_midnight + ($day_seconds / 2)); #must use noon today or we will get odd values
my $tz = $lt[2] - 12; #calc timezome difference, includes DST