使用Time::Moment获取圣枝主日时PreviousDayOfWeek的异常问题排查
PreviousDayOfWeek(7)从复活节周日获取圣枝主日会得到周二? 这个问题的核心要么是对方法逻辑的理解偏差,要么是遇到了模块版本的bug,我们一步步拆解:
先明确PreviousDayOfWeek的官方逻辑
官方文档明确说明:
$adjuster = PreviousDayOfWeek($day);
调整日期到给定星期几[1=周一,7=周日]的上一个出现日期(早于当前日期)。
划重点:它找的是当前日期之前最近的指定星期几,而不是直接往前推整一周。比如如果当前日期是周一(1),调用PreviousDayOfWeek(7)(周日)会得到前一天的周日,而不是7天前的周日。
但回到你的例子:2018年复活节是2018-04-01(周日,day_of_week=7),按照逻辑,调用PreviousDayOfWeek(7)应该找早于4月1日的最近一个周日——也就是7天前的2018-03-25,但你得到的却是提前5天的周二,这明显不符合预期。
可能的异常根源
1. 参数传递笔误
最常见的情况是你在代码中误传了参数2(对应周二)而不是7,刚好得到2018-03-27。建议你检查代码中的参数是否确实写的是7,同时可以打印出复活节日期的星期几确认:
print $easter_sunday->day_of_week(), "\n"; # 应该输出7(周日)
2. 模块版本bug
如果参数确实是7,那大概率是你使用的Time::Moment或Time::Moment::Adjusters版本存在bug,导致参数7被错误解析为周二。比如旧版本中可能存在星期几映射表错误,或者参数模运算处理失误,把7对应到了周二的逻辑。
建议你升级到最新版本的模块再测试:
cpanm Time::Moment Time::Moment::Adjusters
3. 文档与实现不一致
极少数情况下,模块文档的星期几定义和实际实现不符(比如实际是0=周日到6=周六,而文档写的是1=周一到7=周日),导致传入7时被视为无效值,触发了错误的默认逻辑。不过这种情况概率极低,且通常会有其他异常表现。
符合需求的Adjusters替代方案
既然你需要用Adjusters风格处理节日,除了直接用minus_days(7),可以自定义一个Adjuster来实现圣枝主日的计算:
use Time::Moment; use Time::Moment::Adjusters qw(WesternEasterSunday); # 自定义圣枝主日调整器 sub PalmSundayAdjuster { return sub { my ($tm) = @_; return $tm->minus_days(7); }; } # 使用示例 my $easter_sunday = Time::Moment->new(year => 2018)->with(WesternEasterSunday); my $palm_sunday = $easter_sunday->with(PalmSundayAdjuster()); print $palm_sunday, "\n"; # 输出2018-03-25T00:00:00Z
这样既满足了你用Adjusters统一处理节日的需求,又能避免依赖可能有问题的PreviousDayOfWeek逻辑。
内容的提问来源于stack exchange,提问作者sshine

