036-dayjs日期库在鸿蒙应用中的集成与最佳实践
dayjs 日期库在鸿蒙应用中的集成与最佳实践
一、引言
日期处理是记账应用中最基础也最频繁的操作之一——格式化显示账单日期、按月筛选交易数据、按日分组账单列表、计算天数差等。鸿蒙 ArkTS 支持通过 oh-package 引入第三方 JavaScript/TypeScript 库,dayjs 作为轻量级的日期处理库(仅 2KB),提供了与 Moment.js 兼容的 API,是鸿蒙应用日期处理的首选方案。MoneyTrack 在 HomeVM 和 DailyBillGroup 等核心模块中重度使用 dayjs,实现了日期的格式化、按月分组、日历计算和日期比较等功能。
二、核心知识点
2.1 第三方库集成
鸿蒙应用通过 oh-package.json5 文件管理第三方依赖,与 npm 的 package.json 类似:
// oh-package.json5
{
"dependencies": {
"dayjs": "^1.11.10"
}
}
导入后即可在 TypeScript/ArkTS 文件中使用:
import dayjs from 'dayjs'
2.2 日期格式化
dayjs 提供了丰富的日期格式化能力:
dayjs().format('YYYY-MM-DD') // 2026-07-08
dayjs().format('MM月DD日') // 07月08日
dayjs().format('YYYY年MM月DD日 dddd') // 2026年07月08日 星期三
dayjs().format('YYYY-MM-DD HH:mm:ss') // 2026-07-08 14:30:00
在记账应用中,常用格式包括 YYYY-MM-DD(数据库存储)、MM月DD日(列表显示)、YYYY年MM月(月份切换)、YYYY-MM(分组键值)。
2.3 常用日期操作方法
diff 天数差计算
// 计算两个日期之间的天数差
const startDate = '2026-01-01'
const endDate = '2026-07-08'
const daysDiff = dayjs(endDate).diff(dayjs(startDate), 'day')
// 结果: 188 天
// 按月差计算
const monthDiff = dayjs(endDate).diff(dayjs(startDate), 'month')
// 结果: 6 个月
// 精确到小数
const exactDiff = dayjs(endDate).diff(dayjs(startDate), 'day', true)
isBefore / isAfter 日期比较
const today = dayjs()
const deadline = dayjs('2026-07-15')
// 判断是否在某个日期之前
if (today.isBefore(deadline)) {
console.log('未到期')
}
// 判断是否在某个日期之后
if (today.isAfter(deadline)) {
console.log('已过期')
}
// 判断是否相同(支持粒度比较)
dayjs('2026-07-08').isSame('2026-07-08', 'day') // true
dayjs('2026-07').isSame('2026-07', 'month') // true
startOf / endOf 获取时间范围
// 获取月份起止日期(常用于筛选查询)
const monthStart = dayjs().startOf('month').format('YYYY-MM-DD')
const monthEnd = dayjs().endOf('month').format('YYYY-MM-DD')
// 获取年度起止
const yearStart = dayjs().startOf('year').format('YYYY-MM-DD')
const yearEnd = dayjs().endOf('year').format('YYYY-MM-DD')
三、dayjs 插件介绍
dayjs 的核心包只包含最基础的解析和格式化功能,更多高级功能通过插件扩展:
import advancedFormat from 'dayjs/plugin/advancedFormat'
import timezone from 'dayjs/plugin/timezone'
import localizedFormat from 'dayjs/plugin/localizedFormat'
import utc from 'dayjs/plugin/utc'
// 注册插件
dayjs.extend(advancedFormat)
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.extend(localizedFormat)
| 插件名 | 功能 | 使用示例 |
|---|---|---|
| advancedFormat | 更多格式化标记 | dayjs().format('Q') 显示季度 |
| utc | UTC 模式支持 | dayjs.utc() 解析 UTC 时间 |
| timezone | 时区转换 | dayjs().tz('America/New_York') |
| localizedFormat | 本地化格式 | dayjs().format('LTS') 本地时间格式 |
| relativeTime | 相对时间 | dayjs().fromNow() 输出"3天前" |
| isBetween | 区间判断 | dayjs().isBetween(a, b) |
四、dayjs 在记账中的日期处理流程
五、项目代码案例
5.1 HomeVM 中 dayjs 处理月份切换
在 products/entry/src/main/ets/viewmodel/home/HomeVM.ets 中,月份切换功能依赖 dayjs:
// 切换到上个月
previousMonth() {
this.currentMonth = dayjs(this.currentMonth).subtract(1, 'month').format('YYYY-MM')
this.loadMonthData()
}
// 切换到下个月
nextMonth() {
this.currentMonth = dayjs(this.currentMonth).add(1, 'month').format('YYYY-MM')
this.loadMonthData()
}
- 使用
dayjs().subtract(n, 'month')实现月份的向前/向后切换 - 首页的月份切换带动整个账单数据重新加载
- 月份格式统一使用
YYYY-MM存储和传递
5.2 DailyBillGroup 按 dateStr 分组
在账单列表展示中,需要通过 dayjs 将交易记录按日期分组:
// 按日期分组
const groups = transactions.reduce((acc, tx) => {
const dateStr = dayjs(tx.date).format('YYYY-MM-DD')
if (!acc[dateStr]) acc[dateStr] = []
acc[dateStr].push(tx)
return acc
}, {} as Record<string, Transaction[]>)
// 分组标题格式化
const groupTitle = dayjs(dateStr).format('MM月DD日') // 07月08日
// 计算该日总金额
const dayTotal = groups[dateStr].reduce((sum, tx) => sum + tx.amount, 0)
六、日期处理常见陷阱
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 时区问题 | dayjs 默认使用本地时区,与 UTC 时间可能不一致 | 使用 utc 插件显式处理时区,服务端交互统一使用 UTC |
| 闰年处理 | 2月29日在非闰年不存在 | 使用 dayjs 的 API 操作日期(如 endOf('month')),避免手动计算 |
| 月末日期 | 1月31日加一个月是2月28/29日 | 使用 endOf('month') 获取月末安全值 |
| 日期字符串格式 | 2026/07/08 和 2026-07-08 解析结果可能不同 |
统一使用 YYYY-MM-DD 格式 |
| 性能陷阱 | 循环中重复 dayjs() 实例化 |
提前创建 dayjs 对象并复用 |
// 安全地获取上个月同一天(避免月末陷阱)
function getPreviousMonth(date: string): string {
return dayjs(date).subtract(1, 'month').format('YYYY-MM-DD')
// dayjs 自动处理:1月31日 → 12月31日(不是12月31日)
// 实际上 dayjs 的 subtract 会正确处理,如果结果无效会回滚到月末
}
// 正确处理月末
const lastDayOfMonth = dayjs('2026-02-01').endOf('month').format('YYYY-MM-DD')
// 2026年不是闰年,返回 2026-02-28
七、替代方案对比
| 维度 | Date 原生 | dayjs | Luxon |
|---|---|---|---|
| 包体积 | 内置,0KB | 2KB(核心) | ~50KB |
| API 友好度 | 较低(getMonth 从0开始) | 高(Moment.js 兼容) | 高(不可变 API) |
| 国际化 | 基础 | 插件支持 | 内置支持 |
| 时区支持 | 手动处理 | timezone 插件 | 内置 |
| 鸿蒙兼容性 | ✅ 原生支持 | ✅ 已验证 | ⚠️ 需测试 |
| 推荐度 | 简单场景 | ⭐ 推荐 | 复杂国际化场景 |
选型建议:在鸿蒙记账应用中,dayjs 是平衡体积和功能的最佳选择。Date 原生对象 API 不够友好(月份从 0 开始、格式化需要手动实现),而 Luxon 体积较大且鸿蒙兼容性未充分验证。dayjs 仅 2KB 的核心包配合按需加载的插件机制,既能满足记账应用的日期处理需求,又不会显著增加应用体积。
八、参考文档
更多推荐




所有评论(0)