如何使用Moment.js获取指定年份周首日及两段代码结果差异原因
Moment.js 两段代码执行结果差异原因说明
核心前提:ISO 8601周规则与Moment的两个年份概念
首先明确两个容易混淆的时间定义:
- ISO 8601周规则:每周从周一开始,每年的第1周为包含当年第一个周四的周,因此年初/年末的日期可能属于上一年或下一年的ISO周,对应的年份和日历年可能有±1的偏差。
- Moment两类年份属性:
- 日历年:对应
moment.year(),即常规公历纪年,只要日期落在1月1日~12月31日之间就归属对应年份。 - ISO周年:对应
moment.isoWeekYear(),即日期所属ISO周对应的年份,和日历年不一定一致。
- 日历年:对应
第一段代码执行逻辑(结果不符合预期)
你的第一段代码:
// result is: 2014/12/29 moment('2016/01/01').isoWeek(1).startOf('isoWeek').format("YYYY/MM/DD")
执行过程如下:
- 初始化
moment('2016/01/01'):2016年1月1日是周五,属于2015年的最后一个ISO周,因此这个时间对象的year()为2016,isoWeekYear()为2015。 - 调用
isoWeek(1):isoWeek方法的默认行为是基于当前时间的isoWeekYear设置周数,因此这一步是把时间设置为2015年的第1个ISO周的周五(保留原时间的星期属性),对应日期为2015年1月2日。 - 调用
startOf('isoWeek'):跳转至当周周一,也就是2014年12月29日,最终得到你看到的异常结果。
第二段代码执行逻辑(结果符合预期)
你的第二段代码:
// result is: 2016/01/04 moment().year(2016).isoWeek(1).startOf('isoWeek').format("YYYY/MM/DD")
执行过程如下:
- 初始化当前时间后调用
year(2016):会生成一个落在2016年的常规日期(只要不是2016年1月初或12月末的极端日期),这个日期的isoWeekYear和日历年一致,都是2016。 - 调用
isoWeek(1):基于当前的isoWeekYear=2016设置为2016年第1周。 - 调用
startOf('isoWeek')跳转至当周周一,也就是2016年1月4日,符合预期。
最优写法推荐
如果要稳定获取指定年份的第一个ISO周首日,不受初始时间上下文影响,推荐显式设置ISO周年:
// 通用稳定写法,任意调用场景下都能得到正确结果 moment().isoWeekYear(2016).isoWeek(1).startOf('isoWeek').format("YYYY/MM/DD")
内容的提问来源于stack exchange,提问作者Coy
相关产品推荐
相关产品推荐

