HarmonyOs应用《重要日》开发第26篇 - 基于 dayjs 的日期处理方案
·
本文解析 ImportantDays 项目中 dayjs 库的使用方式,包括 dayjs 的引入、在 DateUtil/DateModel/CalendarVM 中的应用场景、与原生 Date 对象的互转,以及 dayjs 在鸿蒙 ArkTS 环境中的实践要点。

一、dayjs 简介
dayjs 是一个轻量级的 JavaScript 日期库,API 设计与 moment.js 兼容,但体积仅 2KB。
核心特性
| 特性 | 说明 |
|---|---|
| 轻量 | 2KB(gzip 后) |
| 不可变 | 所有操作返回新对象 |
| 链式调用 | dayjs().add(1, 'day').format('YYYY-MM-DD') |
| 插件系统 | 可按需引入功能 |
二、项目中的引入
oh-package.json5 依赖
{
"dependencies": {
"dayjs": "^1.11.x"
}
}
导入方式
// DateUtil.ets
import dayjs from 'dayjs';
// DateModel.ets
import dayjs from 'dayjs';
// CalendarVM.ets
import dayjs from 'dayjs';
// MonthWeekCalendar.ets
import dayjs from 'dayjs';
// CalendarPage.ets
import dayjs from 'dayjs';
// BaseCalendar.ets
import dayjs from 'dayjs';
项目中有 6 个文件导入了 dayjs,覆盖了所有需要日期处理的模块。
三、dayjs 在 DateUtil 中的应用
格式化
static getFormatDate(date: Date | dayjs.Dayjs): string {
return dayjs(date).format('YYYY-MM-DD');
}
static getFullDateStr(date: Date): string {
return dayjs(date).format('YYYY年MM月DD日');
}
static getDateTimeStr(date: Date): string {
return dayjs(date).format('YYYY-MM-DD HH:mm');
}
常用格式化占位符
| 占位符 | 含义 | 示例 |
|---|---|---|
YYYY |
四位年份 | 2026 |
MM |
两位月份 | 07 |
DD |
两位日期 | 17 |
HH |
24小时制 | 23 |
mm |
分钟 | 07 |
ss |
秒 | 00 |
日期判断
static isToday(date: Date | dayjs.Dayjs): boolean {
return dayjs(date).isSame(dayjs(), 'day');
}
isSame 的第二个参数指定比较粒度:
'day':比较到天'month':比较到月'year':比较到年
获取月份天数
static getDaysByMonth(year: number, month: number): number {
return dayjs().year(year).month(month).daysInMonth();
}
daysInMonth() 自动处理闰年:
dayjs().year(2024).month(1).daysInMonth()→ 29(闰年二月)dayjs().year(2025).month(1).daysInMonth()→ 28(平年二月)
星期名称
static getWeekdayName(date: Date | dayjs.Dayjs): string {
const names = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
return names[dayjs(date).day()];
}
dayjs().day() 返回 0-6(0=周日)。
日期键生成
static buildKeyByDate(date: Date | dayjs.Dayjs): string {
return dayjs(date).format('YYYY-MM-DD');
}
日期范围
static getDateRangeKeys(startDate: Date, endDate: Date): string[] {
const keys: string[] = [];
const start = dayjs(startDate);
const end = dayjs(endDate);
let current = start;
while (current.isBefore(end) || current.isSame(end, 'day')) {
keys.push(current.format('YYYY-MM-DD'));
current = current.add(1, 'day');
}
return keys;
}
使用 isBefore + isSame 组合判断范围边界,add(1, 'day') 递增日期。
四、dayjs 在 DateModel 中的应用
export class DateModel {
dayjsObj: dayjs.Dayjs;
day: number;
lunarDay: string;
isCurrentMonth: boolean;
dateStr: string;
constructor(dayjsObj: dayjs.Dayjs, isCurrentMonth: boolean = true) {
this.dayjsObj = dayjsObj;
this.day = dayjsObj.date(); // 获取日期数字
this.lunarDay = '';
this.isCurrentMonth = isCurrentMonth;
this.dateStr = dayjsObj.format('YYYY-MM-DD'); // 预生成日期键
}
}
设计要点
- 存储 dayjs 对象:
dayjsObj保存原始 dayjs 对象,便于后续操作 - 预计算属性:
day和dateStr在构造时预计算,避免渲染时重复调用 - 农历预留:
lunarDay预留农历字段(当前为空)
五、dayjs 在 CalendarVM 中的应用
当前日期
@Trace selectDate: dayjs.Dayjs = dayjs();
使用 dayjs() 获取当前时间作为初始选中日期。
月份数据生成
getDateList(date: dayjs.Dayjs): DateModelList {
const year = date.year();
const month = date.month();
const firstDay = dayjs().year(year).month(month).date(1);
const daysInMonth = date.daysInMonth();
let firstDayWeekday: number = firstDay.day();
firstDayWeekday = (firstDayWeekday - this.startWeekday + WEEK_DAYS) % WEEK_DAYS;
// 上月填充
for (let i = firstDayWeekday - 1; i >= 0; i--) {
const prevDate = firstDay.subtract(i + 1, 'day');
list.push(new DateModel(prevDate, false));
}
// 当月
for (let i = 1; i <= daysInMonth; i++) {
const dayDate = dayjs().year(year).month(month).date(i);
list.push(new DateModel(dayDate, true));
}
// 下月填充
while (list.length < 42) {
const lastDate = list[list.length - 1].dayjsObj;
const nextDate = lastDate.add(1, 'day');
list.push(new DateModel(nextDate, false));
}
return list;
}
dayjs 链式操作
dayjs().year(year).month(month).date(1)
创建指定年月的第一天:
dayjs()获取当前时间.year(year)设置年份.month(month)设置月份.date(1)设置为1号
日期加减
firstDay.subtract(i + 1, 'day') // 向前推 i+1 天
lastDate.add(1, 'day') // 向后推 1 天
subtract 和 add 的第二个参数支持:day、week、month、year、hour、minute、second。
初始化数据
private initCalendarData(): void {
this.monthWeekDataSource.clearData();
for (let i = -1; i <= 1; i++) {
const date = dayjs().add(i, 'month'); // 当前月 ± i
this.monthWeekDataSource.addData(this.getDateList(date));
}
}
日期导航
goToToday(): void {
this.selectDate = dayjs();
this.initCalendarData();
}
getCurrentMonthTitle(): string {
return this.selectDate.format('YYYY年MM月');
}
getCurrentYearTitle(): string {
return `${this.selectDate.year()}年`;
}
日期比较
isToday(date: dayjs.Dayjs): boolean {
return date.isSame(dayjs(), 'day');
}
isDateSelect(date: dayjs.Dayjs): boolean {
return date.isSame(this.selectDate, 'day');
}
六、dayjs 在组件中的应用
CalendarPage 中的日期操作
// 上一月
this.vm.selectDate = this.vm.selectDate.subtract(1, 'month');
// 下一月
this.vm.selectDate = this.vm.selectDate.add(1, 'month');
// 日期选择器回调
const date = dayjs().year(value.year).month(value.month).date(value.day);
this.vm.changeDate(date);
// 格式化
this.selectDate.format('YYYY-MM-DD')
MonthWeekCalendar 中的日期比较
// 月份比较
if (!this.vm.selectDate.isSame(middleDate, 'month')) {
this.vm.selectDate = middleDate;
}
BaseCalendar 中的日期操作
private getDateList(): DateModel[] {
const date = dayjs().year(this.year).month(this.month);
const firstDay = date.date(1);
const daysInMonth = date.daysInMonth();
// ...
}
七、dayjs 与 Date 的互转
Date → dayjs
const d = new Date();
const dj = dayjs(d); // Date → dayjs
dayjs → Date
const dj = dayjs();
const d = dj.toDate(); // dayjs → Date
项目中的互转场景
// CalendarPage 中 DatePicker 回调
.onChange((value: DatePickerResult) => {
const date = dayjs().year(value.year).month(value.month).date(value.day);
this.vm.changeDate(date); // 传入 dayjs
})
// MonthWeekCalendar 中日期点击
private onDateClick(dateModel: DateModel): void {
this.vm.changeDate(dateModel.dayjsObj); // 传入 dayjs
this.onChangeDay(dateModel.dayjsObj.toDate()); // 传入 Date
}
八、dayjs 的不可变性
不可变操作
const today = dayjs();
const tomorrow = today.add(1, 'day');
console.log(today.format('YYYY-MM-DD')); // 2026-07-17(不变)
console.log(tomorrow.format('YYYY-MM-DD')); // 2026-07-18(新对象)
add、subtract 等操作不修改原对象,而是返回新对象。
与原生 Date 的对比
// 原生 Date(可变)
const d = new Date('2026-07-17');
d.setDate(d.getDate() + 1);
console.log(d); // 2026-07-18(原对象被修改)
// dayjs(不可变)
const dj = dayjs('2026-07-17');
const next = dj.add(1, 'day');
console.log(dj.format('YYYY-MM-DD')); // 2026-07-17(不变)
console.log(next.format('YYYY-MM-DD')); // 2026-07-18(新对象)
不可变性在状态管理中非常重要——避免了意外修改导致的状态混乱。
九、性能考量
dayjs 对象创建
// 日历渲染中会创建大量 dayjs 对象
for (let i = 1; i <= daysInMonth; i++) {
const dayDate = dayjs().year(year).month(month).date(i);
list.push(new DateModel(dayDate, true));
}
每月创建约 42 个 dayjs 对象,年视图创建 12 × 42 = 504 个。dayjs 对象很轻量,这个数量级不会有性能问题。
预计算优化
constructor(dayjsObj: dayjs.Dayjs, isCurrentMonth: boolean = true) {
this.dayjsObj = dayjsObj;
this.day = dayjsObj.date(); // 预计算
this.dateStr = dayjsObj.format('YYYY-MM-DD'); // 预计算
}
DateModel 预计算了 day 和 dateStr,避免渲染时重复调用 dayjs 方法。
十、总结
dayjs 在 ImportantDays 项目中的使用体现了以下实践:
- 统一工具类:
DateUtil封装所有 dayjs 操作 - 双类型兼容:方法接受
Date | dayjs.Dayjs - 预计算优化:
DateModel预计算常用属性 - 不可变操作:利用 dayjs 不可变性保障状态安全
- 链式调用:
dayjs().year().month().date()简洁高效 - 灵活格式化:
format()支持各种日期格式
dayjs 以极小的代价为项目提供了强大的日期处理能力,是鸿蒙应用中日期处理的首选方案。
更多推荐




所有评论(0)