深入对比Day.js与Moment.js在格式化当前时间时的语法用法、核心机制、打包体积与不可变性差异,提供明确的选型依据与平滑迁移建议。
Day.js与Moment.js格式化当前时间的对比与选择
时间:2026-11-13
浏览:276
文章目录
.format('YYYY-MM-DD HH:mm:ss')),但其打包体积压缩后仅约 2KB,远小于 Moment.js 的 70KB+;同时,Day.js 默认采用不可变数据模型,彻底规避了 Moment.js 因原位修改可能引发的时间污染缺陷。而在仍重度依赖大量历史插件且无需考虑打包体积的传统老项目中,继续维持 Moment.js 则是降低全量回归测试成本的稳妥做法。
一、基础用法对比:如何获取并格式化当前时间
在实际编码中,开发者最常碰到的需求是将当前时间转换成统一的字符串格式,例如日志记录、界面展示或接口传参。从基础代码实现来看,Day.js 与 Moment.js 在获取当前时间并进行格式化时的书写习惯极为贴近。
1. 依赖安装与模块引入
在现代包管理体系下,两者的安装命令与引入方式如下:
# 安装 Day.js
npm install dayjs
# 安装 Moment.js
npm install moment
在业务模块中的引入与调用写法对比如下:
// 方案 A:使用 Day.js
import dayjs from 'dayjs';
// 获取当前时间并输出标准时间格式
const dayjsNow = dayjs().format('YYYY-MM-DD HH:mm:ss');
console.log('Day.js 当前时间:', dayjsNow);
// 示例输出:"2026-11-10 20:55:00"
// 方案 B:使用 Moment.js
import moment from 'moment';
// 获取当前时间并输出标准时间格式
const momentNow = moment().format('YYYY-MM-DD HH:mm:ss');
console.log('Moment 当前时间:', momentNow);
// 示例输出:"2026-11-10 20:55:00"
当两者不传任何参数直接执行 dayjs() 或 moment() 时,底层均会以客户端当前系统的毫秒时间戳构造实例。对于常规的年、月、日、时、分、秒格式化字符串,两者的输出结果完全一致。
二、格式化标记(Tokens)与本地化处理
格式化当前时间时,标记(Tokens)的解析规则是开发者最关心的部分。两库对常用占位符定义了相同的语义规范,但在语言包(Locale)和扩展标记的处理上体现出了架构差异。
| 分类 | 占位符 Token | 说明 | 当前时间输出示例 | Day.js 依赖说明 |
|---|---|---|---|---|
| 年份 | YYYY / YY |
4位 / 2位年份 | 2026 / 26 | 原生核心内置 |
| 月份 | MM / M |
两位补零 / 不补零月份 | 11 / 11 | 原生核心内置 |
| 日期 | DD / D |
两位补零 / 不补零日期 | 10 / 10 | 原生核心内置 |
| 24小时制 | HH / H |
00-23 / 0-23 | 20 / 20 | 原生核心内置 |
| 12小时制 | hh / h |
01-12 / 1-12 | 08 / 8 | 原生核心内置 |
| 分秒 | mm / ss |
补零分秒(00-59) | 55 / 00 | 原生核心内置 |
| 毫秒 | SSS |
三位毫秒值 | 321 | 原生核心内置 |
| 星期几 | dddd / ddd |
星期全称 / 缩写 | 星期二 / 周二 | 需载入中文语言包 |
| 午前/午后 | A / a |
大写 / 小写标志 | 下午 / pm | 需载入中文语言包 |
| 季度/序数 | Q / Do |
季度值 / 英文序数 | 4 / 10th | 需加载 advancedFormat 插件 |
中文语言包的配置差异
如果需要将当前时间格式化为带有“星期几”或“上午/下午”的中文描述,例如 2026年11月10日 星期二 下午 08:55,两者的配置机制存在显著差异:
// 1. Day.js 的按需引入方式(推荐方式,严格控制体积)
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn'; // 单独载入对应语言包
dayjs.locale('zh-cn'); // 全局切换为中文环境
console.log(dayjs().format('YYYY年MM月DD日 dddd A hh:mm'));
// 示例输出:"2026年11月10日 星期二 下午 08:55"
// 2. Moment.js 的语言包处理
import moment from 'moment';
import 'moment/locale/zh-cn';
moment.locale('zh-cn');
console.log(moment().format('YYYY年MM月DD日 dddd A hh:mm'));
// 示例输出:"2026年11月10日 星期二 下午 08:55"
三、核心机制差异:对象可变性与连续格式化陷阱
在很多实际业务中,获取当前时间后,往往会配合简单的时间计算或偏移后再进行格式化(例如“展示当前时间,并预估1小时后到期时间”)。正是在这一常见链路上,Moment.js 与 Day.js 的底层设计产生了根本性分野。
1. Moment.js 的原位修改(Mutable)及其副作用
Moment.js 对象是可变的(Mutable)。调用其时间增减方法(如 .add() 或 .subtract())会直接修改当前对象本身的内部时间戳。
import moment from 'moment';
const currentTime = moment(); // 记录当前时间,假设为 20:00
console.log('格式化当前时间:', currentTime.format('HH:mm')); // "20:00"
// 业务需求:计算下一次执行时间(加1小时)
const nextTime = currentTime.add(1, 'hour');
// 致命陷阱:此时再次使用 currentTime 格式化当前时间
console.log('再次格式化当前时间:', currentTime.format('HH:mm')); // 输出:"21:00" ❌
console.log('两者是否指向同一对象:', currentTime === nextTime); // 输出:true
在这个例子中,原本用于代表“当前时间”的 currentTime 变量,在执行完 .add() 之后其自身的时间被直接篡改。如果该变量在后续逻辑或子组件中被再次用于展示当前时间,就会引发非常隐蔽的业务 Bug。为了避免此问题,开发者必须时刻牢记手动调用 .clone():
// 必须繁琐地显式克隆
const nextTime = currentTime.clone().add(1, 'hour');
2. Day.js 的不可变设计(Immutable)
Day.js 在设计之初就吸取了这一教训,采用了不可变(Immutable)架构。对 Day.js 对象执行的任何时间操作,都会生成并返回一个全新的 Day.js 实例,原有实例绝对保持不变。
import dayjs from 'dayjs';
const currentTime = dayjs(); // 记录当前时间,假设为 20:00
console.log('格式化当前时间:', currentTime.format('HH:mm')); // "20:00"
// 业务需求:计算下一次执行时间(加1小时)
const nextTime = currentTime.add(1, 'hour');
// 安全输出:currentTime 内部状态保持完全只读
console.log('再次格式化当前时间:', currentTime.format('HH:mm')); // 依然稳定输出:"20:00" ✓
console.log('nextTime 格式化:', nextTime.format('HH:mm')); // 输出:"21:00" ✓
console.log('两者是否指向同一对象:', currentTime === nextTime); // 输出:false
不可变性使得 Day.js 可以安全地在组件树、状态管理(如 Redux、Pinia)以及工具函数之间自由传递,开发者无须担心当前时间对象在某处被意外修改。
四、容易混淆的认知与辨析
在技术社区中,关于 Day.js 与 Moment.js 的替代关系常有一些流传甚广的看法,有必要进行针对性的辨析与修正:
1. 误区一:“Day.js 是 Moment.js 的 100% 完整平替,无须任何改动”
辨析与修正:这种说法在“格式化当前时间”这一常用场景下基本成立,因为基本的 .format('YYYY-MM-DD HH:mm:ss') 语法完全兼容。但是,Day.js 采用了插件化架构,并非所有 Moment.js 的高级特性都内置在 2KB 的核心包中。
例如,如果你的格式化模板使用了季度(Q)、英文序号(Do)或严格模式解析,直接调用 Day.js 会出现解析缺失或告警。此时必须先显式声明插件:
import dayjs from 'dayjs';
import advancedFormat from 'dayjs/plugin/advancedFormat';
dayjs.extend(advancedFormat);
console.log(dayjs().format('YYYY年 第Q季度 Do天'));
// 示例输出:"2026年 第4季度 10th天"
2. 误区二:“Moment.js 已经被废弃,现存老项目必须立刻全部重写”
辨析与修正:官方声明的是 Moment.js 进入“维护状态(Maintenance Mode)”,不再新增功能,但依然会修复关键安全漏洞。对于规模庞大、深度整合了 Moment 各种私有方法或旧版组件库的既有后台项目,如果不面临严格的首屏加载限制,盲目进行全局重构反而会带来非预期回归的风险。只有在确实存在网络加载瓶颈或重构契机时,进行平滑替换才是合理做法。
五、选型与迁移建议
综合性能、安全性与维护成本,针对不同项目场景的决策路径如下:
-
新建立项的前端或 Node.js 服务:坚决选择 Day.js
当前时间格式化作为最基础的高频需求,Day.js 仅 2KB 的包体积对首屏性能极度友好,同时天生的不可变性消除了团队协作中的隐性 Bug。 -
正处于维护期、面临打包体积优化的既有项目:平滑迁移至 Day.js
可先从时间格式化的通用工具函数着手,用 Day.js 替换 Moment.js;对于常规格式化代码,几乎只需要将moment(...)文本替换为dayjs(...)。 -
短期内无维护计划或依赖旧UI框架的历史系统:保持 Moment.js
若项目依赖了像旧版 Ant Design(3.x)等强绑定 Moment 的组件库,强行替换往往成本过高。此时可通过配置构建工具(如ContextReplacementPlugin)移除多余语言包,以缓解体积负担。
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。