尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Node系列 · ORM:moment 与时区

Node系列 · ORM:moment 与时区 Node系列 · ORMmoment 与时区时间处理是后端的常见地雷服务器存的是 UTC但用户在不同时区看到的今天不一样。本章讲清楚 UTC 与时区的关系、moment 库的正确用法、以及服务器存 UTC、客户端按本地时区展示的推荐架构。一、UTC 与时区基础1.1 UTC 是什么UTCCoordinated Universal Time是世界时间标准以英国格林威治GMT为基准。UTC 0 点 英国格林威治当地时间的 0 点北京时间是 UTC8比 UTC 早 8 小时UTC 时间 8 小时 北京时间1.2 为什么推荐存 UTC方案优点缺点存 UTC 时间戳不同时区统一跨时区协作无歧义显示时需要转换存本地时间字符串显示直观跨时区错乱DST夏令时处理复杂存带时区的 ISO 8601信息完整不同数据库支持不一致::: tip存 UTC显示按本地时区——这是业界最佳实践。数据库存DATETIME不带时区或BIGINT时间戳应用层负责转换。:::二、moment 库npminstallmoment moment-timezone中文网momentjs.cn2.1 创建时间对象const moment require(moment); // 当前时间系统时区 moment(); moment(); // 两次不一样 // 字符串解析 moment(2024-08-15); // ISO 格式 moment(2024-08-15 14:30:00, YYYY-MM-DD HH:mm:ss); // 自定义格式 moment(15/08/2024, DD/MM/YYYY); // 显式指定格式 // 时间戳 moment(1723698600000); // ms 时间戳 moment.unix(1723698600); // s 时间戳 // 对象 moment({ year: 2024, month: 7, day: 15, hour: 14, minute: 30 }); // 注意month 是 0-1101月2.2 格式化输出const m moment(2024-08-15 14:30:45); m.format(YYYY-MM-DD HH:mm:ss); // 2024-08-15 14:30:45 m.format(YYYY年MM月DD日); // 2024年08月15日 m.format(YYYY/MM/DD dddd); // 2024/08/15 Thursday m.format(a h:mm:ss); // pm 2:30:45 // 常用格式符 m.format(YYYY); // 4 位年 m.format(MM); // 2 位月 m.format(DD); // 2 位日 m.format(HH); // 24 小时制 m.format(hh); // 12 小时制 m.format(mm); // 分钟 m.format(ss); // 秒 m.format(SSS); // 毫秒 m.format(A); // AM / PM m.format(a); // am / pm m.format(Z); // 时区偏移如 08:00 m.format(ZZ); // 时区偏移如 08002.3 时区转换const moment require(moment-timezone); // 系统时区时间北京时间 const beijingTime moment(2024-08-15 14:30:00); // 假设系统时区是 Asia/Shanghai // 转为 UTC const utcTime beijingTime.utc(); utcTime.format(); // 2024-08-15T06:30:00Z // 转为纽约时间 const nyTime beijingTime.tz(America/New_York); nyTime.format(); // 2024-08-14T22:30:00-04:00 // 列出所有时区 moment.tz.names().slice(0, 5); // [Africa/Abidjan, Africa/Accra, Africa/Addis_Ababa, ...]时区 ID偏移UTC0Asia/Shanghai08:00北京时间Asia/Tokyo09:00America/New_York-05:00 / -04:00夏令时Europe/London0 / 01:00夏令时三、推荐架构UTC 客户端本地化3.1 服务器端// ✅ 数据库统一存 UTCDATETIME 不带时区按 UTC 解读 const createdAt moment.utc().format(YYYY-MM-DD HH:mm:ss); // 2024-08-15 06:30:00 // ✅ API 返回 ISO 8601 带 Z 后缀 res.json({ createdAt: moment.utc().toISOString(), // 2024-08-15T06:30:00.000Z }); // ❌ 错误返回本地时间字符串 moment().format(); // 2024-08-15 14:30:00 ← 看不出时区3.2 客户端// 拿到服务器返回的 UTC 时间字符串 const utcTime 2024-08-15T06:30:00.000Z; // 客户端按本地时区显示 const local new Date(utcTime).toLocaleString(zh-CN); // 2024/8/15 14:30:00北京时间用户看到的 // 或用 Intl.DateTimeFormat 控制格式 new Intl.DateTimeFormat(zh-CN, { dateStyle: long, timeStyle: short, timeZone: Asia/Shanghai, }).format(new Date(utcTime)); // 2024年8月15日 14:30::: tip服务器只负责发 UTC 时间戳所有时区转换在客户端做。这样全球用户各自看到本地时间服务器逻辑简单永远用 UTC数据库统一同一种格式DST / 夏令时问题由浏览器/OS 处理:::3.3 表单录入接收 ISO 字符串// 前端表单提交时把本地时间转 UTC const localDate 2024-08-15 14:30:00; const utcDate new Date(localDate).toISOString(); await api.post(/events, { startAt: utcDate });四、moment 的局限4.1 不可变 vs 可变const m moment(2024-08-15); m.add(1, day); console.log(m.format(YYYY-MM-DD)); // 2024-08-16 // ⚠️ moment 是可变的多次复用会出 bug const dates [2024-08-15, 2024-08-16, 2024-08-17]; const formatted dates.map((d) moment(d).add(1, day).format(YYYY-MM-DD)); // 正确4.2 维护状态::: warningmoment 已进入维护模式作者建议迁移到其他库。新项目不要首选 moment。:::五、替代方案5.1Day.js轻量级npminstalldayjsconst dayjs require(dayjs); // API 与 moment 几乎一致 dayjs(2024-08-15).format(YYYY-MM-DD); // 时区插件 const utc require(dayjs/plugin/utc); const timezone require(dayjs/plugin/timezone); dayjs.extend(utc); dayjs.extend(timezone); dayjs(2024-08-15 14:30:00).tz(Asia/Shanghai).format(); // 2024-08-15T14:30:0008:00体积仅 7 KBmoment 是 290 KBAPI 兼容 moment是推荐的替代方案。5.2date-fns函数式npminstalldate-fns date-fns-tzconst { format, addDays } require(date-fns); const { zonedTimeToUtc, utcToZonedTime, format } require(date-fns-tz); format(new Date(), yyyy-MM-dd HH:mm:ss); addDays(new Date(), 7); // 时区 const shanghaiTime utcToZonedTime(new Date(), Asia/Shanghai); format(shanghaiTime, yyyy-MM-dd HH:mm:ssXXX, { timeZone: Asia/Shanghai });函数式 API每个功能独立 import更利于 tree-shaking。5.3 选型对照库大小API 风格时区支持推荐moment290 KB链式 可变对象内置moment-timezone⚠️ 维护模式老项目Day.js7 KB链式 不可变插件✅ 新项目首选date-fns按需函数式date-fns-tz✅ 函数式偏好luxon22 KB不可变对象内置✅ 时区 / 国际化重需求六、最佳实践场景推荐服务器存时间永远存 UTCDATETIME 或 BIGINT 时间戳API 返回ISO 8601 字符串带Z后缀时区转换客户端按本地时区展示库选型新项目用 Day.js / date-fns解析用户输入new Date(input).toISOString()转 UTC表单时间选择器用户选本地时间 → 转 UTC → 提交七、小结存 UTC、显示本地——后端最稳的时间处理架构UTC 是世界时间标准中国是 UTC8比 UTC 早 8 小时moment 库 API 友好但已进入维护模式新项目用Day.js或date-fnsAPI 返回 ISO 8601 字符串带Z后缀客户端按本地时区格式化moment 是可变对象多次复用要.clone()否则会出 bug
返回列表