当进入DST时移时,perl datetime truncate失败

时间:2014-11-20 02:47:45

标签: perl datetime timezone dst

use DateTime;
use DateTime::TimeZone;

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

失败并显示消息Invalid local time for date in time zone: America/Sao_Paulo

因为2014-10-19 00:00:00不是America/Sao_Paulo时区的有效时间。最终结果应为2014-10-19 01:00:00

我的伪代码理论

Try to truncate directly to the datetime object inside an eval

IF fails
   IF the truncation level is "day"
      -1 day 
      truncate
      + seconds of a day
   IF the truncation level is "month"
      -1 month 
      truncate
      seconds of a month. <--- Now here I need to take care of seconds of every month and even leapyear
 ELSE im happy

有助于改进我的理论或提供更简单的方法吗?

1 个答案:

答案 0 :(得分:0)

由于仅在小时设置为零时才发生错误,因此必须始终将小时设置为1。小时变化而不是午夜的区域不会有问题。

这意味着您可以像这样编写一个简单的异常处理程序。

我很想说没有区域在一个月的第一天开始夏令时,所以当截断到月份时不应该出现问题。但我无法保证,所以我也为此编了一个块。

use strict;
use warnings;
use 5.010;

use DateTime;
use DateTime::TimeZone;
use Try::Tiny;

my $dt = DateTime->new( {
    year => 2014, month => 10,  day => 19, 
    hour => 2,  minute=> 2,  second=> 0
});

my $sao_paolo = DateTime::TimeZone->new( name => 'America/Sao_Paulo' );

$dt->set_time_zone($sao_paolo);

try {
  $dt->truncate(to => 'day');
}
catch {
  die $_ unless /Invalid local time for date/;
  $dt->truncate(to => 'hour');
  $dt->set({hour => 1});
};

say $dt;

try {
  $dt->truncate(to => 'month');
}
catch {
  die $_ unless /Invalid local time for date/;
  $dt->truncate(to => 'hour');
  $dt->set({day => 1, hour => 1});
};

say $dt;

<强>输出

2014-10-19T01:00:00
2014-10-01T00:00:00