针对重复发生的事件,使用moment.js防止DST偏移
preventing DST shift with moment.js for recurring events
我正在构建的系统有一个事件组件,其中一部分是创建重复事件的能力。在我的数据库中,我将所有事件存储在UTC中。当一个定期事件显示在用户的日历上时,它应该始终以"墙时间"显示。因此,例如,如果我在每周三下午1:00创建一个定期活动,无论夏令时如何,它都应该始终在下午1:00。
我遇到的问题是,每当我试图使用Moment.js格式化这个日期时,Moment总是会考虑夏令时的变化,并相应地更新事件。以我之前的例子为例,在2016年,夏令时轮班发生在3月13日,因此,如果我的约会是在2016年2月预订的,那么直到3月13号的每个约会都会正确输出为下午1:00。3月13日之后,夏令时开始实施,我的所有活动现在都提前了一个小时。
有推荐的处理方法吗?我似乎找不到让Moment"忽略"DST的方法,在搜索时也找不到合适的解决方案。
不知道这有多重要,但我在输出日期时使用Moment时区转换为用户的本地时区。
谢谢!
根据最佳实践,未来的日期应存储在当地时间。这是因为,正如评论中所指出的,时区规则可以而且确实会改变。因此,您无法提前正确计算当地时间的UTC日期。
当你存储未来的日期时,你应该将它与用户期望事件时间所在的IANA时区一起存储。然后你实际上会做与现在相反的转换。如果您需要知道事件在全球时间线上的确切点,您可以将当地时间转换为UTC。
请注意,IANA时区数据库经常更新。它现在是2016d版本,这意味着它今年已经更新了四次。如果你的应用程序将在多个国家/地区使用,你需要非常勤奋地不断更新时刻时区以跟上变化。
至于为什么你现在看到你的UTC日期没有正确更改,我认为你是在计算你的计划日期,转换为UTC,然后计算你的重复时间。当你用UTC计算日期时,它不会正确解释夏令时的变化,因为它不知道这些变化。因此,当moment将日期转换回本地时,它将延迟一个小时。如果你用当地时间计算日期,然后转换为UTC进行存储,那么只要时区规则没有改变,你的转换就正确了。不过,不要这样做——用当地时间存储日期。
替代(部分)解决方案
还有一种解决夏令时轮班问题的替代方案注意:当某些时区的时区偏移量将来发生变化时,这将无法按预期工作它不使用moment.js,但我认为它仍然可能对某人有所帮助。
解释
诀窍是在创建实体时使用当前UTC到本地时间的偏移量来调整UTC日期,然后存储更新后的UTC日期。
假设本地时区是'Europe/Berlin'
,我们正在创建一个从'2020-03-28 10:00:00Z'
(UTC)开始的重复事件。这是我们的参考日期。此时时区的偏移量为+01:00
,因此用户的意图是让事件在11:00
开始。
现在,此事件一直重复到'2020-03-29'
(即该时区夏令时偏移后的第二天,该时区的偏移量为+02:00
)。因此,如果我们不更改任何内容,事件将在当天的12:00
显示(而不是按照用户的意愿显示11:00)。
因此,如果我们将日期存储为当天的'2020-03-29 09:00:00Z'
(UTC),则它将正确显示在当地时区的11:00。
代码
这有点棘手,我们应用时区偏移的部分肯定会得到改进,但对于我们的用例来说,这已经足够了。它在Node.js环境中使用date-fns和date-fns时区。
referenceDate
:UTC日期,用作要应用的偏移的参考(例如,系列的第一个日期;上例中的'2020-03-28 10:00:00Z'
date
:当前重复的UTC日期;上例中的'2020-03-29 10:00:00Z'
timeZone
:本地时区的IANA时区名称;上例中的'Europe/Berlin'
adjustedUTCDate
:调整后的UTC日期
转换代码:
// offset == '+01:00' in the example
let offset = dateFnsTimezone.formatToTimeZone(referenceDate, 'Z', { timeZone });
if (offset[0] === '+') {
offset = offset.replace('+', '-');
} else {
offset = offset.replace('-', '+');
}
const dateString = dateFns.format(date, 'YYYY-MM-DDTHH:mm:ss[Z]');
// applies the offset to change the time (intended local time); '2020-03-29 10:00:00+01:00' in the example
const localDate = dateString.replace('Z', offset);
// convert the date back to UTC, from the intended local time; '2020-03-29 09:00:00' in the example
const adjustedUTCDate = dateFnsTimezone.parseFromTimeZone(localDate, { timeZone });
- 针对重复发生的事件,使用moment.js防止DST偏移
- 如何使用Moment JS获得时间
- 使用moment.js获取时间分区的偏移日期对象
- 使用Moment添加天数.JS
- 使用moment.js获取月份和年份中的所有日期
- 使用moment.js分析日语日期字符串
- 如何使用moment.js获取24小时内日期之间的时差
- 如何正确使用moment.js
- 使用Moment.js从ISO字符串中提取utcOffset
- 如何使用moment.js将秒转换为时间格式(YYYY:MM:DD HH:MM:ss)
- 使用 moment.toDate() 时,在 Express 4.13.4 中忽略了 Cookie 到期日期
- 如何使用moment.js获取日期和时间
- 使用angular-moment.js设置日期格式
- 在Meteor中使用moment.tz.setDefault()时的作用域是什么
- 使用Moment.js并更新页面以显示秒数
- 如何使用npm在ES6中导入moment.js
- Moment.js只能使用一次
- moment.js如何使用
- moment:使用用户语言格式化日期时出错
- Angular Moment-使用自定义区域设置对象更改区域设置