你可能不需要 date-fns

2025-06-04

你可能不需要 date-fns

你好!我是懒惰的开发者,今天我们来讨论一下date-fns。人们经常在真正需要日期库之前就选择它。“我们该如何格式化日期?”,“还有其他选择吗?”

但真的有吗?

我是一个仇恨者吗?

抱歉,这个问题是必填的。不,我不需要。而且,我曾经是 date-fns 的超级活跃用户和推广者。我是date-io@material-ui/pickers的创建者,他们一直建议大家选择 date-fns,而不是其他日期库。

但是有一天我说 date-fns 不是万能的
在这篇博文中提到的 Twitter帖子之后,date-fns 的维护者就把我屏蔽了,理由是说你可能不需要它。所以,这篇帖子可能对那些选择日期库的人来说有一些有用的信息——所以我决定也把它分享到博客里!希望你读起来会开心 :)

这就是先决条件,让我们开始讨论

你可能根本不需要日期库

首先,当您只需要以用户可读的格式显示日期值时,您可以完全避免使用日期库。如今,所有现代浏览器(甚至 IE11)和 node.js 都完美支持 Intl.DateTimeFormat

我可以使用 Intl.DateTimeFormat 吗

这意味着如果您的任务只是以用户可读的格式显示日期/时间值,您可以执行以下操作:



const date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0))
// Ouput will depend on user locale and timezone 
console.log(new Intl.DateTimeFormat().format(date));


Enter fullscreen mode Exit fullscreen mode

它完美支持原生 IANA时区和语言环境格式。这意味着您完全可以不在 bundle 中包含任何语言环境。



const date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));
// Results below assume UTC timezone - your results may vary

console.log(new Intl.DateTimeFormat('en-US').format(date));
// expected output: "12/20/2012"

console.log(new Intl.DateTimeFormat('fr').format(date));
// expected output: "20/12/2012"


Enter fullscreen mode Exit fullscreen mode

如果你需要更多

但有一个问题。当你需要的不仅仅是格式化——例如,解析,或者你经常使用日期,以至于原生(并非最好的)DateAPI 不够用时,你可能会开始寻找一些有用的日期管理库。

🌈 或许有一天,我们能够摆脱所有库,转而使用原生 API。以下是 的提案Temporal,它提供了一个良好的函数式 API,可用于处理Date

但今天你可能会选择 date-fns。这没什么特别的,只是统计数据。Date-fns 是目前最流行的日期库。moment.js 呢?它已经死了

如今,date-fns 在新项目中的使用频率更高了。以下是date-io 的下载量统计。

date-io 统计数据

从统计学上来说,你会选择 date-fns。但你真的需要它吗?

让我们讨论一下选择日期库的一些常用标准,看看 date-fns 是否是最好的?

捆绑包大小

Date-fns 只解决了一个问题,比其他任何日期库都好得多。而且这跟包大小无关。🎉 惊喜 🎉 date-fns 在没有语言环境的情况下 gzip 压缩后占用了 18kb 的空间。Dayjs占用了6kb(没错,就是 6kb)。

替代文本

但如果比较不是 gzip 压缩的,而是解析后的大小 – date-fns 是最大的

替代文本

好的。好的。date-fns 真正擅长解决的问题是 tree-shaking。因为每个函数都有自己的入口点,并且导出为 esm,所以未使用的代码会从 bundle 中移除,对吗?

摇树

让我们创建一个更“真实世界”的示例,使用最难手动实现的功能:

  • 格式化
  • 解析
  • 显示从X到Y的时间
  • 3 个语言环境

结果:

替代文本

可以看到,仅导入最重要的功能时,date-fns 的 gzip 压缩包占用了 13.88kb 的空间。这可是个大问题。

这是一个非常有趣的例子,其中 React Datepicker 与 date-fns 存在同级依赖。Date-fns 占用的空间是 Datepicker 本身的 3 倍,几乎占 React 大小的 1/3。而且这只是为了让单个 Datepicker 正常工作。

react-nice 日期

另外,正如您在bundlesize 统计信息中看到的,luxon 的大小根本没有变化。这是因为 luxon npm 包只提供了 commonjs 的输出,而这些输出无法进行树优化。所以也许有一天它会变得更小。

但别忘了 Luxon 最棒的地方——它基于原生构建Intl,因此完全不打包语言环境。您甚至可以支持 50 种语言环境,而无需为日期格式增加额外的打包大小!

PS 所有 date-fns 的 75 种语言环境包占用 80kb gzip
替代文本

Bundlesize 结论

Date-fns并非轻量级的日期/时间管理库。市面上有一些被低估的替代方案——例如,Dayjs在功能大致相同的情况下体积要小得多。

API

选择库的下一个标准是 API。API 必须清晰、类型良好且功能全面。我个人最不明白的是——为什么大家都选择 date-fns?

Date-fns 的设计非常简单——所有操作都用一个单独的函数来实现。这非常完美,但遗憾的是,并非所有 JavaScript 开发者都能接受。问题在于 JavaScript 没有原生的函数组合工具。

我的意思是一些带有 date-fns 的复杂代码是完全不可读的:



function checkIsBeforeDateFns(time: Date, maxTime: Date) {
  return isBefore(
    setMilliseconds(setSeconds(setMinutes(time, 0), 0), 0),
    maxTime
  );
}


Enter fullscreen mode Exit fullscreen mode

函数执行需要从内到外阅读。第一个函数调用是setMinutes,最后一个函数调用是isBefore

让我们比较一下 dayjs 和 luxon 中的相同函数。它们使用的是经典的、性能良好的链式 API。大多数情况下,使用developers此类API都能完美运行。editorslintersstatic analyzers



function checkIsBeforeDayjs(time: Dayjs, maxTime: Dayjs) {
  return time.minute(0).second(0).millisecond(0).isBefore(maxTime);
}

function checkIsBeforeLuxon(time: DateTime, maxTime: DateTime) {
  return time.set({ second: 0, minute: 0, millisecond: 0 }) < maxTime;
}


Enter fullscreen mode Exit fullscreen mode

可读性很强吧?这其实是函数式编程中一个普遍的问题。不过,使用一些函数组合技巧可以轻松解决这个问题。例如,下面是使用date-fns/fp子模块和ReasonML(现在的 Rescript)——原生函数式语言编译成 JavaScript 的相同函数。这真是太棒了💜



let checkIsBeforeDateFns = (time, maxTime) =>
  time
  |> DateFns.setMilliseconds(0)
  |> DateFns.setSeconds(0.)
  |> DateFns.setMinutes(0.)
  |> DateFns.isBefore(maxTime);


Enter fullscreen mode Exit fullscreen mode

这仍然只是 4 个函数调用。但可读性大大提升。太棒了!

顺便说一下,我是ReasonML 的date-fns 绑定的维护者。给我们一个⭐️

但是问问你自己——你,更重要的是你的团队,是否已经为函数式编程做好了准备?你是否拥有所有必要的工具,比如pipe……compose

如果是的话——使用 date-fns 并对函数式编程感到满意👨‍💻👩‍💻。

表现

在问题得到解决之前,你不应该考虑性能。

过早优化是万恶之源 © Donald Knuth

性能差异仅在每秒数千次函数调用时可见。但如果您仍然对 date-fns 与其他库之间的性能差异感兴趣:

我们的date-io 基准测试的简短结果

  • Date-fns 是日期计算最快的(加、减、之前等)
  • Date-fns 是最快的日期解析器
  • Moment 是格式化速度最快的(哈哈,没想到这里也有 Moment)

是的,date-fns 非常快,因为它可以直接处理原生日期,无需创建任何额外的包装器。Dayjs 更注重大小而不是速度,而 Luxon 使用的Intl日期格式非常慢🐌。

所以,如果你在其他库中遇到性能问题,那么 date-fns 确实是最佳选择。但你真的遇到过这样的问题吗?

结论

请确保这篇文章的作者不称职、主观、愚蠢、糟糕、懒惰。所以你必须根据多种因素,针对你的具体项目和团队得出自己的结论。

顺便说一下,这里是包含这篇文章中所有 date-fns 比较内容的 repo,你可以查看它,使用 bundlesize 和 API 进行操作。

如果你读完这篇文章后能思考一下 JavaScript 中的日期/时间库以及 date-fns 的要求,我会非常高兴🤓

以作者的拙见,如果你不擅长函数式编程,那就没必要选择 date-fns 了。而且,不幸的是,据我所知,几乎没有人使用他们真正优秀的函数式编程方法 😿。

因此,总而言之:如果这个懒惰的作者有一天会用 JavaScript 开始一个新项目,并且需要某种日期/时间操作,他可能会执行以下操作:

  • 尝试从本地 Intl 格式开始
  • 当真正需要 lib 时,请选择 dayjs – 因为它
    • a)〜更努力,更好,更快,更强〜
    • a) 较小
    • b) 可通过插件进行摇树
    • c) 拥有良好的 API

谢谢

对于这篇长文和传统:

没有任何 date-fns 维护者在撰写本文时受到伤害😉

文章来源:https://dev.to/dmtrkovalenko/you-might-not-need-date-fns-23f7
PREV
一些有用的 React Hooks 的小合集。后记。
NEXT
保护你的 Node.js 应用免受跨站请求伪造