FEATURED · 精选文章

Day.js 实战指南:2kB 不可变日期库的核心 API、国际化与插件体系

发布时间 / 2026/9/18 11:58:54
来源 / 创域科博编辑部
栏目 / 资讯中心
Day.js 实战指南:2kB 不可变日期库的核心 API、国际化与插件体系 Day.js 实战指南2kB 不可变日期库的核心 API、国际化与插件体系【免费下载链接】dayjs⏰ Day.js 2kB immutable date-time library alternative to Moment.js with the same modern API项目地址: https://gitcode.com/gh_mirrors/da/dayjs本文以 Day.js 仓库的 README.md 为主体系统讲解这个约 2kB、不可变、与 Moment.js 用法高度对齐的日期时间库从安装接入、解析/格式化/增删/查询等核心 API到按需加载的国际化I18n体系与插件机制。读完本文你将能够独立完成 Day.js 的引入理解其不可变性与链式调用的底层实现并能按需组合 locale 与插件来扩展日期处理能力。一、Day.js 定位Moment.js 的轻量化替代README 对项目的一句话定位是Day.js is a minimalist JavaScript library that parses, validates, manipulates, and displays dates and times for modern browsers with a largely Moment.js-compatible API. If you use Moment.js, you already know how to use Day.js.它围绕六个核心特性组织与 Moment.js 一致的 API 和模式、不可变Immutable、可链式调用Chainable、国际化支持I18n、2kB 微型体积、全浏览器支持。其中“2kB 体积”并不是宣传口号而是被写进了构建流程的硬性约束。package.json 中配置了size-limit检查产物dayjs.min.js的 gzip 体积上限被设定为2.99 KBnpm run build会执行size-limit gzip-size dayjs.min.js见 package.json。这意味着任何核心功能的膨胀都会在构建阶段被拦截。仓库内同时提供简体中文、日语、葡萄牙语、韩语、西班牙语、俄语、土耳其语等多语言 README例如 docs/zh-cn/README.zh-CN.md内容与主 README 保持一致。需要说明的是仓库docs/目录下更细粒度的 API、安装、I18n、插件文档如 docs/en/API-reference.md目前仅保留了“文档已迁移至官方站点”的跳转说明完整的 API 细节需结合官方文档与本文的源码分析来掌握。二、安装与快速上手2.1 安装npm install dayjs --save安装后即可通过import dayjs from dayjs引入。README 开篇给出的最小可运行示例即展示了 Day.js 的典型调用风格——一段完整的链式表达式dayjs().startOf(month).add(1, day).set(year, 2018).format(YYYY-MM-DD HH:mm:ss);这条链的含义是取当前时刻 → 截断到本月第一天 → 加一天即次月 1 日→ 把年份替换为 2018 → 按YYYY-MM-DD HH:mm:ss格式输出。仓库的 docs/demo/index.js 提供了更多基础用法如add、subtract、diff、isBefore、startOf等可直接作为示例集参考。2.2 入口实现dayjs 函数与 Dayjs 类从源码结构看整个库的入口非常精炼核心只有三块src/index.js约 470 行的核心类、src/constant.js常量与正则和 src/utils.js填充、单位归一化等工具函数。src/index.js 中的dayjs工厂函数负责实例创建并顺带实现了跨实例安全克隆如果传入的已经是 Day.js 对象直接返回其clone()避免重复解析const dayjs function (date, c) { if (isDayjs(date)) { return date.clone() } const cfg typeof c object ? c : {} cfg.date date cfg.args arguments return new Dayjs(cfg) }其中isDayjs通过instanceof Dayjs或标记字段$isDayjsObject双重判断见 src/index.js这样即使对象来自不同打包版本也能被识别。test/constructor.test.js 中对instanceof和$isDayjsObject的测试印证了这一设计。Dayjs类的构造流程见 src/index.js先解析 locale再调用parse(cfg)完成日期解析随后init()把原生Date的 year/month/date/weekday/hour/minute/second/millisecond 拆分缓存到$y/$M/$D/$W/$H/$m/$s/$ms字段上后续所有 getter 都直接读这些缓存避免反复调用原生方法。三、核心 API解析、展示、取值/赋值、增减、查询README 将 API 分为 parse / display / get set / manipulate / query 五类下面逐类展开并对照源码说明行为细节。3.1 解析parsedayjs(2018-08-08) // parse解析逻辑集中在 src/index.js 的parseDate中其规则依次是null直接判为无效日期new Date(NaN)空参数undefined返回今天Date实例按原生日期复制字符串若匹配预置正则则按结构化字段构造不再经过浏览器字符串解析保证行为跨环境一致其余情况回退到new Date(date)。关键正则定义在 src/constant.jsREGEX_PARSE /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d)?$/从正则结构看2018-08-08、2018/8/8、2018-08-08T08:00:00、2018-08-08 08:00:00.123等形态都能被结构化解析分隔符-、/和可选的时间部分都由字符类放宽处理。而带Z结尾的字符串会跳过正则分支!/Z$/i.test(date)条件见 src/index.js直接交给原生Date解析 ISO 格式——时区相关的精细处理则留给utc插件src/plugin/utc/index.js。3.2 展示/格式化displaydayjs().format({YYYY} MM-DDTHH:mm:ss SSS [Z] A) // display不传参数时format()使用默认格式YYYY-MM-DDTHH:mm:ssZISO 风格见 src/constant.js。format的实现见 src/index.js它先用正则REGEX_FORMAT扫描格式串中的每个 tokenYYYY、MM、HH、SSS、Z、A等逐个替换为对应字段值[...]方括号内的文本被原样输出正则中\[([^\]])]分支src/constant.js。这就是 README 示例中[Z]能输出字面量 Z 的原因两位补零由padStart工具完成src/utils.js如MM、HHA/aAM/PM默认输出英文若当前 locale 定义了meridiem函数则优先使用 locale 的版本见 src/index.jsZ输出时区偏移如08:00由 src/utils.js 的padZoneStr基于utcOffset()计算无效实例isValid()为 false会直接返回 locale 的invalidDate或Invalid Datesrc/index.js。格式串解析所用的完整正则src/constant.jsREGEX_FORMAT /\[([^\]])]|YYYY|YY|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g它覆盖了年、月、日、星期、小时24 小时制H与 12 小时制h、上下午、分、秒、时区与毫秒等基础 token更丰富的 token如季度Q、ISO 周WW、时间戳X/x则由插件扩展见第五节。3.3 取值与赋值get setdayjs().set(month, 3).month() // get setget/set是一对对称操作不传参数时是 getter传参数时是 setter。README 示例dayjs().set(month, 3).month()的返回值是从 0 开始的月份序号即3表示四月这一点与源码一致——month()最终返回内部缓存$M而非本地化的“四月”字样。源码上有两个值得注意的实现细节不可变赋值公开方法set(string, int)的第一件事是this.clone()src/index.js所有修改只发生在克隆体上原实例保持不变月末钳制修改年或月时实现会先把日期设为当月 1 号再改年月最后把日期钳制到目标月/年的实际天数内Math.min(this.$D, date.daysInMonth())见 src/index.js。因此dayjs(2020-01-31).set(month, 1)会得到 2 月 29 日2020 闰年而非 3 月 2 日行为与 Moment.js 对齐。getter 的批量生成见 src/index.jsmillisecond/second/minute/hour/date/month/year八个 getter 统一由$g(input, get, set)模式生成——有参走 set无参走 get与 README“get set 一个 API 两用”的描述完全一致。3.4 时间增减manipulatedayjs().add(1, year) // manipulateadd(number, units)见 src/index.js内部按单位分三条路径单位实现策略原因月month、年year走setthis.set(C.M, this.$M number)复用月末钳制逻辑2 月 31 日 1 月得到 3 月 31 日而不是溢出到下下月日day、周week在“日期序号”上整数相加instanceFactorySet用date n的整日步长避免跨夏令时时按毫秒加 24 小时可能少/多算 1 小时时/分/秒/毫秒换算成毫秒后加时间戳时间单位换算精确毫秒常量见 src/constant.jssubtract(number, units)只是add(number * -1, units)的薄封装src/index.js。单位字符串支持year/month/week/day/hour/minute/second等完整名称归一化逻辑在 src/utils.js 的prettyUnit先查别名表y→year、M→month、w→week、h→hour、m→minute、s→second、Q→quarter未命中则转小写并去掉尾部s所以years和year等价。3.5 比较查询querydayjs().isBefore(dayjs()) // queryisBefore/isAfter/isSame的实现见 src/index.js其设计很巧妙不比较时间戳本身而是用startOf/endOf构造区间再比较isSame(that, units) { const other dayjs(that); return this.startOf(units) other other this.endOf(units) } isAfter(that, units) { return dayjs(that) this.startOf(units) } isBefore(that, units) { return this.endOf(units) dayjs(that) }这意味着dayjs().isSame(other, day)判断的是“同一自然日”isBefore(other, hour)判断的是“更早的整点区间之前”与units参数精确对应。不带units时则退化为毫秒级全量比较。区间端点的计算在startOfsrc/index.js中按年/月/周/日/时/分/秒分别取极值其中“周”的起点会读取 locale 的weekStart字段默认 0即周日中文 locale 定义为 1即周一这也是 I18n 影响核心 API 行为的一个实例。3.6 不可变与链式调用README 把 Immutable 与 Chainable 列为两大卖点源码上体现为所有产生新时间的公开方法都返回新实例set、add、subtract、locale等内部均先clone()链式调用的安全性由这一约定保证。clone()本身通过保留 locale、UTC 标记等上下文的wrapper重建实例src/index.js、src/index.js保证克隆体与原实例“同源”。四、国际化 I18n按需加载与双层作用域README 对 I18n 的表述有两层含义功能强大以及默认不打包——“none of them will be included in your build unless you use them”。import dayjs/locale/es // load on demand dayjs.locale(es) // use Spanish locale globally dayjs(2018-05-05).locale(zh-cn).format() // use Chinese Simplified locale in a specific instance4.1 按需加载的机制locale 模块不是被核心引用而是被使用方显式 import。以 src/locale/zh-cn.js 为例文件末尾调用dayjs.locale(locale, null, true)完成自注册——副作用是把它挂进全局注册表之后才能通过名字加载。核心侧的注册表与解析逻辑见 src/index.js 和 src/index.jslet L en // global locale const Ls {} // global loaded locale Ls[L] enparseLocale挂到dayjs.locale上的行为规则传undefined时返回/保持当前全局 locale传字符串时按小写名字查注册表找不到再尝试把zh-cn这类带连字符的名字回退到主语言zhsrc/index.js传 locale 对象时以对象的name字段注册第三个参数为true实例级调用时只解析、不修改全局L否则调用即切换全局 locale。仓库 src/locale/ 目录内置了 150 语言包en、zh-cn、zh-tw、ja、ko、es、fr、de等每个文件结构一致。以 src/locale/zh-cn.js 为例一个 locale 对象包含以下字段字段说明zh-cn 实例weekdays/weekdaysShort/weekdaysMindddd/ddd/dd输出的星期全称/简称/缩写星期日… / 周日… / 日、一、二…months/monthsShortMMMM/MMM输出的月份一月…十二月 / 1月…12月ordinal(number, period)Do、wo等序数格式化的函数W时输出1周默认输出1日weekStart一周的第一天影响startOf(week)1周一yearStart周历中年份切换的参考周4formatsLT/L/LL等快捷格式配合localizedFormat插件LL: YYYY年M月D日relativeTime相对时间文案配合relativeTime插件past: %s前meridiem(hour, minute)A/atoken 的本地化输出凌晨/早上/上午/中午/下午/晚上4.2 全局 locale 与实例 localeREADME 示例演示了两种作用域dayjs.locale(es)修改全局变量L此后所有新建实例默认使用西班牙语dayjs(2018-05-05).locale(zh-cn)走实例方法src/index.js同样返回一个克隆体只有该实例使用简体中文原实例与全局不受影响——与不可变原则一脉相承。locale 对输出行为的影响是立竿见影的中文 locale 定义了meridiemdayjs(2018-05-05 07:30).locale(zh-cn).format(Ah:mm)会输出早上07:30未定义meridiem的 locale 则回退到内置的AM/PMsrc/index.js。同理weekStart会改变startOf(week)的结果、weekOfYear插件的周号计算。这些“locale 影响核心行为”的点在 test/locale/zh-cn.test.js 等语言测试中有覆盖。五、插件体系按需扩展的官方能力README 对插件的定义A plugin is an independent module that can be added to Day.js to extend functionality or add new features.import advancedFormat from dayjs/plugin/advancedFormat // load on demand dayjs.extend(advancedFormat) // use plugin dayjs().format(Q Do k kk X x) // more available formats5.1 dayjs.extend 的安装机制extend的实现只有寥寥数行src/index.js但设计关键dayjs.extend (plugin, option) { if (!plugin.$i) { // install plugin only once plugin(option, Dayjs, dayjs) plugin.$i true } return dayjs }插件签名是plugin(option, DayjsClass, dayjsFn)拿到可配置项、Dayjs类用于修改原型和dayjs函数用于构造实例能力相当完整$i标记实现幂等安装重复extend同一插件只会执行一次多模块各自extend也不会冲突extend返回dayjs本身支持dayjs.extend(a).extend(b)的链式注册风格。5.2 以 advancedFormat 为例看插件如何改写核心行为README 示例中的Q Do k kk X x正是 src/plugin/advancedFormat/index.js 提供的能力。从源码看插件采用的典型手法是原型方法装饰保存旧的proto.format替换为“先扩展新 token、再委托旧实现”的包装器src/plugin/advancedFormat/index.jsconst oldFormat proto.format proto.format function (formatStr) { // 先把 Q / Do / gggg / WW / k / X / x / z 等新 token 替换成具体值 const result str.replace(/\[([^\]])]|Q|wo|ww|w|WW|W|zzz|z|gggg|GGGG|Do|X|x|k{1,2}|S/g, (match) { ... }) return oldFormat.bind(this)(result) // 剩余的基础 token 交给核心 format 处理 }它新增的 token 与实现对应关系见 src/plugin/advancedFormat/index.jstoken输出实现要点Q季度1–4Math.ceil(($M 1) / 3)Do本地化序数日调用 locale 的ordinal($D)中文下输出如5日gggg/GGGG周年 / ISO 周年委托weekYear()/isoWeekYear()依赖 weekOfYear / isoWeek 插件w/ww/wo周年 / 补零周年 / 序数周依赖 weekOfYear 插件W/WWISO 周号依赖 isoWeek 插件k/kk24 小时制小时0 点显示为 24$H 0 ? 24 : $HX/x秒级 / 毫秒级时间戳Math.floor(getTime() / 1000)/getTime()z/zzz时区缩写 / 全称依赖 timezone 插件的offsetName()注意其中weekYear/isoWeek/offsetName等方法是其他插件注入的——这体现了插件生态的协作关系advancedFormat 只负责 token 解析与委托具体能力由对应插件提供。test/plugin/advancedFormat.test.js 验证了Q Do k kk X x等输出结果。5.3 仓库内置插件全景src/plugin/ 目录内置了 35 个官方插件每个插件一个目录、一个index.js并配有同名 TypeScript 声明与测试文件types/plugin/ 与test/plugin/可按需挑选类别插件解析/构造customParseFormat、objectSupport、arraySupport、preParsePostFormat格式化advancedFormat、localizedFormatLT/LL 等快捷格式、buddhistEra查询/判断isBetween、isSameOrAfter、isSameOrBefore、isToday、isTomorrow、isYesterday、isLeapYear、isMoment周/季度weekOfYear、weekYear、weekday、isoWeek、isoWeeksInYear、quarterOfYear、dayOfYear时区/UTCutc、timezone时间差/相对时间duration、relativeTime、calendar集合/边界minMax、localeData、toArray、toObject兼容性/辅助pluralGetSet、negativeYear、bigIntSupport、badMutable、devHelper、updateLocale使用方式与 README 示例完全一致import xxx from dayjs/plugin/xxx后dayjs.extend(xxx)部分插件支持在extend时传入第二个 option 参数如 relativeTime 的thresholds。六、工程化保障测试与体积约束README 徽章区的“Build Status”与“Codecov”背后是仓库 package.json 中定义的测试流水线可作为选型参考100% 行覆盖率门槛jest --coverage --coverageThreshold{ \global\: { \lines\: 100} }未达标则测试任务失败package.json多时区回归test脚本在Pacific/Auckland、Europe/London、America/Whitehorse三个TZ环境各跑一轮 test/timezone.test.js专门覆盖夏令时与偏移计算类场景package.json体积门禁size-limit将 gzip 产物限制在 2.99 KBpackage.json。此外仓库自带完整 TypeScript 类型定义入口 types/index.d.ts、locale 类型 types/locale/、各插件声明 types/plugin/import dayjs from dayjs在 TS 工程下开箱即有类型提示。七、小结回到 README.md 的主线Day.js 用约 2kB 的体积提供了“会 Moment 就会 Day.js”的完整能力面——解析与格式化结构化正则解析REGEX_PARSE 默认 ISO 格式输出token 体系完整且支持字面量转义不可变 链式set/add/locale均返回新实例startOf/endOf/diff/isBefore等构建在其上调用链安全可组合I18n 按需加载locale 自注册 全局/实例双层作用域locale 字段weekStart、meridiem、ordinal等真实参与核心行为插件化扩展dayjs.extend幂等安装35 个官方插件覆盖解析、周历、时区、相对时间等进阶场景且插件之间可以互相依赖组合。配套代码可继续深入核心实现 src/index.js、单位与正则 src/constant.js、工具函数 src/utils.js、示例 docs/demo/index.js以及test/目录下与src/plugin/一一对应的测试文件。项目遵循 MIT LicenseLICENSE。【免费下载链接】dayjs⏰ Day.js 2kB immutable date-time library alternative to Moment.js with the same modern API项目地址: https://gitcode.com/gh_mirrors/da/dayjs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻