What's the best way to get the UTC offset in P

2020-03-01 18:10发布

问题:

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.

回答1:

Time::Local should do the trick

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


回答2:

"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;


回答3:

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);
}


回答4:

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, ...