HarmonyOS 应用开发《掌上英语》第43篇:日期与时间处理——ArkTS 中的时间工具 日期与时间处理——ArkTS 中的时间工具一、时间处理的常见需求在英语学习应用中时间处理无处不在记录生词添加时间、答题时间戳判断今天是否已经学习过计算连续学习天数生成每日挑战题目的种子周维度统计数据的日期计算ArkTS 基于标准 ECMAScript提供了完整的Date对象 API。本文将通过项目中的实际源码展示常见的日期时间处理模式。二、核心 Date 对象用法2.1 获取当前时间戳Date.now()返回距 1970 年 1 月 1 日的毫秒数是最常用的时间记录方式// NewWordManager 中记录添加时间addTime:Date.now()// AnswerQuestionsPage 中记录答题时间戳currentItem.jlStampDate.now();2.2 格式化日期字符串将 Date 对象转为YYYY-MM-DD格式的字符串用于某天的数据的键值判断// DailyChallengeManager 中获取今天的日期字符串privategetTodayStr():string{letnownewDate();letyearnow.getFullYear();letmonth(now.getMonth()1).toString().padStart(2,0);letdaynow.getDate().toString().padStart(2,0);return${year}-${month}-${day};}这段代码有几点值得注意getMonth()返回 0~11所以需要1padStart(2, 0)确保月份和日期始终为两位数使用模板字符串拼接比连接更清晰2.3 时间戳清零归零到当天 0 点在比较是否为同一天时需要先将时间戳归零到当天 0 点// LearningPlanManager 中的关键操作lettodaynewDate();today.setHours(0,0,0,0);// 设置为当天 00:00:00.000lettodayTimestamptoday.getTime();setHours(0, 0, 0, 0)将时、分、秒、毫秒全部归零这样比较两个 Date 对象的getTime()就能判断它们是否在同一天。三、LearningPlanManager 中的日期计算checkAndUpdateContinuousDays方法展示了完整的天数计算逻辑publiccheckAndUpdateContinuousDays():LearningPlan{letplanthis.getLearningPlan();lettodaynewDate();today.setHours(0,0,0,0);lettodayTimestamptoday.getTime();if(plan.lastStudyDate0){// 首次学习plan.continuousDays1;}else{letlastDatenewDate(plan.lastStudyDate);lastDate.setHours(0,0,0,0);letlastTimestamplastDate.getTime();letdiffDaysMath.floor((todayTimestamp-lastTimestamp)/(1000*60*60*24));if(diffDays0){// 同一天不更新}elseif(diffDays1){// 连续plan.continuousDays;}else{// 中断重置plan.continuousDays1;}}plan.lastStudyDatetodayTimestamp;this.saveLearningPlan(plan);returnplan;}3.1 天数差计算letdiffDaysMath.floor((todayTimestamp-lastTimestamp)/(1000*60*60*24));这个公式的原理两个时间戳相减得到毫秒差1000 * 60 * 60 * 24是一天的毫秒数Math.floor向下取整确保跨天判断准确3.2 连续天数的三种状态diffDays含义操作0同一天不更新防止重复打卡增加天数1连续continuousDays1中断重置为 1四、DailyChallengeManager 中的种子算法每日挑战需要确保同一天内题目固定不变不同天题目不同。这通过日期字符串生成随机种子来实现generateDailyChallenge(allQuestions:TopicItemType[]):void{lettodayStrthis.getTodayStr();// 已存在当日题目直接复用letexistingQuestionsthis.challengeQuestions;if(existingQuestions.length5this.todayCompletedfalse){letfirstKeyexistingQuestions.length0?existingQuestions[0].keyID:;letdateSeedtodayStr.split(-).join();if(firstKey.startsWith(DC_dateSeed)){return;}}// 用日期字符串生成种子letseed0;for(leti0;itodayStr.length;i){seedseed*31todayStr.charCodeAt(i);}seedMath.abs(seed);// 基于种子的 Fisher-Yates 洗牌letcountMath.min(5,allQuestions.length);letselected:TopicItemType[][];letindices:number[][];for(leti0;iallQuestions.length;i){indices.push(i);}for(letiindices.length-1;i0;i--){seed(seed*168077)%2147483647;letjseed%(i1);lettempindices[i];indices[i]indices[j];indices[j]temp;}// ...}这里的种子算法是将日期字符串2026-07-08的每个字符的 charCode 累加乘 31生成一个固定的种子。然后用线性同余法seed (seed * 16807 7) % 2147483647生成伪随机序列。五、统计模块中的周日期计算DashboardManager 在构建周度数据时需要回溯前 6 天privatebuildWeeklyData(plan:LearningPlan):DailyStats[]{letresult:DailyStats[][];letdayLabels:string[][周日,周一,周二,周三,周四,周五,周六];lettodaynewDate();today.setHours(0,0,0,0);for(leti6;i0;i--){letdnewDate(today.getTime()-i*24*60*60*1000);letdateStr(d.getMonth()1).toString()/d.getDate().toString();letdayLabeldayLabels[d.getDay()];// ...}}核心技巧new Date(today.getTime() - i * 24 * 60 * 60 * 1000)通过一天天的毫秒数偏移获取前几天的日期。这比setDate(getDate() - 1)更直观。六、日期格式化的其他方式6.1 笔记添加时间// AnswerQuestionsPage 中记录笔记时间this.topicItemModelPref.addNoteTimenewDate().getTime();6.2 打卡日历月份判断getCheckInCalendarMonth(year:number,month:number):boolean[]{letresult:boolean[]newArray(31).fill(false)asboolean[];letmonthStr${year}-${month.toString().padStart(2,0)}-;for(leti0;ithis.checkInDays.length;i){if(this.checkInDays[i].startsWith(monthStr)){letdayStrthis.checkInDays[i].substring(monthStr.length);letdayparseInt(dayStr,10);if(day1day31){result[day-1]true;}}}returnresult;}利用已存储的YYYY-MM-DD格式字符串通过startsWith和substring精确判断某月某日是否打过卡。七、时间处理最佳实践统一使用时间戳Date.now()或date.getTime()存储避免时区问题比较前清零判断是否为同一天时统一setHours(0,0,0,0)后再比较格式化字符串用于显示时再格式化推荐YYYY-MM-DD格式避免直接操作 Date 字符串优先使用时间戳计算差值最后再转为字符串ArkTS 的 Date API 与标准 JavaScript 保持一致掌握这些模式后任何与时间相关的业务需求都能迎刃而解。