【问题标题】:What's the best way to get the UTC offset in Perl? [duplicate]在 Perl 中获取 UTC 偏移量的最佳方法是什么? [复制]
【发布时间】:2011-01-09 18:06:41
【问题描述】:

我需要以跨平台(Windows 和各种风格的 Unix)的方式获取 Perl 中当前时区的 UTC 偏移量。它应该符合这种格式:

zzzzzz,表示相对于 UTC 的 ±hh:mm

看起来我应该可以通过strftime() 获得它,但似乎不一致。

Unix:

Input: perl -MPOSIX -e "print strftime(\"%z\", localtime());"
Output: -0700

窗户:

Input: perl -MPOSIX -e "print strftime(\"%z\", localtime());"
Output: Mountain Standard Time

虽然 Unix 似乎给了我我想要的东西(或者至少是接近的东西),但 Windows 却没有。我很确定我可以使用 Date::Time 或类似的东西来做到这一点,但我真的希望没有任何依赖项,由于我们广泛的安装基础,我无法保证用户将拥有。

我在这里遗漏了一些明显的东西吗?提前致谢。

【问题讨论】:

    标签: perl formatting timestamp


    【解决方案1】:

    Time::Local 应该可以解决问题

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

    【讨论】:

    • Time::Local 是所有 Perl 5 发行版中的核心模块。
    • 这是一个很好的解决方案;只对其进行了初步测试,但它似乎可以正常工作。谢谢。
    【解决方案2】:

    这是一个仅使用核心 POSIX 模块的可移植解决方案:

    perl -MPOSIX -e 'my $tz = (localtime time)[8] * 60 - mktime(gmtime 0) / 60; printf "%+03d:%02d\n", $tz / 60, abs($tz) % 60;'
    

    奖励:以下子例程将返回带有时区偏移和微秒的完整时间戳,如“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;
    }
    

    【讨论】:

      【解决方案3】:

      您可以计算localtime($t)gmtime($t) 之间的差异。这是我受mobanswer 启发的版本:

      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】:

        另一种方法是使用Time::Piece,如下所示,文档here

        my $offsetinsecs = localtime(time)->tzoffset;
        my $datetimetz = sprintf("%+2d:%02d", $datetimetz, $offsetinsecs/3600, abs($offsetinsecs/60%60));
        

        手动测试的一种方法是更改​​ tz

        use POSIX qw(tzset);
        $ENV{TZ} = 'America/Los_Angeles';
        

        相关的stackoverflow分析器:https://stackoverflow.com/a/56396873/11337921

        【讨论】:

          【解决方案5】:

          一种可移植的方法是将localtime 的输出与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;
          

          有人指出这已经在 5、4、3、...的某个模块中实现了

          【讨论】:

          • 你能解释一下 if 语句中的计算——为什么要把年份乘以闰年的天数?为什么是月 * 31?为什么要加天?谢谢。
          • @Kevin Friedman - 测试@a[5,4,3]@b[5,4,3] 是否代表同一天。我将年、月和日组合成一个数字,这样我就可以通过一个比较而不是三个比较。您可以使用比 366 和 31 更大的数字,但仍然会得到正确的结果。
          • 如果你想更快,你应该使用位域而不是乘法:($a[5]&lt;&lt;13)|($a[4]&lt;&lt;5)|$a[3]。此外,&lt;=&gt; 运算符可用于替换 if 语句。
          • 更快:($a[5]&lt;&lt;9)|$a[7]$a[7] 是一年中的天数)
          猜你喜欢
          • 2011-06-20
          • 2021-07-23
          • 1970-01-01
          • 1970-01-01
          • 2010-12-05
          • 1970-01-01
          • 2011-07-05
          • 1970-01-01
          • 2014-09-21
          相关资源
          最近更新 更多