How can I get the difference of two timestamps using Perl? - perl

Here i based one problem.. i have two timestamps with same format like (Tue Dec 14 18:23:19 2010 & Tue Dec 14 17:23:19 2010). how can i get the difference of two timestamps in hours.
please help me

use Date::Parse;
my $t1 = 'Tue Dec 14 17:23:19 2010';
my $t2 = 'Tue Dec 14 18:23:19 2010';
my $s1 = str2time( $t1 );
my $s2 = str2time( $t2 );
print $s2 - $s1, " seconds\n";

I use the DateTime family of classes for pretty much all of my date/time handling.
#!/usr/bin/perl
use strict;
use warnings;
use DateTime::Format::Strptime;
my $dp = DateTime::Format::Strptime->new(
pattern => '%a %b %d %H:%M:%S %Y'
);
# Create two DateTime objects
my $t1 = $dp->parse_datetime('Tue Dec 14 17:23:19 2010');
my $t2 = $dp->parse_datetime('Tue Dec 14 18:23:19 2010');
# The difference is a DateTime::Duration object
my $diff = $t2 - $t1;
print $diff->hours;

You can take advantage of DateTime and its subtract_datetime() method, which returns a DateTime::Duration object.
use Date::Parse;
use DateTime;
my $t1 = 'Tue Dec 14 17:23:19 2010';
my $t2 = 'Tue Dec 14 18:23:19 2010';
my $t1DateTime = DateTime->from_epoch( epoch => str2time( $t1 ) );
my $t2DateTime = DateTime->from_epoch( epoch => str2time( $t2 ) );
my $diff = $t2DateTime->subtract_datetime( $t1DateTime );
print "Diff in hours: " . $diff->in_units('hours') . "\n";
print "Diff in months: " . $diff->in_units('months') . "\n";

Related

Time::Piece (localtime/gmtime) calculation vs bash date

Have this bash script:
future="${1:-Dec 08 2017 22:00:00}"
t1=$(date -j -f "%b %d %Y %H:%M:%S" "$future" +%s) #using OS X
t0=$(date +%s)
echo "Current: $(date)"
echo "Future : $future"
echo "Diff : $(( $t1 - $t0 )) secs"
It prints:
Current: pi 8. december 2017 21:25:25 CET
Future : Dec 08 2017 22:00:00
Diff : 2075 secs
The result (diff) is correct.
Now trying to do the same using perl:
use strict;
use warnings;
use feature 'say';
use Time::Piece;
my $format = '%b %d %Y %H:%M:%S';
my $future = shift // 'Dec 08 2017 22:00:00';
say "Future: $future";
say "localtime: ", scalar localtime();
say "gmtime : ", scalar gmtime();
my $tf = Time::Piece->strptime($future, $format);
say 'localtime-diff : ', $tf-localtime();
say 'gmtime-diff : ', $tf-gmtime();
it prints
Future: Dec 08 2017 22:00:00
localtime: Fri Dec 8 21:27:45 2017 #correct
gmtime : Fri Dec 8 20:27:45 2017 #correct
localtime-diff : 5535 #incorrect (expecting 3600 secs less)
gmtime-diff : 5535 #ok
What is wrong? Mean, why it prints the same diff for the localtime and gmtime but the scalar localtime and scalar gmtime prints different (and correct) strings?
EDIT: So, the main question is: how to get the same result as in bash using perl?
Both localtime() and gmtime() return an object that represents now.
You are doing:
2017-12-08T22:00:00+00:00 - 2017-12-08T21:25:25+01:00 # $tf-localtime()
2017-12-08T22:00:00+00:00 - 2017-12-08T20:25:25+00:00 # $tf-gmtime()
It looks like you want to do
2017-12-08T22:00:00+01:00 - 2017-12-08T21:25:25+01:00
Using Time::Piece:
use Time::Piece qw( localtime );
my $future_str = 'Dec 08 2017 23:00:00';
my $format = '%b %d %Y %H:%M:%S';
my $future_dt = localtime->strptime($future_str, $format);
say $future_dt - localtime(); # 2241 (instead of 5841)
Using DateTime:
use DateTime::Format::Strptime qw( );
my $future_str = 'Dec 08 2017 23:00:00';
my $format = DateTime::Format::Strptime->new(
pattern => '%b %d %Y %H:%M:%S',
locale => 'en',
time_zone => 'local',
on_error => 'croak',
);
my $future_dt = $format->parse_datetime($future_str);
say $future_dt->epoch - time(); # 2241 (instead of 5841)

How to get nano second granularity from hex time?

I am trying to convert hex time(getting first output from kernel module) into nanosecond granularity,
580a9272.0a9ce167
and I am trying to convert it using perl into human readable format:
while (<>) {
s/^([a-fA-F0-9]+)(\.)([a-fA-F0-9]+)(\s+.*)/sprintf("%s%s%s%s",&$converter(hex($1)), $2, hex($3), $4)/oe;
} continue {
print;
}
output : Fri Oct 21 18:10:58 2016.178053479
Converter uses localtime() and gmtime() directly
I want time with nano granularity and then year.Any help is highly appreciated.
POSIX::strftime doesn't support fractional seconds, so you need to build the output in parts.
use POSIX qw( strftime );
my $opt_gmt = 1;
my $hex = '580a9272.0a9ce167';
my ($s, $ns) = map hex($_), split /\./, $hex;
my $formatted_ns = sprintf("%09d", $ns);
my $formatted = strftime("%a %b %d %H:%M:%S.$formatted_ns %Y",
defined($opt_gmt) ? gmtime($s) : localtime($s));
say $formatted; # Fri Oct 21 22:10:58.178053479 2016
DateTime has native support for nanoseconds, so that presents an alternative.
use DateTime qw( );
my $opt_gmt = 1;
my $hex = '580a9272.0a9ce167';
my ($s, $ns) = map hex($_), split /\./, $hex;
my $dt = DateTime->from_epoch( epoch => $s );
$dt->set_nanosecond( $ns );
$dt->set_time_zone( defined($opt_gmt) ? 'UTC' : 'local' );
say $dt->strftime("%a %b %d %H:%M:%S.%N %Y"); # Fri Oct 21 22:10:58.178053479 2016

How to get the system clock in format in Perl?

I want to get the system clock (time and date) and display it in a human-readable format in Perl.
The format like 2014-09-12 15:13:56
#!/usr/local/bin/perl
my %months = qw(Jan Feb Mar Apr May Jun Jul
Aug Sep Oct Nov Dec);
#weekDays = qw(Sun Mon Tue Wed Thu Fri Sat Sun);
($second, $minute, $hour, $dayOfMonth, $month, $yearOffset, $dayOfWeek, $dayOfYear,
$daylightSavings) = localtime();
$year = 1900 + $yearOffset;
$now = "$year-$months-$dayOfMonth $hour:$minute:$second";
print $now;
When you run the program, you should see a much more readable date and time like this:
2014--12 16:57:15
how to get convert the month to number ?
Using Time::Piece (core module since perl v5.9.5)
use Time::Piece;
my $dt = localtime;
print $dt->ymd, " ", $dt->hms, "\n";
using DateTime
use DateTime;
my $dt = DateTime->now();
print $dt->ymd, " ", $dt->hms, "\n";
It's easier using a Perl module (POSIX doesn't requires installation):
use POSIX qw/strftime/;
my $now_string = strftime "%Y-%m-%d %H:%M:%S", localtime;
print $now_string, "\n"; #<-- prints: 2014-09-12 11:09:45 (with my local time)
Regarding to your code, there is a typo:
$now = "$year-$months-$dayOfMonth $hour:$minute:$second";
should be:
$now = "$year-$month-$dayOfMonth $hour:$minute:$second";
Be sure to write use strict; and use warnings; in the top place of your script. It prevents you from errors like that.
I like to put these date and time tasks into functions for reuse.
Here is my approach:
use strict;
use warnings;
my $time_stamp = getTodaysDateTime();
print "Program Started: $time_stamp \n";
# do some processing
$time_stamp = getTodaysDateTime();
print "Program Ended: $time_stamp \n";
# return date in specific format
# ex: 2014-09-12 14:11:43
sub getTodaysDateTime {
my ($sec,$min,$hour,$mday,$mon,$year,
$wday,$yday,$isdst) = localtime(time);
$year += 1900;
$mon += 1;
return sprintf("%d-%02d-%02d %02d:%02d:%02d",
$year,$mon,$mday,$hour,$min,$sec);
}

Date Conversion

In perl, how do I convert date like
Thu Mar 06 02:59:39 +0000 2008
to
2008-03-06T02:59:39Z
Tried HTTP::Date, it works if the question did not have +0000 in the string :(
DateTime::Format::Strptime will do this conversion.
#!/usr/bin/perl
use strict;
use warnings;
use 5.012;
use DateTime::Format::Strptime;
my $date = 'Thu Mar 06 02:59:39 +0000 2008 ';
my( #strp ) = (
DateTime::Format::Strptime->new( pattern => "%a %b %d %T %z %Y", ),
DateTime::Format::Strptime->new( pattern => "%FY%T%Z", )
);
my $dt = $strp[0]->parse_datetime( $date );
print $strp[1]->format_datetime( $dt );
prints 2008-03-06T02:59:39UTC
Chris
If you're absolutely, positively sure that the date will ALWAYS be in that format, you can simply use regular expressions to reformat it. The only thing is that you have to have a way of converting the month to a number. That way, you don't have to download any extra modules to do the date conversion:
my $date = "Thu Mar 06 02:59:39 +0000 2008"; #Original String
#Create the Month Hash (you might want all twelve months).
my %monthHash (Jan => "01", Feb => 2, Mar => 3);
# Use RegEx Matching to parse your date.
# \S+ means one or more non-spaces
# \s+ means one or more spaces
# Parentheses save that part of the string in $1, $2, $3, etc.
$date =~ m/\S+\s+(\S+)\s+(\S+)\s+(\S+)\s+\S+\s(.*)/;
my $monthString = $1;
my $day = $2;
my $time = $3;
my $year = $4;
# Convert Month string to a number.
my $month = $monthHash{$monthString};
#Reformat the string
$fmtDate="$year-$month-$day" . "T" . "$time" . "Z";
Otherwise I was going to say you can also try DateTime::Format::Strptime, but Chris Charley beat me to it.
So, edit it with a regex and use HTTP::Date:
( my $new_date_string = $old_state_string ) =~ s/[+-]\d{4,}\s+//;

How do I get yesterday's date using localtime?

How do I tweak this to get yesterday's date using localtime?
use strict;
sub spGetCurrentDateTime;
print spGetCurrentDateTime;
sub spGetCurrentDateTime {
my ($sec, $min, $hour, $mday, $mon, $year) = localtime();
my #abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my $currentDateTime = sprintf "%s %02d %4d", $abbr[$mon], $mday, $year+1900; #Returns => 'Aug 17 2010'
return $currentDateTime;
}
~
use DateTime qw();
DateTime->now->subtract(days => 1);
The expression on the second line returns a DateTime object.
As tempting as it is to just subtract a day's worth of seconds from the current time, there are times when this will yield the wrong answer (leap seconds, DST, and possibly others). I find it easier to just let strftime (available in the Perl 5 core module POSIX) take care of all of that for me.
#!/usr/bin/perl
use strict;
use warnings;
use Time::Local;
use POSIX qw/strftime/;
#2010-03-15 02:00:00
my ($s, $min, $h, $d, $m, $y) = (0, 0, 0, 15, 2, 110);
my $time = timelocal $s, $min, $h, $d, $m, $y;
my $today = strftime "%Y-%m-%d %T", localtime $time;
my $yesterday = strftime "%Y-%m-%d %T", $s, $min, $h, $d - 1, $m, $y;
my $oops = strftime "%Y-%m-%d %T", localtime $time - 24*60*60;
print "$today -> $yesterday -> $oops\n";
The DST problem can be worked around by taking 3600s from midday today instead of the current time:
#!/usr/bin/perl
use strict;
use warnings;
use Time::Local;
sub spGetYesterdaysDate;
print spGetYesterdaysDate;
sub spGetYesterdaysDate {
my ($sec, $min, $hour, $mday, $mon, $year) = localtime();
my $yesterday_midday=timelocal(0,0,12,$mday,$mon,$year) - 24*60*60;
($sec, $min, $hour, $mday, $mon, $year) = localtime($yesterday_midday);
my #abbr = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
my $YesterdaysDate = sprintf "%s %02d %4d", $abbr[$mon], $mday, $year+1900;
return $YesterdaysDate;
}
In light of the "unspecified" documented behaviour of the strftime solution suggested by Chas, this approach might be better if you're not able to test for expected-but-not-guaranteed results across multiple platforms.
use Time::Piece.
use strict;
use warnings;
use 5.010;
# These are core modules in Perl 5.10 and newer
use Time::Piece;
use Time::Seconds;
my $yesterday = localtime() - ONE_DAY;
say $yesterday->strftime('%b %d %Y');
Note that this can go wrong in certain borderline cases, such as the start of daylight saving time.
The following version does behave correct in such cases:
use strict;
use warnings;
use 5.010;
# These are core modules in Perl 5.10 and newer
use Time::Piece;
use Time::Seconds;
my $now = localtime();
my $yesterday = $now - ONE_HOUR*($now->hour + 12);
say $yesterday->strftime('%b %d %Y');
Alternatively, you can use the DateTime module as described in a different answer. That is not a core module, though.
Solution suggested by most users is wrong!
localtime(time() - 24*60*60)
The worst thing you can do is to assume that 1 day = 86400 seconds.
Example: Timezone is America/New_York, date is Mon Apr 3 00:30:00 2006
timelocal gives us 1144038600
localtime(1144038600 - 86400) = Sat Apr 1 23:30:00 EST 2006
oops!
The right and the only solution is to let system function normalize values
$prev_day = timelocal(0, 0, 0, $mday-1, $mon, $year);
Or let datetime frameworks (DateTime, Class::Date, etc) do the same.
That's it.
localtime(time() - 24*60*60)
my $yesterday = time();
$yesterday = $yesterday - (24*60*60);
24 as 24 hours, 60 as 60 minutes in hour and 60 as 60 seconds in minute
time() will return actual timestamp, and 246060 will remove seconds for exactly one day
After this simply do:
localtime($yesterday);
This is how I do it.
#!/usr/bin/perl
use POSIX qw(strftime);
$epoc = time();
$epoc = $epoc - 24 * 60 * 60;
$datestring = strftime "%F", localtime($epoc);
print "Yesterday's date is $datestring \n";