Perl Date String Parsing with Time::Piece - perl

For some reason I am having a lot of trouble parsing date strings using Time::Piece.
So this works:
my $t = Time::Piece->strptime( 'Sunday, November 4, 2012 10:25:15 PM -0000' , "%A, %B %d, %Y %I:%M:%S %p %z" );
print $t;
But this does not:
my $temp_time = Time::Piece->strptime('7/23/2014 5:24:22 PM', "%-m/%-d/%Y %l:%M:%S %p");
print $temp_time;
I have also used '%D %r' as the format string but that also does not work. Do any of you have insight as to why this might be? For reference , the hour is 1-12 (not 01-12) and the month is 1-12 (not 0-12).
Thanks!

Change
"%-m/%-d/%Y %l:%M:%S %p"
to
"%m/%d/%Y %l:%M:%S %p"

Related

Convert filetime to GMT in Perl

I am working on some training material for new programmers where I am talking about the HTTP Header, so I am trying to set the Last-Modified manually. I have everything worked out except for getting the file time to GMT. Below is what I have so far:
The question is: giving stat($fh)->mtime which could be running in any timezone, what code needs to be added to convert to GMT?
my $scriptFilename = $ENV{'SCRIPT_FILENAME'};
my $timestamp;
my $fh = FileHandle->new;
if ($fh->open("< ${scriptFilename}")) {
$timestamp = time2str("%a, %e %b %Y %X %Z", stat($fh)->mtime);
$fh->close;
}
#Last-Modified: Tue, 15 Oct 2019 12:45:26 GMT
print <<"END";
Content-type: text/html; charset=iso-8859-1
Last-Modified: $timestamp
<html>
...
</html>
time2str from the Date::Format package (I assume that's where time2str comes from) takes an optional 3rd argument to specify the timezone.
$timestamp = time2str("%a, %e %b %Y %X %Z", stat($fh)->mtime, 'UTC');
There seems to be some confusion here: the result of mtime is not in any time zone, it is in seconds since the epoch which is a fixed point in time (barring leap seconds which are ignored). Thus all you need to do is represent it in the time zone you want, which is UTC. Another answer mentioned how to do this with the function you had been using, but the usual function to format a time into a string is strftime, which is provided by a couple core modules.
use strict;
use warnings;
use POSIX 'strftime';
# gmtime interprets the mtime seconds in UTC
my $timestamp = strftime "%a, %e %b %Y %X %Z", gmtime $mtime;
use Time::Piece 'gmtime';
my $timestamp = gmtime($mtime)->strftime("%a, %e %b %Y %X %Z");
But your use case is actually a specific date format, the one the HTTP protocol uses, and which will always be in GMT/UTC. There is a module for that: HTTP::Date
use strict;
use warnings;
use HTTP::Date 'time2str';
my $timestamp = time2str $mtime;
Basically
my $dt = DateTime->new(
year => 2000,
month => 5,
day => 10,
hour => 15,
minute => 15,
time_zone => 'America/Los_Angeles',
);
print $dt->hour; # prints 15
$dt->set_time_zone( 'America/Chicago' );
print $dt->hour; # prints 17
See here for how to format the output.

Time and date using localtime function

I'm trying to get a customized date and time output using Perl.
Can someone help me to get the output as desired?
#!/usr/bin/perl -w
use POSIX;
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
$year += 1900;
print "$sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst\n";
$now_string = localtime;
print "$now_string\n";
$date = strftime "%a %b %e %H:%M:%S %Y", localtime;
print "Date1 is: $date\n";
$date = strftime "%a-%B-%e", localtime;
print "Date2 is: $date\n";
$time = strftime "%H:%M:%S", localtime;
print "Time1 is: $time\n";
$time1 = strftime "%h:%m:%s", localtime;
print "Time2 is: $time1\n";
output
15, 2, 18, 8, 9, 2017, 0, 280, 0
Sun Oct 8 18:02:15 2017
Date1 is: Sun Oct 8 18:02:15 2017
Date2 is: Sun-October- 8
Time1 is: 18:02:15
Time2 is: Oct:10:1507465935
Desired output:
Date1 is: 06-Oct-2017
Date2 is: 06-10-2017
Time1 is: 23:35:10
Time2 is: 11:35:10 PM
Perl has great built-in help. Start by running the command:
perldoc POSIX
and looking at the strftime section.
Here is an ISO 8601 style timestamp:
schumack#linux2 18> perl -MPOSIX -le 'print(strftime("%Y-%m-%dT%H:%M:%S", localtime))'
2017-10-07T00:11:41
From perldoc POSIX:
"strftime"
Convert date and time information to string. Returns the string.
Synopsis:
strftime(fmt, sec, min, hour, mday, mon, year,
wday = -1, yday = -1, isdst = -1)
The month ("mon"), weekday ("wday"), and yearday ("yday") begin at
zero, i.e., January is 0, not 1; Sunday is 0, not 1; January 1st
is 0, not 1. The year ("year") is given in years since 1900, i.e.,
the year 1995 is 95; the year 2001 is 101. Consult your system's
"strftime()" manpage for details about these and the other
arguments.
If you want your code to be portable, your format ("fmt") argument
should use only the conversion specifiers defined by the ANSI C
standard (C89, to play safe). These are "aAbBcdHIjmMpSUwWxXyYZ%".
But even then, the results of some of the conversion specifiers
are non-portable. For example, the specifiers "aAbBcpZ" change
according to the locale settings of the user, and both how to set
locales (the locale names) and what output to expect are
non-standard. The specifier "c" changes according to the timezone
settings of the user and the timezone computation rules of the
operating system. The "Z" specifier is notoriously unportable
since the names of timezones are non-standard. Sticking to the
numeric specifiers is the safest route.
The given arguments are made consistent as though by calling
"mktime()" before calling your system's "strftime()" function,
except that the "isdst" value is not affected.
The string for Tuesday, December 12, 1995.
$str = POSIX::strftime( "%A, %B %d, %Y",
0, 0, 0, 12, 11, 95, 2 );
print "$str\n";
Perl has great built-in help. Start by running the command:
perldoc POSIX
and looking at the strftime section.
Here is an ISO 8601 style timestamp:
schumack#linux2 18> perl -MPOSIX -le 'print(strftime("%Y-%m-%dT%H:%M:%S", localtime))'
2017-10-07T00:11:41

To subtract two Time:Piece objects

I want to get the difference between time in minutes. Both the timings to be compared are in same timezone so no worries about the time zone difference and all.
Say if Start_time = 14 Apr 2016 05:02:26 (which is collected form log line) and for end time would be the current time of machine,
I want to calculate end time - start time. For that I need the Start time to be in format of current time.
I tried subtracting current time which is already a Time::Piece Object and converting $start_time into Time:Piece object.
But I am getting "Error parsing time at C:/Perl/lib/Time/Piece.pm line 469, line 1071883." error. Please suggest.
Also this "1071883" num in error is changing everytime I run the script. Not sure if its a kind of garbage value or what.
Editing the code with below suggested answer
I am getting below output. Seems the problem is coming as $now contains the DAY value like saturday but our $start_time doesnt. However i cant make any change in start time like adding the day value to it as we are collecting it from a log file. If the problem is due to the reason I stated, Kindly suggest how to ignore that day value from $now.
Output :
last line of log: 16 Apr 2016 03:41:49 -- DEBUG -- 16 Apr 2016 03:41:49
Time is Sat Apr 16 03:43:02 2016
difference is 21673
Below is what I tried:
#get last line of log
open my $fh ,"<","$slogfile";
my $last_line;
$last_line = $_,while (<$fh>);
print OUTLOG "last line of log: $last_line \n";
if ($last_line=~ m/^(\d\d) (\w{3}) (\d{4}) (\d\d):(\d\d):(\d\d) --/) {
$start_time = "$1 $2 $3 $4:$5:$6";
print OUTLOG "$start_time\n";
} else {
print OUTLOG "pattern matching didnt work\n";
}
#get current time
my $t = localtime;
#my $current_time = $t ;
print OUTLOG "Time is $current_time \n";
my $format = '%d %b %Y %H:%M:%S';
my $diff = $t - Time::Piece->strptime($var, $format);
print OUTLOG "difference is $diff \n";
Your format doesn't match the format of your date. The format you're using is "'%a %b %d %H:%M:%S %Y", which would match "Thu Apr 14 05:02:26 2016". You can get an explanation of the various pieces of your format from "man strftime" or "man strptime".
I've corrected the format definition in this example.
#!/usr/bin/perl
use strict;
use warnings;
use 5.010;
use Time::Piece;
my $format = '%d %b %Y %H:%M:%S';
my $start_time = '14 Apr 2016 05:02:26';
my $now = localtime;
my $diff = $now - Time::Piece->strptime($start_time, $format);
say "$diff seconds"; # $diff stringifies to seconds
say $diff->minutes, ' minutes';
The output is:
186402 seconds
3106.7 minutes
Update:
Seems the problem is coming as $now contains the DAY value like
saturday but our $start_time doesnt. However i cant make any change in
start time like adding the day value to it as we are collecting it
from a log file. If the problem is due to the reason I stated, Kindly
suggest how to ignore that day value from $now.
No. That's not the problem at all. If you print out the the Time::Piece object that you parse from $start_time then you'll see that also has the day name included. That's just how Time::Piece objects stringify.
The actual problem is more subtle. It seems that when Time::Piece parses a date string, it assumes that it is in UTC unless the string contains an explicit time zone. From what you're saying I'm assuming that your on the east coast of the US, which would explain the ~6 hour differences that you're getting.
I'm investigating this further and will almost certainly submit a patch to Time::Piece to fix this (it might just be a documentation patch to make the behaviour clearer). But in the meantime, you need a fix. And that's pretty simple. You just need to add the time zone to your code. The relevant sections of your code will now look like this:
my $format = '%d %b %Y %H:%M:%S%z'; # %z added here
# Append timezone here (-0600 as you're six hours behind UTC -
# adjust that if my assumption is wrong)
my $diff = $t - Time::Piece->strptime($var . '-0600', $format);
Or (as, quite rightly, pointed out in the comments by Borodin) you could just switch to using UTC throughout).
my $t = gmtime;
my $format = '%d %b %Y %H:%M:%S';
my $diff = $t - Time::Piece->strptime($var, $format);

%-d flag with specifier not working

My platform is Linux. I want the printed date to be formatted as Apr 3, 2014, 5:28 PM when given input such as 2014-04-03T17:28:54.864Z.
My current Perl script.
#!/usr/bin/perl
use lib '/tmp/DateTime/Format';
use DateTime::Format::ISO8601;
my $date = "2014-04-03T17:28:54.864Z";
my $iso8601 = DateTime::Format::ISO8601 -> new;
my $dt = $iso8601->parse_datetime( $date );
print "Date before conversion *******-> \"$date\"\n";
my $s6 = $dt->strftime("%b %-d, %Y, %I:%M %p");
print "Date after conversion *******-> \"$s6\"\n";
It gives the output Apr %-d, 2014, 05:28 PM. When the day of the month or hour is a single digit, I do not want zero or space padding. I want the output as Apr 3, 2014, 5:28 PM when day and hour are each a single digit.
I see no %-d option listed in the valid strftime patterns.
You could use %e to get a leading space instead of a leading zero but, since you discount that, you can get the day independently, then change it and use that changed value within the format string, something like (untested, but you'll get the idea):
# Get day and remove leading zero.
my $dayofmonth = $dt->strftime("%d");
$dayofmonth =~ s/^0//;
# Construct format string from "hard-coded" day.
my $s6 = $dt->strftime("%b " . $dayofmonth . ", %Y, %I:%M %p");
Try this:
my $s6 = $dt->format_cldr('MMM d, Y, h:mm a');
It makes use of the module:
DateTime::Format::CLDR - Parse and format CLDR time patterns
Which you can find documented here.
Because there aren't any strftime patterns for days other than %d and %e, the easy solution is just to edit the result after the fact
$s6 =~ s/\s+/ /g;
This would change your script to the following:
use strict;
use warnings;
use DateTime::Format::ISO8601;
my $date = "2014-04-03T17:28:54.864Z";
my $iso8601 = DateTime::Format::ISO8601->new;
my $dt = $iso8601->parse_datetime( $date );
print qq{Date before conversion *******-> "$date"\n};
my $s6 = $dt->strftime("%b %e, %Y, %I:%M %p");
$s6 =~ s/\s+/ /g;
print qq{Date after conversion *******-> "$s6"\n};

How to parse string in perl having timezone of format GMT[+/-]hh:mm?

I have a string of date and time of format:
Day, Month DAY_OF_MONTH, Year HH:MM:SS AM/PM GMT[+/-]hh:mm .
But I am unable to parse it. I have used this:
Time::Piece->strftime($string, "%A, %B %d, %Y %I:%M:%S %p %Z%z");
I have converted the string into the format : Day, Month DAY_OF_MONTH, Year HH:MM:SS AM/PM [+/-]hhmm . But still its not working for me with
Time::Piece->strftime($string, "%A, %B %d, %Y %I:%M:%S %p %z");
The string is getting parsed but the time zone is not considered in the Time::Piece object as tzoffset is 0000 and the HH of the date has the same value as that in the string. Please anyone help.
Eg: String to be parsed: Friday, July 25, 2008 12:15:57 PM GMT-0700
If Time::Piece doesn't work, try using Date::Parse.
And as oalders mentioned, do include an actual date string example in your question.
Perhaps the following will help:
use strict;
use warnings;
use Time::Piece;
my $string = 'Sunday, November 4, 2012 10:25:15 PM -0000';
my $t = Time::Piece->strptime( $string, "%A, %B %d, %Y %I:%M:%S %p %z" );
print $t;
Output:
Sun Nov 4 22:25:15 2012