TypeScript 中的时区:如何不被日期逼疯

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么时间是每个开发者的头疼问题

\n\n

您是否曾经遇到过这样的 bug:来自符拉迪沃斯托克的用户看到的是明天的日期,而日历上显示的却是今天?或者,当 AWS us-east-1 上的服务器发送时间,而您在加里宁格勒的客户端却将其解释为凌晨 3 点?欢迎来到 TypeScript 的时区世界。

\n\n

问题在于,JavaScript(以及作为其超集的 TypeScript)最初是为浏览器设计的。内置的 Date 对象本质上是一个伪装成 UTC 的时间戳。它无法像我们需要的那样处理时区。如果您正在用 TypeScript 编写一个严肃的应用程序——无论是 React 管理后台、Node.js 后端还是 React Native 移动应用——没有妥善的时区处理是行不通的。

\n\n

在本文中,我将向您展示如何驯服时间。我们将理解基本概念,学习使用现代库,并避免常见的陷阱。我们开始吧!

\n\n

在编写代码之前需要了解的时区知识

\n\n

在深入 TypeScript 之前,让我们先明确一些概念。世界上有两种时间:绝对时间(UTC)和本地时间(用户看到的时间)。

\n\n
    \n
  • UTC(协调世界时) 是标准时间。服务器、数据库和 API 应该以 UTC 存储时间。这是一条铁律。
  • \n
  • IANA 时区数据库 是一个时区字典,格式如 Europe/Moscow、Asia/Vladivostok。在代码中应该使用这些字符串,而不是像 MSK 或 PST 这样的缩写。
  • \n
  • 偏移量 是与 UTC 的差值,例如 +03:00。但请记住:偏移量可能会因为夏令时(DST)而改变。因此,存储偏移量而不是 IANA 时区是一个坏主意。
  • \n
\n\n
\n

黄金法则:以 UTC 存储,在客户端转换,使用 IANA 字符串标识时区。

\n
\n\n

为什么原生的 Date 不够用:痛苦与折磨

\n\n

让我们面对现实:TypeScript 中的 new Date() 就像一把刀刃坏了的瑞士军刀。它只能存储时间戳,并在浏览器或服务器的本地时区显示。尝试做一些更复杂的事情:

\n\n
// 糟糕的例子:尝试用原生方法处理时区\nconst date = new Date('2024-03-10T12:00:00');\nconsole.log(date.toLocaleString('ru-RU', { timeZone: 'Europe/Moscow' }));\n// 输出:"10.03.2024, 15:00:00" —— 但这只是一个字符串,不是对象!\n\n// 如果需要获取另一个时区的 Date 呢?\n// 哎呀... 原生 JS 无法创建绑定到时区的 Date。\n
\n\n

问题在于,Date 始终以 UTC 存储时间。所有像 getHours() 这样的方法只是将 UTC 转换为浏览器的本地时间。您无法获得一个“认为”自己在纽约的 Date 对象。

\n\n

这就是我们使用库的原因。在 TypeScript 生态系统中,有两个主要候选者:Luxon 和 date-fns-tz。

\n\n

Luxon:处理日期的重型武器

\n\n

Luxon 是一个现代库,由 Moment.js(已弃用)的创建者开发。它是从头开始编写的,使用原生的 Intl.DateTimeFormat,并且为 TypeScript 提供了良好的类型定义。

\n\n

安装 Luxon

\n\n
npm install luxon\nnpm install --save-dev @types/luxon\n
\n\n

在特定时区创建日期

\n\n
import { DateTime } from 'luxon';\n\n// 在 Europe/Moscow 时区创建日期\nconst moscowTime = DateTime.fromObject(\n  { year: 2024, month: 3, day: 10, hour: 12 },\n  { zone: 'Europe/Moscow' }\n);\nconsole.log(moscowTime.toISO());\n// 输出:"2024-03-10T12:00:00.000+03:00"\n\n// 转换为另一个时区\nconst nyTime = moscowTime.setZone('America/New_York');\nconsole.log(nyTime.toISO());\n// 输出:"2024-03-10T05:00:00.00                

博客

书籍推荐