HarmonyOs应用《重要日》开发第7篇 - 日期处理:dayjs 集成与 DateUtil 封装

发布时间:2026/7/16 0:10:04
HarmonyOs应用《重要日》开发第7篇 - 日期处理:dayjs 集成与 DateUtil 封装 本篇深入讲解 ImportantDays 项目中日期处理的核心工具类 DateUtil它基于 dayjs 库封装了所有日期计算逻辑包括倒数计算、重复事件处理、日期格式化等。一、为什么选择 dayjs1.1 dayjs 简介dayjs 是一个轻量级的 JavaScript 日期处理库API 设计与 moment.js 兼容但体积只有 2KB。在 HarmonyOS 的 ArkTS 环境中dayjs 通过 OpenHarmony 包管理器安装使用。1.2 对比原生 Date API// 原生 Date计算两个日期相差天数constdiffMath.floor((newDate(2026-03-15).getTime()-newDate(2026-01-01).getTime())/86400000);// dayjs同样的计算constdiffdayjs(2026-03-15).diff(dayjs(2026-01-01),day);dayjs 的优势API 简洁链式调用代码可读性高功能丰富日期加减、比较、格式化一站式解决不可变性操作返回新对象不修改原对象轻量压缩后仅 2KB二、DateUtil 工具类2.1 完整实现// utils/DateUtil.etsimportdayjsfromdayjs;import{YearMonth}from../types/Types;exportclassDateUtil{// 获取今天的日期字符串statictoday():string{returndayjs().format(YYYY-MM-DD);}// 格式化日期staticformatDate(date:string,format:stringYYYY-MM-DD):string{returndayjs(date).format(format);}// 计算两个日期之间的天数差staticdaysBetween(fromDate:string,toDate:string):number{constfromdayjs(fromDate);consttodayjs(toDate);returnto.diff(from,day);}// 计算从今天到目标日期的天数正未来负过去staticdaysFromToday(targetDate:string):number{returnDateUtil.daysBetween(DateUtil.today(),targetDate);}// 判断是否是今天staticisToday(date:string):boolean{returndayjs(date).isSame(dayjs(),day);}// 判断是否在本月staticisThisMonth(date:string):boolean{returndayjs(date).isSame(dayjs(),month);}// 根据偏移量获取年月staticgetYearMonth(offset:number):YearMonth{constddayjs().add(offset,month);return{year:d.year(),month:d.month()1};}// 从日期字符串获取年月staticgetYearMonthFromDate(date:string):YearMonth{constddayjs(date);return{year:d.year(),month:d.month()1};}// 获取某月的天数staticgetDaysInMonth(year:number,month:number):number{returndayjs().year(year).month(month-1).daysInMonth();}// 获取某月第一天是星期几0周日6周六staticgetFirstDayOfMonth(year:number,month:number):number{returndayjs().year(year).month(month-1).date(1).day();}// 格式化年月显示staticformatYearMonth(year:number,month:number):string{return${year}年${month}月;}// 格式化中文日期staticformatDateChinese(date:string):string{constddayjs(date);return${d.year()}年${d.month()1}月${d.date()}日;}// 获取下一次重复日期staticgetNextOccurrence(date:string,repeatType:number):string{consttargetdayjs(date);constnowdayjs();if(repeatType1){// 每年letnexttarget.year(now.year());if(next.isBefore(now,day)||next.isSame(now,day)){nextnext.year(now.year()1);}returnnext.format(YYYY-MM-DD);}elseif(repeatType2){// 每月letnexttarget.year(now.year()).month(now.month());if(next.isBefore(now,day)||next.isSame(now,day)){nextnext.add(1,month);}returnnext.format(YYYY-MM-DD);}returndate;}// 获取星期标签staticgetWeekLabel(date:string):string{constdaydayjs(date).day();constlabels[周日,周一,周二,周三,周四,周五,周六];returnlabels[day];}// 日期加减天数staticaddDays(date:string,days:number):string{returndayjs(date).add(days,day).format(YYYY-MM-DD);}// 生成唯一IDstaticgenerateId():string{return${Date.now()}_${Math.floor(Math.random()*10000)};}}三、核心方法详解3.1 日期差计算staticdaysFromToday(targetDate:string):number{returnDateUtil.daysBetween(DateUtil.today(),targetDate);}staticdaysBetween(fromDate:string,toDate:string):number{constfromdayjs(fromDate);consttodayjs(toDate);returnto.diff(from,day);}返回值含义正数目标日期在未来还有 X 天负数目标日期在过去已过 X 天零就是今天这个方法是整个应用倒数/正数功能的基础。3.2 重复事件的下一次日期staticgetNextOccurrence(date:string,repeatType:number):string{consttargetdayjs(date);constnowdayjs();if(repeatType1){// 每年重复letnexttarget.year(now.year());// 替换为今年if(next.isBefore(now,day)||next.isSame(now,day)){nextnext.year(now.year()1);// 如果今年已过或就是今天推到明年}returnnext.format(YYYY-MM-DD);}if(repeatType2){// 每月重复letnexttarget.year(now.year()).month(now.month());// 替换为本月if(next.isBefore(now,day)||next.isSame(now,day)){nextnext.add(1,month);// 如果本月已过或就是今天推到下月}returnnext.format(YYYY-MM-DD);}returndate;// 不重复返回原日期}逻辑流程以每年重复为例原始日期: 2020-03-15今天: 2026-07-15 1. target dayjs(2020-03-15) 2. next target.year(2026) 2026-03-15 3. 2026-03-15 2026-07-15已过→ next next.year(2027) 2027-03-15 4. 返回 2027-03-153.3 闰年处理staticgetDaysInMonth(year:number,month:number):number{returndayjs().year(year).month(month-1).daysInMonth();}dayjs 内部自动处理闰年。例如DateUtil.getDaysInMonth(2024,2);// 29闰年DateUtil.getDaysInMonth(2025,2);// 28平年3.4 月初星期计算staticgetFirstDayOfMonth(year:number,month:number):number{returndayjs().year(year).month(month-1).date(1).day();}返回值 0-6 对应周日到周六。用于日历视图中确定第一行需要留多少空位。3.5 中文格式化staticformatDateChinese(date:string):string{constddayjs(date);return${d.year()}年${d.month()1}月${d.date()}日;}staticformatYearMonth(year:number,month:number):string{return${year}年${month}月;}注意 dayjs 的month()返回 0-11所以需要 1。四、在项目中的使用场景4.1 列表页计算倒数天数// MainViewModel.getCountText()consteffectiveDatethis.getEffectiveDate(day);constdaysDateUtil.daysFromToday(effectiveDate);// days 0: X天后// days 0: X天前// days 0: 今天4.2 日历页生成月份数据// CalendarDataSource.generateMonth()constdaysInMonthDateUtil.getDaysInMonth(d.year,d.month);constfirstDayOfWeekDateUtil.getFirstDayOfMonth(d.year,d.month);// 用这些数据生成 42 格日历6行 x 7列4.3 日历页标记今天// CalendarGridView.dayCell()privateisToday(date:string):boolean{returnDateUtil.isToday(date);}4.4 统计本月重要日// MainViewModel.getStats()if(DateUtil.isThisMonth(effectiveDate)){thisMonth;}4.5 日历查询按月日匹配// MainViewModel.getDaysForDate()if(day.repeatTypeRepeatType.YEARLY){constdayMonthDateUtil.formatDate(day.date,MM-DD);consttargetMonthDateUtil.formatDate(date,MM-DD);returndayMonthtargetMonth;}五、dayjs 在 ArkTS 中的注意事项5.1 导入方式importdayjsfromdayjs;在 oh-package.json5 中声明依赖{ dependencies: { dayjs: ^1.11.13 } }5.2 链式调用dayjs 支持链式调用但需要注意 ArkTS 的类型推断// 正确分步调用constddayjs().year(year).month(month-1);constdaysd.daysInMonth();// 也可以链式调用constdaysdayjs().year(year).month(month-1).daysInMonth();5.3 不可变性dayjs 对象是不可变的每次操作返回新对象constadayjs(2026-01-01);constba.add(1,month);// a 仍然是 2026-01-01// b 是 2026-02-01这在 ArkUI V2 的响应式系统中很重要——不会意外修改被Trace观察的对象。六、扩展思考6.1 时区处理当前项目没有考虑时区问题。如果需要国际化可以使用 dayjs 的 UTC 插件importutcfromdayjs/plugin/utc;dayjs.extend(utc);constutcDatedayjs.utc(2026-03-15);6.2 相对时间如果需要显示3小时后这样的相对时间可以使用 dayjs 的 relativeTime 插件。但本项目只精确到天不需要。6.3 自定义解析dayjs 默认能解析YYYY-MM-DD格式。如果需要解析其他格式dayjs(2026年3月15日,YYYY年M月D日);七、小结DateUtil 作为项目的日期处理核心封装了 dayjs 的常用操作提供了简洁的静态方法接口。从倒数计算到重复事件处理从日期格式化到日历数据生成所有日期相关的逻辑都集中在这里提高了代码的可维护性和可测试性。