PHP IntlDateFormatter周首日获取异常问题求助
问题分析与解决方案
我先帮你拆解下这个问题:你用IntlDateFormatter的'e'格式符获取2018-01-15(周一)的星期数值,得到了2,但根据你参考的ICU文档,预期应该是1(因为系统将周一设为周首日)。这个差异其实是ICU格式符'e'的定义细节,以及PHP Intl扩展的实现逻辑导致的。
为什么会出现不符合预期的结果?
先回顾你的代码:
$timezone = new \DateTimeZone('Europe/Amsterdam'); $date = new \DateTime('2018-01-15', $timezone); $formatter = new \IntlDateFormatter('nl_NL', null, null); // 正确输出"Monday"(2018-01-15确实是周一) echo $formatter->formatObject($date, 'EEEE'); // 输出"2",不符合预期的"1" echo $formatter->formatObject($date, 'e');
这里的核心矛盾点在于'e'格式符的实际行为:
- 你参考的ICU文档可能标注
'e'为**“本地星期几(1=周首日,7=周末日)”,但PHP的Intl扩展(依赖特定版本的ICU)中,'e'的实际逻辑是直接返回IntlCalendar的星期常量值**——而IntlCalendar的星期计数是从周日开始的:IntlCalendar::DOW_SUNDAY = 1,IntlCalendar::DOW_MONDAY = 2,以此类推。 - 你提到“系统将周一(对应数值2)设为周首日”,这里的数值2是
IntlCalendar的常量值,并非“周首日为1”的偏移计数,所以'e'会直接返回这个常量值,而非你预期的1。
如何得到“周首日为1”的星期数值?
如果你需要的是以系统/区域设置的周首日为起点的计数(比如周一=1、周二=2…周日=7,当周首日是周一的时候),不能直接用'e'格式符,需要借助IntlCalendar手动计算:
$timezone = new \DateTimeZone('Europe/Amsterdam'); $date = new \DateTime('2018-01-15', $timezone); // 创建对应区域和时区的IntlCalendar实例 $calendar = IntlCalendar::createInstance($timezone, 'nl_NL'); // 将DateTime的时间同步到IntlCalendar(注意IntlCalendar使用毫秒时间戳) $calendar->setTime($date->getTimestamp() * 1000); // 获取当前区域的周首日(nl_NL返回IntlCalendar::DOW_MONDAY,对应数值2) $firstDayOfWeek = $calendar->getFirstDayOfWeek(); // 获取当前日期的星期常量值(周一对应数值2) $currentDayOfWeek = $calendar->get(IntlCalendar::FIELD_DAY_OF_WEEK); // 计算相对于周首日的数值:周首日=1,依次递推 $relativeDay = ($currentDayOfWeek - $firstDayOfWeek + 7) % 7 + 1; echo $relativeDay; // 输出1,符合你的预期
额外:其他星期格式符的区别
如果你的需求是固定规则的星期数值,可以选择对应格式符:
- 固定ISO标准(周一=1,周日=7):使用
'u'格式符(需确保ICU版本支持)echo $formatter->formatObject($date, 'u'); // 输出1 - 以周日为1的计数(周日=1,周一=2…周六=7):使用
'c'格式符echo $formatter->formatObject($date, 'c'); // 输出2
内容的提问来源于stack exchange,提问作者Martin Winkel
相关产品推荐
相关产品推荐

