Time::Piece strptime has trouble with %X - date

I'm trying to parse the following data using Time::Piece->strptime() in perl v5.26:
my $str = "Mon Feb 21 02:54:49 IST 2022";
my $time_zone = substr($str, 20, 3);
my $date_time = Time::Piece->strptime($str, "%a %b %e %X $time_zone %Y");
print "Todays date/time is : $date_time";
But, when I execute the above code snippet, I get the following error:
Error parsing time at /usr/local/lib64/perl5/Time/Piece.pm line 598.
I'm not sure what is it that I'm missing out, any help will be really helpful.

%X without the final AM/PM only works at the end of a string.
my $str = "Mon Feb 21 IST 2022 02:54:49";
my $time_zone = substr($str, 11, 3);
warn $time_zone;
my $date_time = Time::Piece->strptime($str, "%a %b %e $time_zone %Y %X");
print "Todays date/time is : $date_time";
or
my $str = "Mon Feb 21 02:54:49 AM IST 2022";
my $time_zone = substr($str, 23, 3);
my $date_time = Time::Piece->strptime($str, "%a %b %e %X $time_zone %Y");
print "Todays date/time is : $date_time";
But my testing shows this behaviour is Perl version dependent, so maybe don't use %X (same as %I:%M:%S %p) at all and switch to %T (same as %H:%M:%S).
my $str = "Mon Feb 21 02:54:49 IST 2022";
my $time_zone = substr($str, 20, 3);
my $date_time = Time::Piece->strptime($str, "%a %b %e %T $time_zone %Y");
print "Todays date/time is : $date_time";

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

Convert time in GMT to current time zone in perl

I want to convert GMT time string to my system time zone.
Ex.
Tue Nov 04 22:03:03 2014 GMT
My machine time zone is PST, so output should be : 2014-11-04 14:03:03 PST
I can do this in bash but could not find any solution for perl.
Bash solution=> timestamp_local=date "+%Y-%m-%d %H:%M:%S %Z" -d "$timestamp_GMT"
Anyone have solution in perl?
PS: I have to process a huge file ( around 100-200MB of text file ). So, I want a optimized solution.
Simple enough with DateTime and friends.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use DateTime::Format::Strptime;
use DateTime;
my $format = '%a %b %d %H:%M:%S %Y %Z';
my $time_string = 'Tue Nov 04 22:03:03 2014 GMT';
my $dt_p = DateTime::Format::Strptime->new(
pattern => $format,
time_zone => 'UTC',
);
my $time = $dt_p->parse_datetime($time_string);
say $time->strftime('%a %b %d %H:%M:%S %Y %Z');
$time->set_time_zone('America/Los_Angeles');
say $time->strftime('%a %b %d %H:%M:%S %Y %Z');
Update: And this old answer shows how to do something very similar with the core module Time::Piece.
POSIX library should be enough to do this;
use strict;
use feature qw/say/;
use POSIX qw(strftime tzset);
say strftime("%Y %d %m %H:%M:%S GMT", gmtime(time)); # GMT
say strftime("%Y %d %m %H:%M:%S %Z", localtime(time)); # Local Time
# Set to custom timezone
$ENV{TZ} = 'America/Los_Angeles';
tzset;
say strftime("%Y %d %m %H:%M:%S %Z", localtime(time)); # Custom Zone

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);
}

How can I get the difference of two timestamps using 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";