kchinger kchinger - 7 months ago 18
Perl Question

Converting Historical Timestamps from UTC to Local

I have a file with timestamps that are in the past and are all in UTC. I need to convert these to Eastern Time. I've gotten close, but it messes up around the daylight savings changeover.

#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;

#DST not in effect
my $utc = '2015-03-08 01:59:00.000';
my $local = utc_to_local($utc);
print "DST not in effect: utc($utc) = local($local)\n";

#DST not in effect
$utc = '2015-03-08 02:00:00.000';
$local = utc_to_local($utc);
print "DST not in effect: utc($utc) = local($local)\n";


sub utc_to_local
{
my $utc_ts = $_[0]; #this has ms on right side, we want left 19 characters
$utc_ts = substr $utc_ts, 0, 19;
my $local_tp = localtime->strptime( $utc_ts, '%Y-%m-%d %H:%M:%S' );
$local_tp = $local_tp + $local_tp->tzoffset();

return $local_tp->strftime('%Y-%m-%d %H:%M:%S');
}


As you can see, if I try 1:59 AM UTC on 3/8 and 2 AM UTC on 3/8, it thinks the change has occurred, but 2 AM UTC is only 3/7 at 9 PM. Away from the daylight savings changeover it does -5 and -4 offsets correctly.

DST not in effect: utc(2015-03-08 01:59:00.000) = local(2015-03-07 20:59:00)
DST not in effect: utc(2015-03-08 02:00:00.000) = local(2015-03-07 22:00:00)

Answer

$utc_ts isn't a local time.

my $local_tp = localtime->strptime( $utc_ts, '%Y-%m-%d %H:%M:%S' );

should be

my $utc_tp = Time::Piece->strptime( $utc_ts, '%Y-%m-%d %H:%M:%S' );

Then there's the issue of converting to localtime.

$local_tp = $local_tp + $local_tp->tzoffset();

should be

my $local_tp = localtime($utc_tp->epoch);

All together:

#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw( );
use Time::Piece;

sub utc_to_local {
    my ($utc_ts) = @_;
    my $utc_tp = Time::Piece->strptime( $utc_ts, '%Y-%m-%d %H:%M:%S' );
    my $local_tp = localtime($utc_tp->epoch);
    return $local_tp->strftime('%Y-%m-%d %H:%M:%S');
}

sub local_to_utc {
    my ($local_ts) = @_;
    my $local_tp = Time::Piece->strptime( $local_ts, '%Y-%m-%d %H:%M:%S' );
    my $utc_tp = gmtime($local_tp->epoch);
    return $utc_tp->strftime('%Y-%m-%d %H:%M:%S');
}

{
    $ENV{TZ} = 'America/Toronto';
    POSIX::tzset();

    # DST not in effect
    my $utc = '2015-03-08 06:59:00.000';
    my $local = utc_to_local(substr($utc, 0, -4));
    print "DST not in effect: utc($utc) = local($local)\n";

    # DST not in effect
    $utc = '2015-03-08 07:00:00.000';
    $local = utc_to_local(substr($utc, 0, -4));
    print "DST not in effect: utc($utc) = local($local)\n";
}

Output:

DST not in effect: utc(2015-03-08 06:59:00.000) = local(2015-03-08 01:59:00)
DST not in effect: utc(2015-03-08 07:00:00.000) = local(2015-03-08 03:00:00)
Comments