最近在业务中遇到一个使用 Day.js
创建 UTC 时间再转换时区的异常。纠正了长久以来一个自己对于 Date()
构造函数的误解。
展示一段示例代码,以便让大家来理解我的误解:
// 引入 dayjs 并扩展 utc
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
dayjs.extend(utc)
// 示例时间文本
const demoTimeStr = '2025-08-20 00:00:00'
// 使用 dayjs 创建一个 UTC 模式的 dayjs 实例
const utcDayjsInst = dayjs.utc(demoTimeStr)
// 输出格式化后的时间文本
console.log('utcDayjsInst1.format:', utcDayjsInst.format('YYYY-MM-DD HH:mm:ss'))
// utcDayjsInst1.format: 2025-08-20 00:00:00
// 使用示例时间文本创建一个 Date 实例
const demoDateInst = new Date(demoTimeStr)
// 使用 Date 实例创建一个 UTC 模式的 dayjs 实例
const utcDayjsInst2 = dayjs.utc(demoDateInst)
// 输出格式化后的时间文本
console.log('utcDayjsInst2.format:', utcDayjsInst2.format('YYYY-MM-DD HH:mm:ss'))
// utcDayjsInst2.format: 2025-08-19 16:00:00
可以非常直接明显的看到两个UTC模式下输出的时间文本是不一样的。
当然在事后写回顾的时候真的觉得自己特别的蠢 😂,但确实在当时困扰了我一段时间。
其实原因很简单,在 MDN 文档中就已经说明了:
Date
创建一个 JavaScript
Date
实例,该实例呈现时间中的某个时刻。Date
对象则基于 Unix Time Stamp,即自 1970 年 1 月 1 日(UTC)起经过的毫秒数。
其实也就是说 Date
对象是 UTC 模式 的,那么自然返回值也都是 UTC 模式下经过的毫秒数。
但是我们在浏览器中调试时,工具会帮我们显示为本地化时间(并不是找借口甩锅)。也就是经常看到的 Date Wed Aug 20 2025 17:47:48 GMT+0800 (中国标准时间)
所以实践比理论先行的我会有一个错误的认知
,即:会获取的系统时间和时区去创建时间对象,而不是按照系统时间和时区创建一个UTC模式的时间对象,只是在调试工具中显示成本地时间。
🚧 发现其它问题
在阅读 MDN 文档时,发现了一个需要注意的特殊提醒:
备注: 当用
Date
构造函数(和Date.parse
,它们是等价的)解析日期字符串时,一定要确保输入符合 ISO 8601 格式(YYYY-MM-DDTHH:mm:ss.ssZ
),其他格式的解析行为是实现定义的,可能无法在所有浏览器上运行。对 RFC 2822 格式字符串的支持只是惯例。如果要适应许多不同的格式,库可以提供帮助。仅有日期的字符串(例如 “
1970-01-01
“)被视为 UTC,而日期时间的字符串(例如 “1970-01-01T12:00
“)被视为本地时间。因此,我们也建议你确保这两种类型的输入格式是一致的。
那么还是有一些特殊逻辑在里面的,
new Date('2025-08-20')
// Date Wed Aug 20 2025 08:00:00 GMT+0800 (中国标准时间)
new Date('2025-08-20 00:00:00')
// Date Wed Aug 20 2025 00:00:00 GMT+0800 (中国标准时间)
可以看到 new Date('2025-08-20')
创建的日期对象是 2025-08-20 08:00:00 GMT+0800
,
而 new Date('2025-08-20 00:00:00')
创建的日期对象是 2025-08-20 00:00:00 GMT+0800
。
这些细微的区别在使用 new Date()
时还是需要注意的。
不过在使用 Day.js
中并不会出现这些细微的特殊逻辑,结果是可预期的一致。
dayjs('2025-08-20').format("YYYY/MM/DD HH:mm:ss")
// "2025/08/20 00:00:00"
dayjs('2025-08-20 00:00:00').format("YYYY/MM/DD HH:mm:ss")
// "2025/08/20 00:00:00"