
在移动端表单中日期选择是最常见的输入场景之一。生日、纪念日、截止日期——这些都需要一个直观的日期选择组件。与日历视图CalendarPicker不同滚轮式日期选择器更适合快速翻选年月日特别适合选择出生日期、历史日期等需要跨越较大时间范围的场景。HarmonyOS NEXT ArkUI 提供了 DatePicker 组件——一个滚轮式的日期选择器用户通过上下滑动年/月/日三列滚轮来精确选择日期。本文将深入讲解 DatePicker 的 API 和使用细节并构建一个纪念日管理器——支持日期选择、事件命名、倒计时/正计时计算和分类筛选。关键词HarmonyOS、ArkUI、DatePicker、日期选择器、纪念日、倒计时、日期计算一、DatePicker 组件 API1.1 基本用法DatePicker({selected:this.pickDate}).onChange((value:DatePickerResult){letyvalue.year??newDate().getFullYear();letmvalue.month??0;letdvalue.day??1;this.pickDatenewDate(y,m,d);})核心参数与属性参数/属性类型说明selectedDate默认选中的日期startDate可选日期范围的起始可选endDate可选日期范围的结束可选.onChange()callback选中日期变化时的回调已标记为 deprecated1.2 DatePickerResult 的可选字段DatePicker 的onChange回调接收一个DatePickerResult对象包含三个字段interfaceDatePickerResult{year?:number;// 选中的年份可能为 undefinedmonth?:number;// 选中的月份0-110一月day?:number;// 选中的日期1-31}重要这三个字段都是可选的number | undefined。在构建Date对象时必须处理undefined的情况.onChange((value:DatePickerResult){letyvalue.year??newDate().getFullYear();letmvalue.month??0;letdvalue.day??1;this.pickDatenewDate(y,m,d);})使用??空值合并运算符为undefined提供合理的默认值。不处理undefined会导致编译错误Argument of type number | undefined is not assignable to parameter of type number。1.3 month 的索引问题DatePickerResult.month返回的是0-based月份索引——即 0 代表一月1 代表二月…11 代表十二月。这与 JavaScript 的Date构造函数中month参数的语义一致。但Date.getMonth()也返回 0-based 的月份所以直接用于new Date(y, m, d)是正确的。然而在显示时需要加 1formatDate(date:Date):string{letm(date.getMonth()1).toString().padStart(2,0);// 显示用 1-based一月显示为 01returny.toString().concat(-).concat(m).concat(-).concat(d);}1.4 onChange 的 deprecated 状态在当前 API 版本中DatePicker 的.onChange()被标记为deprecated。虽然仍然可以使用不会导致编译错误仅为警告但未来版本可能会移除。由于 ArkUI 的 DatePicker 目前没有公开的替代回调 API如onDateChange现阶段继续使用.onChange()是唯一可行的方案。当官方提供替代 API 时迁移也非常简单——只需修改回调方法名。1.5 DatePicker vs CalendarPicker这是读者容易混淆的两个组件维度DatePickerCalendarPicker交互形态滚轮翻选年月日三列日历网格月视图日期范围适合大跨度几十年适合近距选择几个月内空间占用固定高度可嵌入表单需要较大空间展示整月典型场景出生日期、证件有效期日程安排、事件日期查询视觉效果选中行高亮居中选中日期圆圈高亮选择建议需要快速选择跨越几十年的日期如出生年份选到 1980 年→ 用DatePicker滚轮翻选比翻日历快得多需要展示月度视图、查看某天是否有事件 → 用CalendarPicker二、纪念日管理器的整体设计2.1 页面架构DatePickerPage ├── 标题栏 — 纪念日管理 事件计数 ├── 日期选择卡片 │ ├── DatePicker 组件年月日三列滚轮 │ └── 格式化日期显示含星期 ├── 添加事件卡片 │ ├── 事件名称输入框 │ └── 添加事件按钮 └── 事件列表卡片 ├── 筛选标签全部/即将到来/已过去 ├── 事件行名称 日期 倒计时/正计时 删除 └── 空状态提示2.2 数据类设计classAnniversaryEvent{name:string;dateStr:string;// YYYY-MM-DD 格式用于 Date 解析timestamp:number;constructor(name:string,dateStr:string,timestamp:number){this.namename;this.dateStrdateStr;this.timestamptimestamp;}getDaysDiff():number{lettodaynewDate();today.setHours(0,0,0,0);leteventTimenewDate(this.dateStr);eventTime.setHours(0,0,0,0);letdiffMseventTime.getTime()-today.getTime();returnMath.floor(diffMs/(1000*60*60*24));}getDaysLabel():string{letdaysthis.getDaysDiff();if(days0)return还有 .concat(days.toString()).concat( 天);if(days0)return今天;return已过 .concat(Math.abs(days).toString()).concat( 天);}isUpcoming():boolean{returnthis.getDaysDiff()0;}isToday():boolean{returnthis.getDaysDiff()0;}}getDaysDiff()是核心计算方法。它将今天和事件日期的时间部分都归零setHours(0,0,0,0)只保留日期部分进行比较。毫秒差除以1000 * 60 * 60 * 24得到天数差Math.floor()取整。正数表示未来日期“还有 X 天”0 表示今天负数表示过去的日期“已过 X 天”。isUpcoming()判断事件是否即将到来。注意这里用的是 0——“今天也算入即将到来”因为今天的事件确实还没有过去。2.3 日期格式化Demo 中实现了两种格式化方式存储格式formatDateYYYY-MM-DD用于dateStr字段。这个格式可以直接传给new Date()构造函数精确解析。显示格式formatDisplay中文日期 星期例如2026年7月8日 周三。星期通过date.getDay()获取0周日1周一…6周六映射到中文星期名数组。formatDisplay(date:Date):string{letydate.getFullYear();letmdate.getMonth()1;letddate.getDate();letweekDays[日,一,二,三,四,五,六];letwweekDays[date.getDay()];returny.toString().concat(年).concat(m.toString()).concat(月).concat(d.toString()).concat(日 周).concat(w);}2.4 添加事件addEvent():void{letnamethis.eventName.trim();if(name.length0){promptAction.showToast({message:请输入事件名称,duration:1500});return;}letdateStrthis.formatDate(this.pickDate);leteventnewAnniversaryEvent(name,dateStr,Date.parse(dateStr));this.eventsthis.events.slice().concat(event);this.eventName;promptAction.showToast({message:已添加.concat(name),duration:1500});}trim()去空格后检查是否为空将 DatePicker 选中的日期格式化为YYYY-MM-DDDate.parse(dateStr)获取时间戳备用实际计算使用new Date(dateStr)slice().concat(event)创建新数组更新State三、倒计时与正计时3.1 天数计算getDaysDiff():number{lettodaynewDate();today.setHours(0,0,0,0);leteventTimenewDate(this.dateStr);eventTime.setHours(0,0,0,0);letdiffMseventTime.getTime()-today.getTime();returnMath.floor(diffMs/(1000*60*60*24));}setHours(0,0,0,0)是关键——它消除了时间部分对日期比较的影响。如果不归零时间部分同一天的不同时刻如上午 8 点和下午 6 点会得到不同的天数差。3.2 三种状态的视觉呈现状态getDaysDiff()显示文字文字颜色未来 0“还有 X 天”蓝色#1677FF今天 0“今天”绿色#52C41A已过 0“已过 X 天”灰色#BBBBCCText(event.getDaysLabel()).fontSize(14).fontColor(event.isToday()?#52C41A:(event.isUpcoming()?#1677FF:#BBBBCC)).fontWeight(FontWeight.Bold)三种颜色传达三种时间关系——蓝色表示期待即将到来、绿色表示正在发生今天、灰色表示回忆已过去。四、分类筛选4.1 三种视图StateactiveTab:stringall;// all | upcoming | past与上一篇 Checkbox 任务清单的筛选逻辑类似纪念日管理器使用三态筛选activeTab含义筛选条件all全部不过滤upcoming即将到来isUpcoming() true含今天past已过去isUpcoming() false4.2 删除时的筛选感知deleteEvent方法需要感知当前筛选状态——用户可能在即将到来视图下删除某个事件但该事件在完整列表中的索引与筛选后列表的索引不同。Demo 中采用按名称日期匹配删除的策略deleteEvent(index:number):void{letsourceEventsthis.getFilteredEvents();lettargetsourceEvents[index];// 从完整列表中移除匹配的事件letfullNew:AnniversaryEvent[][];letskipNametarget.name;letskipDatetarget.dateStr;for(leti0;ithis.events.length;i){if(this.events[i].nameskipNamethis.events[i].dateStrskipDate){skipName;// 只删除第一个匹配项}else{fullNewfullNew.slice().concat(this.events[i]);}}this.eventsfullNew;}通过匹配name dateStr在完整列表中定位要删除的事件确保在任意筛选视图下都能正确删除。五、交互流程演示5.1 选择日期进入页面DatePicker 显示当前日期。上下滑动年、月、日三列滚轮来改变日期。格式化显示区实时更新——例如选择 2026 年 12 月 25 日显示2026年12月25日 周五。5.2 添加事件在输入框中输入圣诞节点击添加事件。Toast 提示已添加圣诞节。事件列表出现一条记录名称圣诞节、日期2026-12-25、倒计时还有 170 天蓝色。再添加一个已过去的日期——将 DatePicker 调到2026-01-01输入元旦。倒计时显示已过 188 天灰色。5.3 筛选视图点击即将到来标签——列表只显示未来的事件包括今天。元旦已过 188 天被隐藏。点击已过去标签——列表只显示过去的事件。圣诞节被隐藏只看到元旦。点击全部标签——恢复显示所有事件。5.4 删除事件点击某条事件右侧的红色删除按钮。Toast 提示已删除该事件从所有视图中消失。标题栏的事件计数减 1。5.5 今日事件将 DatePicker 调到今天的日期输入一个事件名称点击添加事件。事件列表显示今天标签绿色与其他还有 X 天或已过 X 天形成对比。六、实用扩展建议6.1 重复提醒当前 Demo 中的事件是一次性的——每个事件对应一个具体日期。实际应用通常需要重复提醒每年生日、每月还款日等。可以扩展AnniversaryEvent类增加repeat字段classAnniversaryEvent{repeat:string;// none | yearly | monthly// ...}对于每年重复的事件计算天数差时取下一年的对应日期。6.2 通知提醒结合ohos.notification能力在纪念日到来时推送系统通知。可以在事件添加时计算剩余天数并设置定时提醒。6.3 农历支持中文纪念日场景中农历日期非常常见如春节、中秋节。可以集成农历转换库在 DatePicker 选中的公历日期旁边显示对应的农历日期。七、总结本文通过纪念日管理器这个实战案例全面讲解了 ArkUI DatePicker 日期选择器组件的使用方法。核心知识点包括DatePicker 基础 APIselected默认日期 .onChange()回调获取选中结果DatePickerResult 可选字段处理year/month/day均为number | undefined需用??提供默认值month 索引陷阱DatePickerResult.month为 0-based0一月显示时需 1onChange deprecated 警告当前仍可使用未来需要迁移到新 API日期天数计算setHours(0,0,0,0)归零时间 毫秒差 → 天数整数三种时间状态未来“还有 X 天”/蓝色、今天“今天”/绿色、已过“已过 X 天”/灰色筛选感知删除按name dateStr在完整列表中定位要删除的事件DatePicker vs CalendarPicker交互形态滚轮 vs 日历网格和适用场景的差异日期选择是移动端的核心输入方式之一。DatePicker 的滚轮交互让用户在海量的日期空间中快速定位——对于需要跨越数十年范围的日期选择如出生日期它比日历视图高效得多。配合日期计算和状态管理开发者可以轻松构建纪念日、倒计时、事件规划等各种时间相关功能。