JavaScript带时区偏移的toISOString日期解析格式适配问题
问题背景
开发仪表盘日期筛选功能时,配置了Radio单选按钮供用户点击设置待应用筛选规则的日期状态。
此前出现过时区导致的时间偏差问题:点击触发日期转toISOString格式逻辑时未考虑时区差异,比如选择「本年」选项时,本应返回午夜0点的时间实际返回了前一天22:00。通过自定义toISOStringWithTimezone方法做了时区偏移修正解决了该问题,但不清楚该方法生成的日期格式类型,配合自定义格式化函数处理时持续返回invalid time value错误。
相关代码
- 单选切换日期范围逻辑:
const onSwitchValueChange = useCallback((val) => { const toISOStringWithTimezone = date => { const tzOffset = -date.getTimezoneOffset(); const diff = tzOffset >= 0 ? '+' : '-'; const pad = n => `${Math.floor(Math.abs(n))}`.padStart(2, '0'); return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + pad(date.getDate()) + 'T' + pad(date.getHours()) + ':' + pad(date.getMinutes()) + ':' + pad(date.getSeconds()) + diff + pad(tzOffset / 60) + ':' + pad(tzOffset % 60); }; switch (val) { case SwitchOptions.this_month: const this_month = new Date(); const start = toISOStringWithTimezone(startOfMonth(this_month)); const end = toISOStringWithTimezone(endOfMonth(this_month)); setBaseDates({from: start, to: end}); break; case SwitchOptions.custom: break; default: break; } setSwitchValue(val) }, [setSwitchValue, SwitchOptions, setBaseDates])
- 弹窗日期渲染逻辑:
return <Modal className={"dates-filter-modal"} close={close}> <ModalBody> <Heading align={"center"} level={"2"}>{t("pages.transactions.filters.modals.dates.title")}</Heading> <Radio options={switchOptions} value={switchValue} onChange={onSwitchValueChange} /> <ChipList setList={() => setBaseDates({from: "", to: ""})} list={(!!baseDates.from && !!baseDates.to) ? [{label: "dates", value: baseDates}] : []} render={() => <><CryptoIcon name={"calendar"} />{t(`pages.transactions.filters.date`).replace("[from]", t(formatRelativeDate(baseDates.from))).replace("[to]", t(formatRelativeDate(baseDates.to)))}</>} removable={true}/> </ModalBody> </Modal>
- 原有日期格式化函数(适配UTC Z后缀格式):
export const formatRelativeDate = (rawDate, dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSX") => { const date = parseDate(rawDate, dateFormat) if (isToday(date)) return "common.dates.today"; if (isYesterday(date)) return "common.dates.yesterday"; if (isThisYear(date)) return formatDate(date, "dd MMM"); return formatDate(date, "dd MMM yyyy"); }
待解决问题
当前toISOStringWithTimezone生成的日期字符串携带+03:00这类±HH:mm格式的时区偏移后缀,而非此前的000ZUTC后缀,需要将formatRelativeDate函数的dateFormat参数修改为何种配置,才能正确解析新格式的日期字符串?
解决方案
先对比两种日期字符串的格式差异:
- 原适配的UTC格式:
2024-05-01T00:00:00.000Z,包含毫秒段,时区后缀为Z - 自定义方法生成的带时区格式:
2024-05-01T00:00:00+08:00,没有毫秒段,时区后缀为带冒号的±HH:mm偏移格式
直接把dateFormat默认值改成yyyy-MM-dd'T'HH:mm:ssXXX即可:
- 去掉原格式里的
.SSS毫秒匹配段,匹配自定义方法没有输出毫秒的特征 - 把原来单字符的
X(匹配Z/±HHmm/±HH格式时区)改成三个X的XXX,专门匹配带冒号的±HH:mm格式时区偏移
修改后的格式化函数代码如下:
export const formatRelativeDate = (rawDate, dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXX") => { const date = parseDate(rawDate, dateFormat) if (isToday(date)) return "common.dates.today"; if (isYesterday(date)) return "common.dates.yesterday"; if (isThisYear(date)) return formatDate(date, "dd MMM"); return formatDate(date, "dd MMM yyyy"); }
如果后续自定义时区方法加了毫秒输出,只需要把格式改成yyyy-MM-dd'T'HH:mm:ss.SSSXXX就能兼容。
内容的提问来源于stack exchange,提问作者Asher
相关产品推荐
相关产品推荐

