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 在记账中的日期处理流程

用户选择

后端接口

交易日期数据

数据来源

dayjs 标准化

存储为 YYYY-MM-DD

查询场景

展示场景

统计场景

startOf/endOf 按月范围

isBefore/isAfter 条件筛选

format 格式化为 MM月DD日

分组为日 > 按月聚合

diff 计算时间跨度

isSame 按月分组汇总

五、项目代码案例

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/082026-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 的核心包配合按需加载的插件机制,既能满足记账应用的日期处理需求,又不会显著增加应用体积。

八、参考文档

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐