What's the best way to get the UTC offset in Perl? [duplicate]
I need to get the UTC offset of the current time zone in Perl in a cross platform (W开发者_JAVA百科indows 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);
Here is a portable solution using only the core POSIX module:
perl -MPOSIX -e 'my $tz = (localtime time)[8] * 60 - mktime(gmtime 0) / 60; printf "%+03d:%02d\n", $tz / 60, abs($tz) % 60;'
Bonus: The following subroutine will return a full timestamp with time zone offset and microseconds, as in "YYYY-MM-DD HH:MM:SS.nnnnnn [+-]HHMM":
use POSIX qw[mktime strftime];
use Time::HiRes qw[gettimeofday];
sub timestamp () {
my @now = gettimeofday;
my $tz = (localtime $now[0])[8] * 60 - mktime(gmtime 0) / 60;
my $ts = strftime("%Y-%m-%d %H:%M:%S", localtime $now[0]);
return sprintf "%s.%06d %+03d%02d", $ts, $now[1], $tz / 60, abs($tz) % 60;
}
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);
}
An alternative is to use Time::Piece
like below, docs here
my $offsetinsecs = localtime(time)->tzoffset;
my $datetimetz = sprintf("%+2d:%02d", $datetimetz, $offsetinsecs/3600, abs($offsetinsecs/60%60));
One way to manually test is by changing the tz
use POSIX qw(tzset);
$ENV{TZ} = 'America/Los_Angeles';
related stackoverflow anser: https://stackoverflow.com/a/56396873/11337921
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, ...
精彩评论