views:

5875

answers:

4

I've used the localtime function in Perl to get the current date and time but need to parse in existing dates. I have a GMT date in the following format: "20090103 12:00" I'd like to parse it into a date object I can work with and then convert the GMT time/date into my current time zone which is currently Eastern Standard Time. So I'd like to convert "20090103 12:00" to "20090103 7:00" any info on how to do this would be greatly appreciated.

+3  A: 

Take your pick:

There are a zillion others, no doubt, but they're probably the top contenders.

Jonathan Leffler
+13  A: 

Because the Perl built in date handling interfaces are kind of clunky and you wind up passing around a half dozen variables, the better way is to use either DateTime or Time::Piece. DateTime is the all-singing, all-dancing Perl date object, and you'll probably eventually want to use it, but Time::Piece is simpler and perfectly adequate to this task, has the advantage of shipping with 5.10 and the technique is basically the same for both.

Here's the simple, flexible way using Time::Piece and strptime.

#!/usr/bin/perl

use 5.10.0;

use strict;
use warnings;

use Time::Piece;

# Read the date from the command line.
my $date = shift;

# Parse the date using strptime(), which uses strftime() formats.
my $time = Time::Piece->strptime($date, "%Y%m%d %H:%M");

# Here it is, parsed but still in GMT.
say $time->datetime;

# Get your local time zone offset and add it to the time.
$time += $time->localtime->tzoffset;

# And here it is localized.
say $time->datetime;

And here's the by-hand way, for contrast.

Since the format is fixed, a regular expression will do just fine, but if the format changes you'll have to tweak the regex.

my($year, $mon, $day, $hour, $min) = 
    $date =~ /^(\d{4}) (\d{2}) (\d{2})\ (\d{2}):(\d{2})$/x;

Then convert it to Unix epoch time (seconds since Jan 1st, 1970)

use Time::Local;
# Note that all the internal Perl date handling functions take month
# from 0 and the year starting at 1900.  Blame C (or blame Larry for
# parroting C).
my $time = timegm(0, $min, $hour, $day, $mon - 1, $year - 1900);

And then back to your local time.

(undef, $min, $hour, $day, $mon, $year) = localtime($time);

my $local_date = sprintf "%d%02d%02d %02d:%02d\n",
    $year + 1900, $mon + 1, $day, $hour, $min;
Schwern
Even when rolling one's own, one can `use POSIX qw(strftime)` on both Unix and Windows systems from at least Perl 5.6.0 onward. That allows the final "roll-your-own" to become the both shorter and more maintainable: `my $local_date = strftime "%Y%m%d %H:%M\n", localtime($time);`.
pjf
$time->localtime->tzoffset AFAICT returns the offset for the current time, not the given time.
ysth
@ysth Yes, and that's ok unless we want to worry about historical time zone changes, which I sure don't. DST might be important.
Schwern
+4  A: 

Here's an example, using DateTime and its strptime format module.

use DateTime;
use DateTime::Format::Strptime;

my $val = "20090103 12:00";

my $format = new DateTime::Format::Strptime(
                pattern => '%Y%m%d %H:%M',
                time_zone => 'GMT',
                );

my $date = $format->parse_datetime($val);

print $date->strftime("%Y%m%d %H:%M %Z")."\n";

$date->set_time_zone("America/New_York");

print $date->strftime("%Y%m%d %H:%M %Z")."\n";

$ perl dates.pl
20090103 12:00 UTC
20090103 07:00 EST


If you had wanted to parse localtime, here's how you'd do it :)

use DateTime;

my @time = (localtime);

my $date = DateTime->new(year => $time[5]+1900, month => $time[4]+1,
                day => $time[3], hour => $time[2], minute => $time[1],
                second => $time[0], time_zone => "America/New_York");

print $date->strftime("%F %r %Z")."\n";

$date->set_time_zone("Europe/Prague");

print $date->strftime("%F %r %Z")."\n";
Vinko Vrsalovic
And what's wrong with this is?
Vinko Vrsalovic
You're missing the parsing part.
Schwern
Oh, true. I mistakenly assumed he wanted to use localtime output.
Vinko Vrsalovic
+1  A: 

That's what I'd do ...


#!/usr/bin/perl
use Date::Parse;
use POSIX;

$orig = "20090103 12:00";

print strftime("%Y%m%d %R", localtime(str2time($orig, 'GMT')));

You can also use Time::ParseDate and parsedate() instead of Date::Parse and str2time(). Note that the de facto standard atm. seems to be DateTime (but you might not want to use OO syntax just to convert a timestamp).

mjy