Perl 日期时间添加月份在 DST 时区中失败



这两行是测试数据

 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 库本身不这样做呢?如果我的解决方案是正确的,则不需要回答这个问题;)

谢谢

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

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。

文档中建议使用您的解决方案,因此应该没问题。

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

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

如果此方法确实适用于所有时区和所有 DST 方案

正确的方法做什么?

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

使用日期时间进行日期算术?我会使用"浮动"而不是"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

相关内容

  • 没有找到相关文章

最新更新