Reputation: 5730
I want to convert a timestamp from localtime to GMT. I have legacy code that does this "manually" with Time::Local::timelocal()
and gmtime
. It works, but I don't like it and wanted to use Time::Piece
instead. I used this answer to do so (albeit they are converting the other way round, but I was able to replace +
with -
:-)).
This is my code:
#!/usr/bin/env perl
use strict;
use warnings;
use Time::Local;
use Time::Piece;
use POSIX qw(strftime);
sub local_to_utc_timelocal
{
my $local_ts = shift;
my ( $year, $mon, $mday, $hour, $min, $sec )
= ( $local_ts =~ /(\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)/ );
my $local_secs = Time::Local::timelocal( $sec, $min, $hour, $mday, $mon - 1, $year );
return POSIX::strftime( '%y-%m-%d %H:%M:%S', gmtime($local_secs) );
}
sub local_to_utc_timepiece
{
my $local_ts = shift;
my $local_tp = Time::Piece->strptime( $local_ts, '%y-%m-%d %H:%M:%S' );
my $utc_tp = $local_tp - localtime->tzoffset(); # ***
return $utc_tp->strftime('%y-%m-%d %H:%M:%S');
}
my $local;
# DST in effect (April 19 2016):
$local = '16-04-19 14:30:00';
print "DST in effect:\n";
printf("utc(%s) = %s (using timelocal)\n", $local, local_to_utc_timelocal($local));
printf("utc(%s) = %s (using timepiece)\n\n", $local, local_to_utc_timepiece($local));
# DST NOT in effect (Feb 19 2016):
$local = '16-02-19 14:30:00';
print "DST NOT in effect:\n";
printf("utc(%s) = %s (using timelocal)\n", $local, local_to_utc_timelocal($local));
printf("utc(%s) = %s (using timepiece)\n", $local, local_to_utc_timepiece($local));
Unfortunately the Time::Piece code doesn't seem to work properly wrt DST. I'm living in Germany, so currently (spring/summer, DST in effect) we are UTC+2. For "April 19 2016" the above code gives:
DST in effect:
utc(16-04-19 14:30:00) = 16-04-19 12:30:00 (using timelocal)
utc(16-04-19 14:30:00) = 16-04-19 12:30:00 (using timepiece)
which is correct. But for "Feb 19 2016" (when we are UTC+1) the same code gives:
DST NOT in effect:
utc(16-02-19 14:30:00) = 16-02-19 13:30:00 (using timelocal)
utc(16-02-19 14:30:00) = 16-02-19 12:30:00 (using timepiece)
That is: The gmtime(Time::Local::timelocal($timestamp))
gives correct 1 hour offset while Time::Piece
still gives 2 hours offset.
Is this a bug in Time::Piece
or do I use it wrongly?
I know there are plenty of ways to convert localtime to UTC but I'd like to use Time::Piece
because of its simplicity. Plus I cannot use DateTime
because that would involve deploying it on a dozen of production machines.
Upvotes: 8
Views: 1890
Reputation: 385657
localtime
returns now, so localtime->tzoffset()
returns the offset for now. Change
my $utc_tp = $local_tp - localtime->tzoffset();
to
my $utc_tp = $local_tp - $local_tp->tzoffset();
However, that leaves $utc_tp
flagged as a localtime, so you really want:
my $utc_tp = gmtime( $local_tp->epoch );
Despite its name, $local_tp
is not a local time, so $local_tp->tzoffset()
is zero. Change
Time::Piece->strptime( $local_ts, '%y-%m-%d %H:%M:%S' )
to
localtime->strptime( $local_ts, '%y-%m-%d %H:%M:%S' );
Upvotes: 10