本文解析 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.etsimportdayjsfrom'dayjs';// DateModel.etsimportdayjsfrom'dayjs';// CalendarVM.etsimportdayjsfrom'dayjs';// MonthWeekCalendar.etsimportdayjsfrom'dayjs';// CalendarPage.etsimportdayjsfrom'dayjs';// BaseCalendar.etsimportdayjsfrom'dayjs';项目中有 6 个文件导入了 dayjs,覆盖了所有需要日期处理的模块。
三、dayjs 在 DateUtil 中的应用
格式化
staticgetFormatDate(date:Date|dayjs.Dayjs):string{returndayjs(date).format('YYYY-MM-DD');}staticgetFullDateStr(date:Date):string{returndayjs(date).format('YYYY年MM月DD日');}staticgetDateTimeStr(date:Date):string{returndayjs(date).format('YYYY-MM-DD HH:mm');}常用格式化占位符
| 占位符 | 含义 | 示例 |
|---|---|---|
YYYY | 四位年份 | 2026 |
MM | 两位月份 | 07 |
DD | 两位日期 | 17 |
HH | 24小时制 | 23 |
mm | 分钟 | 07 |
ss | 秒 | 00 |
日期判断
staticisToday(date:Date|dayjs.Dayjs):boolean{returndayjs(date).isSame(dayjs(),'day');}isSame的第二个参数指定比较粒度:
'day':比较到天'month':比较到月'year':比较到年
获取月份天数
staticgetDaysByMonth(year:number,month:number):number{returndayjs().year(year).month(month).daysInMonth();}daysInMonth()自动处理闰年:
dayjs().year(2024).month(1).daysInMonth()→ 29(闰年二月)dayjs().year(2025).month(1).daysInMonth()→ 28(平年二月)
星期名称
staticgetWeekdayName(date:Date|dayjs.Dayjs):string{constnames=['周日','周一','周二','周三','周四','周五','周六'];returnnames[dayjs(date).day()];}dayjs().day()返回 0-6(0=周日)。
日期键生成
staticbuildKeyByDate(date:Date|dayjs.Dayjs):string{returndayjs(date).format('YYYY-MM-DD');}日期范围
staticgetDateRangeKeys(startDate:Date,endDate:Date):string[]{constkeys:string[]=[];conststart=dayjs(startDate);constend=dayjs(endDate);letcurrent=start;while(current.isBefore(end)||current.isSame(end,'day')){keys.push(current.format('YYYY-MM-DD'));current=current.add(1,'day');}returnkeys;}使用isBefore+isSame组合判断范围边界,add(1, 'day')递增日期。
四、dayjs 在 DateModel 中的应用
exportclassDateModel{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 中的应用
当前日期
@TraceselectDate:dayjs.Dayjs=dayjs();使用dayjs()获取当前时间作为初始选中日期。
月份数据生成
getDateList(date:dayjs.Dayjs):DateModelList{constyear=date.year();constmonth=date.month();constfirstDay=dayjs().year(year).month(month).date(1);constdaysInMonth=date.daysInMonth();letfirstDayWeekday:number=firstDay.day();firstDayWeekday=(firstDayWeekday-this.startWeekday+WEEK_DAYS)%WEEK_DAYS;// 上月填充for(leti=firstDayWeekday-1;i>=0;i--){constprevDate=firstDay.subtract(i+1,'day');list.push(newDateModel(prevDate,false));}// 当月for(leti=1;i<=daysInMonth;i++){constdayDate=dayjs().year(year).month(month).date(i);list.push(newDateModel(dayDate,true));}// 下月填充while(list.length<42){constlastDate=list[list.length-1].dayjsObj;constnextDate=lastDate.add(1,'day');list.push(newDateModel(nextDate,false));}returnlist;}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。
初始化数据
privateinitCalendarData():void{this.monthWeekDataSource.clearData();for(leti=-1;i<=1;i++){constdate=dayjs().add(i,'month');// 当前月 ± ithis.monthWeekDataSource.addData(this.getDateList(date));}}日期导航
goToToday():void{this.selectDate=dayjs();this.initCalendarData();}getCurrentMonthTitle():string{returnthis.selectDate.format('YYYY年MM月');}getCurrentYearTitle():string{return`${this.selectDate.year()}年`;}日期比较
isToday(date:dayjs.Dayjs):boolean{returndate.isSame(dayjs(),'day');}isDateSelect(date:dayjs.Dayjs):boolean{returndate.isSame(this.selectDate,'day');}六、dayjs 在组件中的应用
CalendarPage 中的日期操作
// 上一月this.vm.selectDate=this.vm.selectDate.subtract(1,'month');// 下一月this.vm.selectDate=this.vm.selectDate.add(1,'month');// 日期选择器回调constdate=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 中的日期操作
privategetDateList():DateModel[]{constdate=dayjs().year(this.year).month(this.month);constfirstDay=date.date(1);constdaysInMonth=date.daysInMonth();// ...}七、dayjs 与 Date 的互转
Date → dayjs
constd=newDate();constdj=dayjs(d);// Date → dayjsdayjs → Date
constdj=dayjs();constd=dj.toDate();// dayjs → Date项目中的互转场景
// CalendarPage 中 DatePicker 回调.onChange((value:DatePickerResult)=>{constdate=dayjs().year(value.year).month(value.month).date(value.day);this.vm.changeDate(date);// 传入 dayjs})// MonthWeekCalendar 中日期点击privateonDateClick(dateModel:DateModel):void{this.vm.changeDate(dateModel.dayjsObj);// 传入 dayjsthis.onChangeDay(dateModel.dayjsObj.toDate());// 传入 Date}八、dayjs 的不可变性
不可变操作
consttoday=dayjs();consttomorrow=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(可变)constd=newDate('2026-07-17');d.setDate(d.getDate()+1);console.log(d);// 2026-07-18(原对象被修改)// dayjs(不可变)constdj=dayjs('2026-07-17');constnext=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(leti=1;i<=daysInMonth;i++){constdayDate=dayjs().year(year).month(month).date(i);list.push(newDateModel(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 以极小的代价为项目提供了强大的日期处理能力,是鸿蒙应用中日期处理的首选方案。