日期与时间处理——ArkTS 中的时间工具
一、时间处理的常见需求
在英语学习应用中,时间处理无处不在:
- 记录生词添加时间、答题时间戳
- 判断"今天"是否已经学习过
- 计算连续学习天数
- 生成每日挑战题目的种子
- 周维度统计数据的日期计算
ArkTS 基于标准 ECMAScript,提供了完整的Date对象 API。本文将通过项目中的实际源码,展示常见的日期时间处理模式。
二、核心 Date 对象用法
2.1 获取当前时间戳
Date.now()返回距 1970 年 1 月 1 日的毫秒数,是最常用的时间记录方式:
// NewWordManager 中记录添加时间addTime:Date.now()// AnswerQuestionsPage 中记录答题时间戳currentItem.jlStamp=Date.now();2.2 格式化日期字符串
将 Date 对象转为YYYY-MM-DD格式的字符串,用于"某天的数据"的键值判断:
// DailyChallengeManager 中获取今天的日期字符串privategetTodayStr():string{letnow=newDate();letyear=now.getFullYear();letmonth=(now.getMonth()+1).toString().padStart(2,'0');letday=now.getDate().toString().padStart(2,'0');return`${year}-${month}-${day}`;}这段代码有几点值得注意:
getMonth()返回 0~11,所以需要+1padStart(2, '0')确保月份和日期始终为两位数- 使用模板字符串拼接,比
+连接更清晰
2.3 时间戳清零(归零到当天 0 点)
在比较是否为同一天时,需要先将时间戳归零到当天 0 点:
// LearningPlanManager 中的关键操作lettoday=newDate();today.setHours(0,0,0,0);// 设置为当天 00:00:00.000lettodayTimestamp=today.getTime();setHours(0, 0, 0, 0)将时、分、秒、毫秒全部归零,这样比较两个 Date 对象的getTime()就能判断它们是否在同一天。
三、LearningPlanManager 中的日期计算
checkAndUpdateContinuousDays方法展示了完整的天数计算逻辑:
publiccheckAndUpdateContinuousDays():LearningPlan{letplan=this.getLearningPlan();lettoday=newDate();today.setHours(0,0,0,0);lettodayTimestamp=today.getTime();if(plan.lastStudyDate===0){// 首次学习plan.continuousDays=1;}else{letlastDate=newDate(plan.lastStudyDate);lastDate.setHours(0,0,0,0);letlastTimestamp=lastDate.getTime();letdiffDays=Math.floor((todayTimestamp-lastTimestamp)/(1000*60*60*24));if(diffDays===0){// 同一天,不更新}elseif(diffDays===1){// 连续plan.continuousDays++;}else{// 中断,重置plan.continuousDays=1;}}plan.lastStudyDate=todayTimestamp;this.saveLearningPlan(plan);returnplan;}3.1 天数差计算
letdiffDays=Math.floor((todayTimestamp-lastTimestamp)/(1000*60*60*24));这个公式的原理:
- 两个时间戳相减得到毫秒差
1000 * 60 * 60 * 24是一天的毫秒数Math.floor向下取整,确保跨天判断准确
3.2 连续天数的三种状态
| diffDays | 含义 | 操作 |
|---|---|---|
| 0 | 同一天 | 不更新(防止重复打卡增加天数) |
| 1 | 连续 | continuousDays++ |
| >1 | 中断 | 重置为 1 |
四、DailyChallengeManager 中的种子算法
每日挑战需要确保:同一天内题目固定不变,不同天题目不同。这通过日期字符串生成随机种子来实现:
generateDailyChallenge(allQuestions:TopicItemType[]):void{lettodayStr=this.getTodayStr();// 已存在当日题目,直接复用letexistingQuestions=this.challengeQuestions;if(existingQuestions.length===5&&this.todayCompleted===false){letfirstKey=existingQuestions.length>0?existingQuestions[0].keyID:'';letdateSeed=todayStr.split('-').join('');if(firstKey.startsWith('DC_'+dateSeed)){return;}}// 用日期字符串生成种子letseed=0;for(leti=0;i<todayStr.length;i++){seed=seed*31+todayStr.charCodeAt(i);}seed=Math.abs(seed);// 基于种子的 Fisher-Yates 洗牌letcount=Math.min(5,allQuestions.length);letselected:TopicItemType[]=[];letindices:number[]=[];for(leti=0;i<allQuestions.length;i++){indices.push(i);}for(leti=indices.length-1;i>0;i--){seed=(seed*16807+7)%2147483647;letj=seed%(i+1);lettemp=indices[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[]=['周日','周一','周二','周三','周四','周五','周六'];lettoday=newDate();today.setHours(0,0,0,0);for(leti=6;i>=0;i--){letd=newDate(today.getTime()-i*24*60*60*1000);letdateStr=(d.getMonth()+1).toString()+'/'+d.getDate().toString();letdayLabel=dayLabels[d.getDay()];// ...}}核心技巧:new Date(today.getTime() - i * 24 * 60 * 60 * 1000)通过一天天的毫秒数偏移获取前几天的日期。这比setDate(getDate() - 1)更直观。
六、日期格式化的其他方式
6.1 笔记添加时间
// AnswerQuestionsPage 中记录笔记时间this.topicItemModelPref.addNoteTime=newDate().getTime();6.2 打卡日历月份判断
getCheckInCalendarMonth(year:number,month:number):boolean[]{letresult:boolean[]=newArray(31).fill(false)asboolean[];letmonthStr=`${year}-${month.toString().padStart(2,'0')}-`;for(leti=0;i<this.checkInDays.length;i++){if(this.checkInDays[i].startsWith(monthStr)){letdayStr=this.checkInDays[i].substring(monthStr.length);letday=parseInt(dayStr,10);if(day>=1&&day<=31){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 保持一致,掌握这些模式后,任何与时间相关的业务需求都能迎刃而解。