这两行是测试数据
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