本文解析 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');  // 预生成日期键
  }
}

设计要点

  1. 存储 dayjs 对象dayjsObj 保存原始 dayjs 对象,便于后续操作
  2. 预计算属性daydateStr 在构造时预计算,避免渲染时重复调用
  3. 农历预留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)

创建指定年月的第一天:

  1. dayjs() 获取当前时间
  2. .year(year) 设置年份
  3. .month(month) 设置月份
  4. .date(1) 设置为1号

日期加减

firstDay.subtract(i + 1, 'day')  // 向前推 i+1 天
lastDate.add(1, 'day')            // 向后推 1 天

subtractadd 的第二个参数支持:dayweekmonthyearhourminutesecond

初始化数据

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(新对象)

addsubtract 等操作不修改原对象,而是返回新对象。

与原生 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 预计算了 daydateStr,避免渲染时重复调用 dayjs 方法。

十、总结

dayjs 在 ImportantDays 项目中的使用体现了以下实践:

  1. 统一工具类DateUtil 封装所有 dayjs 操作
  2. 双类型兼容:方法接受 Date | dayjs.Dayjs
  3. 预计算优化DateModel 预计算常用属性
  4. 不可变操作:利用 dayjs 不可变性保障状态安全
  5. 链式调用dayjs().year().month().date() 简洁高效
  6. 灵活格式化format() 支持各种日期格式

dayjs 以极小的代价为项目提供了强大的日期处理能力,是鸿蒙应用中日期处理的首选方案。

Logo

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

更多推荐