date-fns 入门指南:在浏览器与 Node.js 中优雅地操作 JavaScript 日期
【免费下载链接】date-fns⏳ Modern JavaScript date utility library ⌛️项目地址: https://gitcode.com/gh_mirrors/da/date-fns
date-fns 是一个现代、模块化的 JavaScript 日期工具库,为在浏览器与 Node.js 中处理日期提供全面、简洁且一致的工具集。本文基于 date-fns 仓库(当前核心包版本为 v4.4.0)的官方文档与源码,讲解它的核心设计理念、安装方式、子模块体系以及最常用的函数用法,读完即可在项目中快速上手。仓库核心文档与源码可分别参考 pkgs/core/docs/gettingStarted.md 与 pkgs/core/src。
为什么选择 date-fns
date-fns 的定位是“日期领域的 Lodash”,它在不引入复杂抽象的前提下,把日常开发中几乎所有与日期相关的操作都封装成了简单、一致的函数。从仓库根目录 README.md 的定位描述看,它的核心卖点集中在以下几点:
- 200+ 函数覆盖各种场景:从加减日期、比较日期、格式化输出,到区间遍历、周/年历计算、时区处理,几乎覆盖所有日常需求。
- 模块化设计:只引入你需要的那部分代码,天然兼容 webpack、Browserify、Rollup 等打包工具,并且支持 tree-shaking,避免把整个库塞进最终产物。
- 原生 Date 类型:直接基于 JavaScript 现有的原生
Date类型工作,出于安全考虑不去扩展核心对象(不污染原型链)。 - 不可变与纯函数:所有函数都是纯函数,总是返回一个新的 Date 实例,绝不修改传入的参数,便于预测行为与测试。
- 100% TypeScript:整个库由 TypeScript 编写,并附带全新手工打磨的类型定义,开箱即用的类型安全。
- I18n 国际化:内置数十种语言 locale,按需引入,只打包你需要的语言。
需要特别说明的是:这里的“200+ 函数”是官方文档的表述,实际数量以发布版本为准;从 pkgs/core/package.json 的exports字段可以看到,当前仓库为每个函数都提供了独立的子路径导出。
安装与快速上手
date-fns 以 npm 包形式发布。官方文档给出的安装命令如下:
npm install date-fns --save # 或 yarn add date-fns以 npm 安装为例,仓库中pkgs/core就是实际的发布包(包名为date-fns,见 pkgs/core/package.json)。安装完成后即可按需导入:
import { compareAsc, format } from "date-fns"; format(new Date(2014, 1, 11), "yyyy-MM-dd"); //=> '2014-02-11' const dates = [ new Date(1995, 6, 2), new Date(1987, 1, 11), new Date(1989, 6, 10), ]; dates.sort(compareAsc); //=> [ // Wed Feb 11 1987 00:00:00, // Mon Jul 10 1989 00:00:00, // Sun Jul 02 1995 00:00:00 // ]上面的示例同时演示了两个最常用的函数:
format(date, formatString):按指定的格式字符串输出日期;compareAsc(dateLeft, dateRight):比较两个日期,可作为Array.prototype.sort的比较器直接传入,实现日期数组的升序排序。
从 pkgs/core/src/compareAsc/index.ts 的实现可以看到compareAsc的内部逻辑非常直观:它先把两个入参统一转换成时间戳做差,差值为负返回-1、为正返回1、相等返回0(若差值为NaN则原样返回NaN)。该行为在 pkgs/core/src/compareAsc/test.ts 中有完整的测试覆盖,包括相等、早于、晚于以及数组排序四种典型场景。
另一个文档中的经典示例是相对时间格式化:
import { formatDistance, subDays } from "date-fns"; formatDistance(subDays(new Date(), 3), new Date(), { addSuffix: true }); //=> "3 days ago"这里subDays从当前时间减去 3 天,formatDistance计算两个日期之间的友好距离描述,addSuffix: true会在结果后追加“ago”等前后缀。值得一提的是,这类描述会根据所选 locale 输出不同语言(详见下文“国际化”一节)。
关于输入参数的类型
date-fns 的绝大多数函数都接受“可被转换为 Date 的值”(DateArg)作为输入:既可以是 Date 实例,也可以是时间戳数字。所有传入的参数都会先经过内部统一的toDate转换。从 pkgs/core/src/toDate/index.ts 的实现可以看到,toDate通过constructFrom完成转换:Date 实例会被克隆,数字被当作时间戳处理,其余类型得到 Invalid Date。这也从源码层面印证了“不可变、纯函数”的设计——输入参数永远不会被修改。
子模块:FP 函数式编程变体
date-fns 的 npm 包内还附带了一些可选功能子模块。根据 pkgs/core/docs/gettingStarted.md 的说明,目前主要包括:
- FP—— 面向函数式编程的函数变体,参见 pkgs/core/src/fp 目录。
子模块是嵌套包含的:如果你想同时使用多个子模块功能,后列的子模块已被包含在前者之中。使用子模块前同样需要先安装 npm 包,然后从对应子路径导入:
// 主模块:标准用法 import { addDays } from "date-fns"; // FP 变体:柯里化、参数顺序更贴合函数式组合 import { addDays, format } from "date-fns/fp";从 pkgs/core/package.json 的exports可以看出,FP 子模块为几乎每个函数都提供了两种变体:标准版(如./fp/addDays)和带WithOptions后缀、可传入 options 的版本(如./fp/addDaysWithOptions)。相应地,pkgs/core/src/fp/index.ts 是这些导出的汇总入口。
核心 API 速览:format 与 unicode 令牌
format是 date-fns 使用频率最高的函数之一,它的格式字符串基于Unicode Technical Standard #35(CLDR 日期字段符号表),而不是 Moment.js 的自定义规则。所有支持的令牌在 pkgs/core/src/format/index.ts 的文档注释中有完整表格,以下列出最常用的一部分:
| 单元 | 令牌 | 结果示例 | 说明 |
|---|---|---|---|
| 日历年份 | y/yy/yyyy | 44/44/0044 | 四位年份建议用yyyy |
| 月份 | M/MM/MMM/MMMM | 1/01/Jan/January | MMMM为完整月份名 |
| 日(月内) | d/dd | 1/01 | 与D(年内第几天)区分 |
| 日(年内) | D/DD/DDD | 1/01/001 | 需useAdditionalDayOfYearTokens选项 |
| 星期几 | E..EEE/EEEE/EEEEE | Mon/Monday/M | |
| 小时 [0-23] | H/HH | 0/00 | 24 小时制 |
| 小时 [1-12] | h/hh | 1/01 | 12 小时制,常与a连用 |
| AM/PM | a..aa/aaa/aaaa | AM/am/a.m. | |
| 分钟 | m/mm | 0/00 | |
| 秒 | s/ss | 0/00 |
注意format的令牌与 Moment.js 等库不同,例如:
D/DD表示“年内第几天”(1, 2, ..., 365, 366),常被误当作“月内第几天”;月内第几天应使用d/dd(1, 2, ..., 31)。YY/YYYY表示“本地周编号年份”(44, 01, 00, 17),常被误当作日历年份;日历年份应使用yy/yyyy。
为防止这种混淆,format与parse默认会拒绝D、DD、YY、YYYY四个令牌,除非显式传入对应选项(详见 pkgs/core/docs/unicodeTokens.md):
// ❌ 错误:把 YYYY/DD 当成年月日用 format(new Date(), "YYYY-MM-DD"); // ✅ 正确 format(new Date(), "yyyy-MM-dd"); //=> '2018-10-10' // ✅ 显式启用额外令牌 format(new Date(), "D", { useAdditionalDayOfYearTokens: true }); //=> '283' parse("365+1987", "DD+YYYY", new Date(), { useAdditionalDayOfYearTokens: true, useAdditionalWeekYearTokens: true, }); //=> 'Wed Dec 31 1986 ...'此外,格式字符串中被单引号'...'包裹的字符会被当作字面量原样输出,两个连续单引号''表示一个真正的单引号字符。
format 的底层实现
从 pkgs/core/src/format/index.ts 可以看到,格式化字符串首先被拆解为两部分正则:formattingTokensRegExp匹配yYQqMLwIdDecihHKkms等字母加o构成的序数令牌、连续的相同字母序列以及引号转义段;longFormattingTokensRegExp则负责P/p开头的长格式令牌(如PPPP)。随后每个令牌交由_lib/format/formatters下的对应格式化器逐个渲染。对format的内部机制感兴趣的读者,可以继续阅读 pkgs/core/src/_lib/format 目录。
国际化(I18n)与按需引入 locale
date-fns 内置数十种语言 locale。从 pkgs/core/package.json 的exports可以看到按语言代码划分的独立子路径导出(如./locale/zh-CN、./locale/en-US、./locale/ja等),每个 locale 均位于 pkgs/core/src/locale 目录。以简体中文为例:
import { format, formatDistance } from "date-fns"; import { zhCN } from "date-fns/locale"; format(new Date(2024, 0, 1), "EEEE MMMM d, yyyy", { locale: zhCN }); // 输出中文星期与月份 formatDistance(subDays(new Date(), 3), new Date(), { locale: zhCN, addSuffix: true });每个 locale 都是一个符合统一Locale接口的对象。从 pkgs/core/src/locale/zh-CN/index.ts 的实现可以看到,zhCN包含code、formatDistance、formatLong、formatRelative、localize、match六个成员,并通过options.weekStartsOn: 1(周一为一周起点)与options.firstWeekContainsDate: 4声明该语言环境的周历规则。这些选项会直接影响依赖周历计算的函数(如startOfWeek、getWeek、format中的w/E等令牌)的结果。locale 的完整结构定义见 pkgs/core/src/locale/types.ts(位于 pkgs/core/src/locale 目录内)。
由于每个 locale 都是独立子路径导出,打包器可以轻松做 tree-shaking,最终产物只包含实际用到的语言,这也是“Include only what you need”理念的体现。
进阶阅读:更多函数与官方文档
本文只覆盖了入门必需的部分。date-fns 的函数体系非常庞大,按用途大致可分为几类:
- 运算类:
addDays、subMonths、addBusinessDays等加减操作; - 比较类:
compareAsc、isAfter、isEqual、isSameDay等; - 区间类:
eachDayOfInterval、areIntervalsOverlapping、intervalToDuration等; - 取值/设值类:
getYear、setMonth、startOfWeek、endOfDay等; - 解析类:
parse、parseISO、fromUnixTime等; - 单位换算类:
hoursToMinutes、daysToWeeks、yearsToMonths等。
所有这些函数都遵循同样的设计哲学:纯函数、不修改入参、类型安全、按需导入。仓库内每个函数目录都包含index.ts(实现)与test.ts(vitest 测试),例如 pkgs/core/src/compareAsc 目录,方便开发者阅读实现细节与行为契约。
结语
date-fns 用一套简单、一致、类型安全的函数,把 JavaScript 日期操作的复杂度封装了起来:安装只需一条 npm 命令,使用遵循“按需导入 + 原生 Date + 纯函数”的现代实践,国际化与 FP 变体则让它能适配更多工程场景。希望本文能帮你快速建立起对 date-fns 的整体认知,并在自己的项目中得心应手地使用它。
【免费下载链接】date-fns⏳ Modern JavaScript date utility library ⌛️项目地址: https://gitcode.com/gh_mirrors/da/date-fns
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考