【问题标题】:perl datetime add months fails in dst timezoneperl datetime 添加月份在 dst 时区失败
【发布时间】:2014-11-20 11:38:30
【问题描述】:

这两行是测试数据

 my $dt = DateTime->new( {
     year => 2014, month => 9, day => 19, 
     hour => 00, minute=> 00, second=> 00});
 $dt->set_time_zone(DateTime::TimeZone->new( name => 'America/Sao_Paulo' ));

现在我想用失败的日期添加一个月

 $dt->add ( months => 1 );
 # This  fails because  the result date is 2014-10-19 00:00:00 and for Sao_Paulo
 # the result should be a DST shifted time 2014-10-19 01:00:00 

我想出的最简单的解决方案是这样的

 my $tz_backup = $dt->time_zone();   # Backup the timezone
 $dt->set_time_zone('UTC');          # Moving the time to UTC to get rid of DST complexities
 $dt->add ( months => 1 );           # Perform the original operation
 $dt->set_time_zone($tz_backup);     # Setting back the original timezone

问题是,这个解决方案是否存在任何缺陷?

如果这种方法确实适用于所有时区和所有 DST 场景,那么为什么 perl DateTime 库自己不这样做呢? 如果我的解决方案是正确的,则不需要回答这个问题;)

谢谢

【问题讨论】:

  • 当我运行您的原始代码时,我收到“时区日期的本地时间无效”错误。
  • 你没有提到你想用这些变异的日期时间对象做什么,但在我看来你可能对时间字段不感兴趣。我建议,与其费力维护一个有效的日期时间,不如简单地将值转换为一个Date::Simple 对象,该对象没有可能“无效”的时间字段。
  • 我还认为您应该避免显式使用 DateTime::TimeZone 类,而是将区域作为参数添加到构造函数:my $dt = DateTime->new(year => 2014, month => 9, day => 19, time_zone => 'America/Sao_Paulo')

标签: perl datetime timezone dst


【解决方案1】:

为什么图书馆不自己做呢?因为一个月不是一定的秒数,它是一定的(因月而异)天数。如果您想要增加一个月的秒数,您可以这样做:

my $dt = DateTime->new( year => 2014, month => 9, day => 19, time_zone => 'America/Sao_Paulo' );
$dt->add( seconds => DateTime->last_day_of_month( year => $dt->year, month => $dt->month )->day * 24 * 60 * 60 );

或者按照您的建议,只使用 UTC。

【讨论】:

    【解决方案2】:

    documentation推荐你的解决方案,所以应该没问题。

    另外,当我运行你的原始代码时,我收到了一个错误:

    Invalid local time for date in time zone: America/Sao_Paulo
    

    【讨论】:

      【解决方案3】:

      如果这种方法真的适用于所有时区和所有 DST 场景

      正确的做法是什么?

      一个月后获取日期时间?不,这给出了错误的结果。

      要使用 DateTime 进行日期运算吗?我会使用“浮动”而不是“UTC”,这样您就不必转换回来了。使用 UTC 也会给您带来错误的结果。

      $ perl -MDateTime -E'
         my $dt = DateTime->now( time_zone => "America/Toronto" )->set_hour(0);
         say $dt->ymd;
      
         say $dt->clone
            ->set_time_zone("UTC")
               ->truncate( to => "day" )
                  ->set_time_zone("America/Toronto")
                     ->ymd;
      
         say $dt->clone
            ->set_time_zone("floating")
               ->truncate( to => "day" )
                  ->ymd;
      '
      2014-11-20
      2014-11-19   # XXX
      2014-11-20   
      

      那为什么 perl DateTime 库自己不这样做呢?

      你要求它在一个月后给 DT。在没有被告知的情况下推迟一个月零一小时是不正确的。

      换句话说,人们期望的附加功能如下:

      timestamp + duration - duration = timestamp
      

      【讨论】:

        猜你喜欢
        • 2017-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-12
        • 1970-01-01
        • 1970-01-01
        • 2018-03-14
        相关资源
        最近更新 更多