I need to get the UTC offset of the current time zone in Perl in a cross platform (Windows and various flavors of Unix) way. It should meet this format:
zzzzzz, which represents ±hh:mm in relation to UTC
It looks like I should be able to get it via strftime()
, but it doesn't appear to be consistent.
Unix:
Input: perl -MPOSIX -e "print strftime(\"%z\", localtime());"
Output: -0700
Windows:
Input: perl -MPOSIX -e "print strftime(\"%z\", localtime());"
Output: Mountain Standard Time
While it appears that Unix is giving me what I want (or at least something close), Windows is not. I'm pretty sure I can do it with Date::Time
or similar, but I'd really like to not have any dependencies that I can't guarantee a user will have due to our wide install base.
Am I missing something obvious here? Thanks in advance.
Time::Local
should do the trick
use Time::Local;
@t = localtime(time);
$gmt_offset_in_seconds = timegm(@t) - timelocal(@t);
"I'd really like to not have any dependencies that I can't guarantee a user will have due to our wide install base"
How about including a custom copy of Date::Time (we'll call it My::Date::Time) in your installation? For example,
use lib 'my-module/dependencies/';
use My::Date::Time;
You can compute the difference between localtime($t)
and gmtime($t)
. Here is my version inspired by mob's answer:
use strict;
use warnings;
sub tz_offset
{
my $t = shift;
my @l = localtime($t);
my @g = gmtime($t);
my $minutes = ($l[2] - $g[2] + ((($l[5]<<9)|$l[7]) <=> (($g[5]<<9)|$g[7])) * 24) * 60 + $l[1] - $g[1];
return $minutes unless wantarray;
return (int($minutes / 60), $minutes % 60);
}
push @ARGV, time;
foreach my $t (@ARGV) {
printf "%s (%d): %+03d%02u\n", scalar localtime($t), $t, tz_offset($t);
}
A portable way is to compare the output of localtime
with gmtime
$t = time;
@a = localtime($t);
@b = gmtime($t);
$hh = $a[2] - $b[2];
$mm = $a[1] - $b[1];
# in the unlikely event that localtime and gmtime are in different years
if ($a[5]*366+$a[4]*31+$a[3] > $b[5]*366+$b[4]*31+$b[3]) {
$hh += 24;
} elsif ($a[5]*366+$a[4]*31+$a[3] < $b[5]*366+$b[4]*31+$b[3]) {
$hh -= 24;
}
if ($hh < 0 && $mm > 0) {
$hh++;
$mm = 60-$mm;
}
printf "%+03d:%02d\n", $hh, $mm;
Someone pointing out that this is already implemented in a module somewhere in 5, 4, 3, ...