Anders
Anders

Reputation: 6218

How can I change the timezone of a datetime value in Perl?

Using this function:

perl -e 'use Time::Local; print timelocal("00","00","00","01","01","2000"),"\n";'

It will return an epochtime - but only in GMT - if i want the result in GMT+1 (which is the systems localtime(TZ)), what do i need to change?

Thanks in advance,

Anders

Upvotes: 7

Views: 16762

Answers (7)

HoldOffHunger
HoldOffHunger

Reputation: 20931

The Algorithm

If you want to change a time value from one timezone to another timezone, you must be able to indicate both timezones.

After all, if you set if you want to convert "12:30" to GMT or US/Eastern or Venezuelan time, which means adding/subtracting some amount of hours or hours and minutes, you need to know what timezone is the starting time zone, otherwise, the calculation won't know how much to add or subtract.

If you use DateTime->now;, the timezone is defaulted to the system-time, which may not be the timezone you want to convert from.

In the below code, I demonstrate how to initialize the datetime object to the right starting timezone (fromtimezone) and how to convert that time to the ending timezone (totimezone)...

Working Code

I could not find a Perl sandbox online with the DateTime CPAN module installed.

use strict;
use DateTime;

sub convertTimeZonesForTime {
        my ($args) = @_;

        my $time = $args->{time};
        my $date = $args->{date};
        my $totimezone = $args->{totimezone};
        my $fromtimezone = $args->{fromtimezone};
        my $format = $args->{format} || '%H:%M:%S';

        my ($year, $month, $day) = map {int $_} split('-', $date);
        my ($hour, $minute, $second) = map {int $_} split(':', $time);

        $year ||= 1999 if !defined $year;
        $month ||= 1 if !defined $month;
        $day ||= 1 if !defined $day;
        $hour ||= 12 if !defined $hour;
        $minute ||= 30 if !defined $minute;
        $second ||= 0 if !defined $second;

        my $dt = DateTime->new(
                year=>$year,
                month=>$month,
                day=>$day,
                hour=>$hour,
                minute=>$minute,
                second=>$second,
                time_zone => $fromtimezone,
        );
        my $formatter = new DateTime::Format::Strptime(pattern => $format);
        $dt->set_formatter($formatter);
        $dt->set_time_zone($totimezone);

        return "$dt";
}

print(convertTimeZonesForTime({
    'totimezone'=>'America/Denver',
    'fromtimezone'=>'US/Eastern',
    'time'=>'12:30:00',
}));

Output:

10:30:00

Upvotes: 0

Kaoru
Kaoru

Reputation: 1570

While Time::Local is a reasonable solution, you may be better off using the more modern DateTime object oriented module. Here's an example:

use strict;
use DateTime;
my $dt = DateTime->now;
print $dt->epoch, "\n";

For the timezones, you can use the DateTime::TimeZone module.

use strict;
use DateTime;
use DateTime::TimeZone;

my $dt = DateTime->now;
my $tz = DateTime::TimeZone->new(name => "local");

$dt->add(seconds => $tz->offset_for_datetime($dt));

print $dt->epoch, "\n";

CPAN Links:

DateTime

Upvotes: 3

palik
palik

Reputation: 2863

An other example based on DateTime::Format::Strptime

use strict;
use warnings;
use v5.10;
use DateTime::Format::Strptime;

my $s = "2016-12-22T06:16:29.798Z";
my $p = DateTime::Format::Strptime->new(
  pattern => "%Y-%m-%dT%T.%NZ",
  time_zone => "UTC"
);

my $dt = $p->parse_datetime($s);    
$dt->set_time_zone("Europe/Berlin");
say join ' ', $dt->ymd, $dt->hms; # shows 2016-12-22 07:16:29

Upvotes: 3

Otto
Otto

Reputation: 3294

use DateTime;
my $dt   = DateTime->now;
$dt->set_time_zone( 'Europe/Madrid' );

Upvotes: 10

mob
mob

Reputation: 118665

There is only one standard definition for epochtime, based on UTC, and not different epochtimes for different timezones.

If you want to find the offset between gmtime and localtime, use

use Time::Local;
@t = localtime(time);
$gmt_offset_in_seconds = timegm(@t) - timelocal(@t);

Upvotes: 4

Greg Bacon
Greg Bacon

Reputation: 139661

Time::Local::timelocal is the inverse of localtime. The result will be in your host's local time:

$ perl -MTime::Local -le \
    'print scalar localtime timelocal "00","00","00","01","01","2000"'
Tue Feb  1 00:00:00 2000

Do you want the gmtime that corresponds to that localtime?

$ perl -MTime::Local' -le \
    'print scalar gmtime timelocal "00","00","00","01","01","2000"'
Mon Jan 31 23:00:00 2000

Do you want it the other way around, the localtime that corresponds to that gmtime?

$ perl -MTime::Local -le \
    'print scalar localtime timegm "00","00","00","01","01","2000"'
Tue Feb  1 01:00:00 2000

Upvotes: 2

William Pursell
William Pursell

Reputation: 212514

You just need to set the timezone. Try:

env TZ=UTC+1 perl -e 'use Time::Local; print timelocal("00","00","00","01","01","2000"),"\n";'

Upvotes: 2

Related Questions