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

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

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

Converting string to datetime MMM DD YYYY HH:MMAM to YYYY-MM-DD HH:MM:SS

Feb 2 2016 12:00AM
Feb 15 2015 05:00PM
Would like to convert the above data to 'YYYY-MM-DD TT:TT:TT' format, ideally:
2016-02-02 00:00:00
Here's what I've tried using a substitution to convert it. The reason I have two different ones is because when I BCP out my data into a text file the dates with single digit uses a space instead of a 0 in front of it:
s/Feb (\d{1}) (\d{4}) (\d{2})(\:\d{2})AM/2-$1-2 $3$4/g;
s/Feb (\d{2}) (\d{4}) (\d{2})(\:\d{2})AM/2-$1-2 $3$4/g;
Also I am not sure how I would go about converting the time to military time and also would there be a more efficient way to do all 12 months rather then have 12 lines of substitution for all of them, in this case there would be 24.
The Time::Piece module is convenient for this sort of thing
The strptime class method takes a date-time string and a format specification that it uses to parse the string to create a new object. Meanwhile the strftime method will return a date-time string according to another format specification
use strict;
use warnings 'all';
use Time::Piece ();
while ( <DATA> ) {
chomp;
my $new_dt = Time::Piece->strptime($_, '%b %d %Y %H:%M%p')->strftime('%Y-%m-%d %H:%M:%S');
printf "%s --> %s\n", $_, $new_dt;
}
__DATA__
Feb 2 2016 12:00AM
Feb 15 2015 05:00PM
output
Feb 2 2016 12:00AM --> 2016-02-02 00:00:00
Feb 15 2015 05:00PM --> 2015-02-15 17:00:00
Some people prefer Time::Piece because it comes bundled with Perl, but it's so easy to get things wrong with that modules. I strongly recommend using a module that doesn't confuse the experts, such as DateTime.
use strict;
use warnings qw( all );
use feature qw( say );
use DateTime::Format::Strptime qw( );
my $format = DateTime::Format::Strptime->new(
pattern => '%b %d %Y %I:%M%p',
#time_zone => 'local',
on_error => 'croak',
);
for ('Feb 2 2016 12:00AM', 'Feb 15 2015 05:00PM') {
say $format->parse_datetime($_)->strftime('%Y-%m-%d %H:%M:%S');
}
Output:
2016-02-02 00:00:00
2015-02-15 17:00:00

Perl Date String Parsing with Time::Piece

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"

How can I convert of the unix date output across multiple time zones to UTC, in Perl?

In Perl, how would one efficiently parse the output of unix's date command, taking into account time zone, and also convert to UTC?
I've read many similar questions on stackoverflow, but few seem to take into account parsing multiple time zones. Instead they seem to set the timezone manually and assume it to stay fixed.
# Example Input Strings:
my #inputs = (
'Tue Oct 12 06:31:48 EDT 2010',
'Tue Oct 12 07:49:54 BST 2010',
);
I tried the following to no avail:
foreach my $input ( #inputs ) {
my $t = Time::Piece->strptime( $input,
'%a %b %d %T %Z %Y' );
print $t->cdate, "\n";
}
It seems the problem is the time zone (%Z). Additionally, a time zone field does not seem to exist in Time::Piece, which would require me to write custom code to convert to UTC, which just seems... wrong.
Context:
I'm attempting to parse legacy logs from a variety of sources that use the unix date command for timestamps. Ideally, I'd like to convert all timestamps to UTC.
Any help would be greatly appreciated.
If you know how to disambiguate the TZs, just pop them into a dispatch table:
use strict; use warnings;
use DateTime::Format::Strptime ();
my #inputs = (
'Tue Oct 12 06:31:48 EDT 2010',
'Tue Oct 12 07:49:54 BST 2010',
);
my %tz_dispatch = (
EDT => build_parser( 'EST5EDT' ),
BST => build_parser( '+0100' ),
# ... etc
default => build_parser( ),
);
for my $input (#inputs) {
my ($parser, $date) = parse_tz( $input, %tz_dispatch );
print $parser->parse_datetime( $date ), "\n";
}
sub build_parser {
my ($tz) = #_;
my %conf = (
pattern => '%a %b %d %T %Z %Y',
on_error => 'croak',
);
#conf{qw/time_zone pattern/} = ($tz, '%a %b %d %T %Y')
if $tz;
return DateTime::Format::Strptime->new( %conf );
}
sub parse_tz {
my ($date, %tz_dispatch) = #_;
my (#date) = split /\s/, $date;
my $parser = $tz_dispatch{splice #date, 4, 1};
return $parser
? ($parser, join ' ', #date)
: ($tz_dispatch{default}, $date);
}
The Perl DateTime FAQ on timezones has a good background on why EDT and EST cannot be used in most conversions. The issue is that other countries also have an Eastern time zone with the same 3 letter abbreviation. EST EDT is ambiguous without other clues.
You might look at other modules, or just assume that "EDT" is the same as "EST5EDT" if that is true.
If you are using Date::Time::Strptime, you can use %O for the Olson Time Zone name and do a manual fixup before parsing.
i.e. if you know that EDT in your input means America/New_York, do this:
$time_in =~ s{EDT}{America/New_York};
and instead of
%a %b %d %T %Z %Y
for your time zone spec use
%a %b %d %T %O %Y
I've always found Date::Manip::ParseDate to be good for these sorts of situations.
use strict;
use warnings qw<FATAL all>;
use Date::Manip qw<ParseDate UnixDate>;
my #inputs = (
q<Tue Oct 12 06:31:48 EDT 2010>,
q<Tue Oct 12 07:49:54 BST 2010>,
);
sub date2epoch($) {
my $user_string = shift();
my $timestamp = ParseDate($user_string);
my $seconds = UnixDate($timestamp, "%s");
return $seconds;
}
sub epoch2utc($) {
my $seconds = shift();
return gmtime($seconds) . q< UTC>;
}
for my $random_date (#inputs) {
my $epoch_seconds = date2epoch($random_date);
my $normal_date = epoch2utc($epoch_seconds);
print "$random_date == $normal_date\n";
}
When run, that produces this:
Tue Oct 12 06:31:48 EDT 2010 == Tue Oct 12 10:31:48 2010 UTC
Tue Oct 12 07:49:54 BST 2010 == Tue Oct 12 06:49:54 2010 UTC
which seem to be what you're looking for.
I'm a little late on this, but GNU date itself is good at parsing dates:
$ date -u -d 'Thu Oct 14 01:17:00 EDT 2010'
Thu Oct 14 05:17:00 UTC 2010
I don't know how it resolves the EDT ambiguity though.
I agree with Jander on date command. -d and -u are great and save a lot of code lines.