Skip to content

JavaScript Date 语法要过时了!以后用这个替代! #393

@mqyqingfeng

Description

@mqyqingfeng

1. 前言

作为一名前端开发工程师,你一定被 JavaScript 的日期处理折磨过。

这不是你的问题,是 JavaScript 自己的问题——它的 Date 功能真的很糟糕。

2. Date 的离谱行为

让我给你举几个例子,你就明白有多离谱了:

月份从 0 开始计数:

// 你以为这是 2026 年 1 月 1 日?
console.log(new Date(2026, 1, 1));
// 结果:2026 年 2 月 1 日!

// 因为月份是从 0 开始数的:0=1月,1=2月...
// 但年份和日期又是正常计数的

日期格式混乱到让人抓狂:

// 用斜杠分隔,加不加前导零都没问题
console.log(new Date("2026/01/02"));
// Fri Jan 02 2026 00:00:00 GMT+0800 (中国标准时间)

// 但如果用短横线分隔,同样的写法
console.log(new Date("2026-01-02"));
// Fri Jan 02 2026 08:00:00 GMT+0800 (中国标准时间)

// 时间居然不一样了!

// 如果用东半球标准时间,更离谱!一个是 1 月 2 日,一个是 1 月 1 日

两位数年份的迷惑行为:

console.log(new Date("49")); // 2049 年
console.log(new Date("99")); // 1999 年
console.log(new Date("100")); // 公元 100 年!

规则莫名其妙:33-99 代表 1900 年代,但 32-49 又代表 2000 年代,100 以上就真的是公元那一年了。

更致命的问题是 —— 日期居然可以被“改变”!

const today = new Date();
console.log(today.toDateString()); // Fri Jan 09 2026

// 我想算一下明天是几号
const addDay = (theDate) => {
  theDate.setDate(theDate.getDate() + 1);
  return theDate;
};

console.log(`明天是 ${addDay(today).toLocaleDateString()}。`);
// 明天是 2026/1/10。

console.log(`今天是 ${today.toLocaleDateString()}。`);
// 今天是 2026/1/10。

// 等等,今天怎么也变成明天了?!

当然这是可以解释的:

因为 today 就像一个地址,指向内存里的某个位置。当你把 today 传给函数时,函数拿到的也是这个地址。所以当函数修改日期时,原来的 today 也被改了。

但这种设计违反了一个基本常识:日期应该是固定的。“2026 年 1 月 10 日”就是“2026 年 1 月 10 日”,不应该因为你拿它做了个计算,它自己就变了。

所以 Date 真的很糟糕。实际上,它就是挂羊头卖狗肉,它叫做 Date,表示日期,实际上,它是时间。

在内部,Date 是以数值形式存储的,这就是我们熟悉的以 1000 毫秒为单位的时间戳。

时间当然包含日期,你可以从时间中推断出日期,但这多少有点恶心了。

Java 早在 1997 年就弃用了其 Date 类,而 JavaScript 的 Date 类仅仅在几年后就问世了;与此同时,我们却一直被这个烂摊子困扰着。

正如你目前所见,它在解析日期方面极其不稳定。它除了本地时间和格林威治标准时间 (GMT) 之外,对其他时区一无所知。而且,Date 类只支持公历。它完全不理解夏令时的概念。当然最糟糕的还是它的可变的,这直接让他偏离了时间的本质。

所有这些缺陷使得使用第三方库来解决这些问题变得异常普遍,其中一些库体积庞大,这种性能损耗已经对网络造成了切实可衡量的损害。

3. Temporal 才是未来

幸运的是,Date 即将彻底退出历史舞台。

当然这样说,还是有点夸张了。

实际上是它会一直存在,但如果可以避免,你最好不要再用它了。

因为我们会有一个完全取代 Date 的对象 —— Temporal。

部分同学可能对 Temporal 这个单词不太熟悉,实际上,它的意思就是“时间”,你可以理解为它是一个更专业的词汇:

与 Date 不同,Temporal 不是构造函数,它是一个命名空间对象——一个由静态属性和方法组成的普通对象,就像 Math 对象一样:

console.log(Temporal);
/* Result (expanded):
Temporal { … }
  Duration: function Duration()
  Instant: function Instant()
  Now: Temporal.Now { … }
  PlainDate: function PlainDate()
  PlainDateTime: function PlainDateTime()
  PlainMonthDay: function PlainMonthDay()
  PlainTime: function PlainTime()
  PlainYearMonth: function PlainYearMonth()
  ZonedDateTime: function ZonedDateTime()
  Symbol(Symbol.toStringTag): "Temporal"
*/

Temporal 包含的类和命名空间对象允许你计算两个时间点之间的持续时间、表示一个时间点(无论是否具有时区信息)、通过 Now 属性访问当前时间点等。

如果我们要获取当前时间:

console.log(Temporal.Now.plainDateISO());
/* Result (expanded):
Temporal.PlainDate 2025-12-31
  <prototype>: Object { … }
*/

该方法返回的是当前时区的今天日期。

Temporal 还能支持时区:

const date = Temporal.Now.plainDateISO();

// 指定这个日期在伦敦时区
console.log(date.toZonedDateTime("Europe/London"));

Temporal 还可以计算日期差:

const today = Temporal.Now.plainDateISO();
const jsShipped = Temporal.PlainDate.from("1995-12-04"); // JavaScript 发布日期
const difference = today.since(jsShipped, { largestUnit: "year" });

console.log(`JavaScript 已经存在了 ${difference.years}${difference.months} 个月零 ${difference.days} 天。`);

各种时间操作也会更加直观:

const today = Temporal.Now.plainDateISO();

// 加一天
console.log(today.add({ days: 1 }));

// 加一个月零一天,再减两年——可以链式操作
console.log(today.add({ months: 1, days: 1 }).subtract({ years: 2 }));

// 看,多清楚!

当然,更重要的是,日期不会被意外修改

const today = Temporal.Now.plainDateISO();

// 计算明天的日期
const tomorrow = today.add({ days: 1 });

console.log(`今天是 ${today}。`); // 2025-12-31
console.log(`明天是 ${tomorrow}。`); // 2026-01-01

// 今天还是今天,完美!

add 方法会返回一个新的日期对象,而不是修改原来的。就像你复印了一份日历,在复印件上写字,原件不会被弄脏。

4. 什么时候能用?

好消息:最新版的 Chrome 和 Firefox 已经支持了!

坏消息:它还在“实验阶段”,这意味着具体用法可能还会微调,但大方向已定。

我们终于要和 Date 的噩梦说再见了。

我是冴羽,10 年笔耕不辍,专注前端领域,更新了 10+ 系列、300+ 篇原创技术文章,翻译过 Svelte、Solid.js、TypeScript 文档,著有小册《Next.js 开发指南》、《Svelte 开发指南》、《Astro 实战指南》。

欢迎围观我的“网页版朋友圈”,关注我的公众号:冴羽(或搜索 yayujs),每天分享前端知识、AI 干货。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions