【问题标题】:Perl Date::Manip detects timezone wrong for EST and EDTPerl Date::Manip 检测到 EST 和 EDT 的时区错误
【发布时间】:2019-06-18 09:21:35
【问题描述】:

我有系统时区为 EST 的服务器

它是用

设置的
# timedatectl set-timezone EST
# timedatectl 
  Local time: Tue 2019-06-18 03:29:42 EST
  Universal time: Tue 2019-06-18 08:29:42 UTC
    RTC time: Tue 2019-06-18 08:29:42
    Time zone: EST (EST, -0500)
    NTP enabled: yes
    NTP synchronized: no
    RTC in local TZ: no
    DST active: n/a

然后在我的 perl 代码中,我尝试检测时区并将时间转换为 UTC 由于某种原因,时区被检测为 EDT。目前(夏季)EST 是我们在 EDT 之后的一个

我的 perl 代码是

# cat test.cgi # code file is named test.cgi
#!/usr/bin/perl -w

use Date::Manip;

print "Local time: ".UnixDate("now","%Y-%m-%d %H:%M:%S %Z")."\n";

# get local system timezone
my $TZ = UnixDate("now","%Z");

# convert time to GMT 
my $dtlocal =  Date_ConvTZ(ParseDate(UnixDate("now","%g")),$TZ,"GMT");

# format GMT time
my $dt =  UnixDate($dtlocal,"%Y-%m-%d %H:%M:%S"); 

print "Timezone: $TZ\n";
print "UTC time: $dt\n";

然后我执行脚本

# date
Tue Jun 18 04:17:56 EST 2019
# date -u
Tue Jun 18 09:17:58 UTC 2019
# ./test.cgi 
Local time: 2019-06-18 04:18:00 EDT
Timezone: EDT
UTC time: 2019-06-18 08:18:00

Date::Manip 错误检测时区的原因是什么? 还有其他检测系统时区的好方法吗?

更新。

我刚刚发现 POSIX 可以正确检测时区。所以,问题在于 Date::Manip

 use POSIX;
 print strftime("%Z", localtime()), "\n";

【问题讨论】:

  • 我无法很快找到 Date::Manip 决定您是在标准时间还是 DST 的确切位置,但美国在 2007 年更改了 DST 的开始/结束日期。您的 Date::Manip 是否可能已经足够老,仍然使用旧的开始/结束日期而不是当前日期?
  • 我的版本是 6.54 ,根据他们的文档,它来自 2016-05-31 。 metacpan.org/changes/distribution/Date-Manip
  • 我已经使用最新版本的 Date::Manip (6.77) 运行了您的代码,但我仍然看到相同的不正确行为。我建议reporting it as a bug
  • 请务必记住,EST 和 EDT 不是完全实现的时区。它们是偏移量。时区是 America/New_York(或适当的语言环境)。这就是您可以在同一时区中使用不同偏移量的冬季日期时间和夏季日期时间的方法。因此,DST 是否生效的“决定”取决于所讨论的时区和日期时间。
  • 因此听起来 Date::Manip 太聪明了,并假设所有 EST 时区都是 America/New_York。

标签: perl datetime date-manipulation


【解决方案1】:

Date::Manip::TZ 文档中有一个关于Determining the System Time Zone 的部分。如果您向下滚动一点,您会找到一个列表,其中列出了使用哪些方法(以及以什么顺序)来确定系统时区,具体取决于您是在 Unix 还是 Windows 下运行。

根据您在问题中发布的内容,它似乎可以正确读取当地时间,但无法识别正确的时区,因此您应该能够通过查看 TZ 列表找到根本问题您的操作系统上使用的识别方法。 (即,对于 Unix,检查 $::TZ,然后是 zoneTZ 环境设置,然后是 /etc/TIMEZONE 等,直到找到一个既存在且列出的时区错误的设置)

【讨论】:

    【解决方案2】:

    查找时区的一种方法是使用DateTime::TimeZone。与DateTime 一起使用,满足其他需求

    use warnings;
    use strict;
    use feature 'say';
    
    use DateTime; 
    use DateTime::TimeZone; 
    
    my $tz = DateTime::TimeZone->new(name => "local");
    say $tz->name();                                   #--> America/Los_Angeles
    
    
    my $dt = DateTime->now(time_zone => "local");
    say "local: $dt";                             #--> local: 2019-06-18T09:57:58
    
    $dt->set_time_zone("UTC"); 
    say "UTC: $dt";                               #--> UTC: 2019-06-18T16:57:58
    

    请参阅文档以了解详细信息,至少对于 DateTime::TimeZone constructor

    还要注意他们建议不要$tz->short_name...下使用“短”名称(“EST”)


    一旦你使用DateTime 也可以有偏移

    my $dt = DateTime->now(time_zone => 'local');
    
    say $dt->offset/(60*60);                       #--> -7
    
    say $dt->strftime("%z");                       #--> -0700
    

    offset 方法以秒为单位返回时区偏移量。

    strftime 提供我们通常期望的格式;文档说“任何与 POSIX 标准的偏差都可能是一个错误。”完整的时间戳示例:$dt->strftime('%Y-%m-%dT%H:%M:%S%z')

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-09
      • 2019-04-25
      • 2014-05-18
      • 1970-01-01
      • 2022-06-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多