一、前言

在上一篇文章《用 ArkTS 实现一个生肖查询小工具》中,我们从零搭建了一个完整的交互页面,学习了 @StateColumnTextInputButtonText 这五个最核心的 API。这篇文章延续同一个风格,用日期查星期这个小工具,带大家掌握 ArkTS 中日期处理的完整流程。

日期处理是移动端开发中的高频场景:

  • 日历 App 需要根据日期计算星期
  • 备忘录 / 待办需要显示创建时间
  • 聊天记录需要展示"今天 / 昨天 / 前天 / 周几"这样的友好格式
  • 统计页面需要按周聚合数据

本篇文章将围绕 Date 对象的解析、校验、格式化,结合 ArkTS 的声明式 UI,完成一个开箱即用的日期查询组件。


二、完整代码展示

先上完整代码,建立整体印象:

@Entry
@Component
struct Index {
  @State inputDate: string = ""
  @State weekResult: string = "请输入日期 格式:2026-06-14"

  checkWeek() {
    let dateStr = this.inputDate
    let date = new Date(dateStr)
    if (isNaN(date.getTime())) {
      this.weekResult = "日期格式错误,请重输"
      return
    }
    const weekArr = ["周日","周一","周二","周三","周四","周五","周六"]
    let week = weekArr[date.getDay()]
    this.weekResult = `${dateStr} 是 ${week}`
  }

  build() {
    Column({ space: 25 }) {
      TextInput({text: this.inputDate, placeholder: "例如:2026-06-14"})
        .width("85%")
        .height(50)
        .onChange(val => this.inputDate = val)

      Button("查询星期")
        .width(130)
        .onClick(() => this.checkWeek())

      Text(this.weekResult)
        .fontSize(20)
        .textAlign(TextAlign.Center)
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
    .padding(15)
  }
}
typescript
@State inputDate: string = ""
@State weekResult: string = "请输入日期 格式:2026-06-14"

和上一期的生肖查询完全一致的模式:

  • inputDate —— 绑定 TextInput 的输入内容,类型为 string
  • weekResult —— 展示计算结果,初始值为提示文字

这里有一个值得注意的设计细节:初始值就是占位说明。用户还没操作时,结果区域显示的文本直接告诉用户该做什么、格式是什么样的。这种设计比空白区域更友好,减少了用户的学习成本。

从架构角度看,@State 的使用模式可以总结为:

typescript checkWeek() { let dateStr = this.inputDate let date = new Date(dateStr) if (isNaN(date.getTime())) { this.weekResult = "日期格式错误,请重输" return } const weekArr = ["周日","周一","周二","周三","周四","周五","周六"] let week = weekArr[date.getDay()] this.weekResult = ${dateStr} 是 ${week} }


这段代码做了三件事:**解析 → 校验 → 计算输出**。

#### 4.2.1 日期解析:new Date(dateStr)

JavaScript / TypeScript 的 `Date` 构造函数支持多种字符串格式:

| 输入格式 | 示例 | 是否有效 | 说明 |
|----------|------|:--------:|------|
| YYYY-MM-DD | `2026-06-14` | ✅ | 标准 ISO 8601 格式 |
| YYYY/MM/DD | `2026/06/14` | ✅ 大部分浏览器支持 | 兼容性良好 |
| YYYY.MM.DD | `2026.06.14` | ❌ 部分引擎不支持 | 不推荐 |
| YYYYMMDD | `20260614` | ❌ | 会被当作数字 |
| MM/DD/YYYY | `06/14/2026` | ✅ 但易混淆 | 非中国习惯格式 |

最简单的方案就是强制要求 `YYYY-MM-DD` 格式,这也是最安全、最通用的写法。

值得注意的是,`new Date()` 对带连接符的 ISO 字符串解析时会将其**当作 UTC 时间**,而不是本地时间。但对我们这种只需要获取日期的场景来说,影响可以忽略——因为无论时区如何,`getDay()` 得到的星期几是一致的。

#### 4.2.2 日期校验:isNaN(date.getTime())

在 JavaScript/TypeScript 中,`new Date('abc')` 并不会抛出异常,而是创建一个**无效的 Date 对象**。无效的 Date 对象仍然有方法,但调用 `getTime()` 会返回 `NaN`。

所以标准做法是:

```typescript
if (isNaN(date.getTime())) {
  // 日期无效
}

这是 JavaScript 中校验日期的标准惯用法,ArkTS 完全沿用这个模式。

常见几种校验方式的对比:

方式 代码 是否能检测"2026-13-01" 是否能检测"2026-02-30"
isNaN(date.getTime()) ✅ 一检测就死 ✅ 月份超了变 NaN ✅ 天数超了变 NaN
正则匹配格式 复杂且不全 ❌ 放过 ❌ 放过
手动解析年月日 啰嗦 需要自己实现

所以 isNaN(date.getTime()) 是最简洁且有效的兜底方案。

2.1 星期映射:getDay() 与中文星期
const weekArr = ["周日","周一","周二","周三","周四","周五","周六"]
let week = weekArr[date.getDay()]
typescript
const WEEK_DAYS = ["周日","周一","周二","周三","周四","周五","周六"] as const

as const 让 TypeScript 知道这个数组的元素类型是字面量联合类型,类型更加精确。

2.1.1 模板字符串:${}
this.weekResult = `${dateStr} 是 ${week}`

ArkTS 继承了 TypeScript 的模板字符串能力,用反引号和 ${} 语法做字符串插值。这比字符串拼接更清晰:

// 旧式拼接(可读性差)
this.weekResult = dateStr + " 是 " + week

// 模板字符串(清晰直接)
this.weekResult = `${dateStr} 是 ${week}`

模板字符串还支持多行和表达式:

this.weekResult = `日期:${dateStr}
星期:${week}
距离今天还有:${Math.ceil((date.getTime() - Date.now()) / 86400000)} 天`

2.2  建:build() 方法

build() {
  Column({ space: 25 }) {
    TextInput({text: this.inputDate, placeholder: "例如:2026-06-14"})
      .width("85%")
      .height(50)
      .onChange(val => this.inputDate = val)

    Button("查询星期")
      .width(130)
      .onClick(() => this.checkWeek())

    Text(this.weekResult)
      .fontSize(20)
      .textAlign(TextAlign.Center)
  }
  .width("100%")
  .height("100%")
  .justifyContent(FlexAlign.Center)
  .padding(15)
}
2.2.1 Column 属性
  • { space: 25 } —— 比上一期少 5,子组件间距更紧凑
  • .padding(15) —— 比上一期少 5,整体布局更紧凑

这种微调反映了 UI 开发的常态——没有标准答案,只有适合场景的选择。间距大小取决于页面的整体视觉风格和信息密度。

2.2.2 TextInput 属性
TextInput({text: this.inputDate, placeholder: "例如:2026-06-14"})
  .width("85%")
  .height(50)
  .onChange(val => this.inputDate = val)

这次 placeholder 写的是具体示例:"例如:2026-06-14",比上一期"输入出生年份"更具体。这是有意为之——日期格式比年份更复杂,用户可能不知道应该输入什么格式,直接给范例可以大大降低出错率。

这种设计模式叫 placeholder 即模板——占位符不再是空泛的说明,而是直接展示一个可参考的样例。

2.2.3 Button 属性
Button("查询星期")
  .width(130)
  .onClick(() => this.checkWeek())

和上一期几乎一致,区别是宽度设为 130(上期为 120)。因为"查询星期"比"查询生肖"多了一个字,稍微放宽了一些。ArkTS 的 Button 文字会居中显示,宽度不够时文字会被截断,所以需要根据文字长度调整。

2.2.4 Text 属性
Text(this.weekResult)
  .fontSize(20)
  .textAlign(TextAlign.Center)

新增了 .textAlign(TextAlign.Center)。因为结果字符串长度不固定:

  • 初始状态:"请输入日期 格式:2026-06-14"(较长,17 字)
  • 正常结果:"2026-06-14 是 周日"(适中)
  • 错误提示:"日期格式错误,请重输"(9 字)

居中显示可以保证不同长度的文本在视觉上保持一致的对齐方式。

TextAlign 枚举值:

效果
Start 左对齐(默认)
Center 居中对齐
End 右对齐

三、与上期生肖查询的对比

把两期的代码放在一起对比,能看出 ArkTS 组件的通用模式:

维度 生肖查询 日期查星期
@State 数量 2 个 (year, zodiac) 2 个 (inputDate, weekResult)
UI 组件 TextInput + Button + Text TextInput + Button + Text
布局容器 Column Column
核心方法 getZodiac() checkWeek()
核心逻辑 (year-4)%12 索引数组 date.getDay() 索引数组
校验方式 isNaN(parseInt(...)) isNaN(date.getTime())
代码行数 38 行 30 行
间距 space: 30 space: 25
内边距 padding: 20 padding: 15

这揭示了一个重要规律:ArkTS 的交互组件具有非常固定的模板结构。在学习新组件时,你只需要关注三件事:

  1. 状态定义 —— 需要几个 @State?类型是什么?
  2. 业务方法 —— 核心计算逻辑怎么写?边界情况怎么处理?
  3. UI 编排 —— 哪些组件?怎么排列?样式参数怎么调?

一旦掌握了这个模板,无论是写生肖查询、日期查星期、BMI 计算器、单位换算、还是密码生成器,本质上都是换不同的业务逻辑而已。


四、扩展与进阶

基础版本比较简单,下面给出几个有价值的扩展方向。

4.1 输入实时校验

用户输入过程中,可以实时检查日期是否合法,而不是等到点击按钮才校验:

@State inputError: string = ""

onDateChange(val: string) {
  this.inputDate = val
  if (val.length === 10) {  // YYYY-MM-DD 固定 10 字符
    let date = new Date(val)
    if (isNaN(date.getTime())) {
      this.inputError = "日期格式错误"
    } else {
      this.inputError = ""
    }
  } else {
    this.inputError = ""
  }
}

UI 中增加错误提示:

Text(this.inputError)
  .fontSize(14)
  .fontColor("#e74c3c")
  .visible(this.inputError !== "")

这种"输入即校验"的模式,在表单类页面中非常常见——用户不需要等点击提交才发现问题,在输入过程中就能获得即时反馈。

4.2 支持多种日期格式

很多用户输入日期时不会严格遵守 YYYY-MM-DD 格式,可能输入 202606142026/06/142026.06.14。可以加一个格式化预处理:

normalizeDate(input: string): string {
  // 去掉所有非数字字符
  const digits = input.replace(/\D/g, "")
  if (digits.length !== 8) return input // 不是有效日期长度
  // 格式化为 YYYY-MM-DD
  const year = digits.substring(0, 4)
  const month = digits.substring(4, 6)
  const day = digits.substring(6, 8)
  return `${year}-${month}-${day}`
}

然后在 checkWeek() 中调用:

checkWeek() {
  let dateStr = this.normalizeDate(this.inputDate)
  // ... 后续不变 ...
}

这样用户输入 20260614 后,自动被转为 2026-06-14,体验更加友好。

4.3 显示更多日期信息

查星期只是一个开始,可以扩展显示更多信息:

checkWeek() {
  let dateStr = this.inputDate
  let date = new Date(dateStr)
  if (isNaN(date.getTime())) {
    this.weekResult = "日期格式错误,请重输"
    return
  }

  const weekArr = ["周日","周一","周二","周三","周四","周五","周六"]
  const week = weekArr[date.getDay()]

  // 额外信息
  const today = new Date()
  const diffDays = Math.ceil((date.getTime() - today.getTime()) / 86400000)
  const isToday = date.toDateString() === today.toDateString()
  const isPast = diffDays < 0
  const isFuture = diffDays > 0

  let extra = ""
  if (isToday) {
    extra = "(就是今天!)"
  } else if (isPast && diffDays > -7) {
    extra = `(${-diffDays} 天前)`
  } else if (isFuture && diffDays < 7) {
    extra = `(${diffDays} 天后)`
  } else if (isPast) {
    extra = `(${-diffDays} 天前)`
  } else {
    extra = `(${diffDays} 天后)`
  }

  this.weekResult = `${dateStr} 是 ${week} ${extra}`
}

这样用户输入一个日期后,不仅知道星期几,还能直观地知道离今天有多远。

4.4 农历日期显示(扩展思路)

如果需要同时显示农历,可以引入一个农历转换库。不过 ArkTS 目前没有内置的农历支持,需要自己实现或者引入第三方计算表。

最简方案:准备一段 1900-2100 年的农历数据表(通过 codegen 生成):

// 简化版:只做月日映射
const LUNAR_MONTH_NAMES = ["正","二","三","四","五","六","七","八","九","十","冬","腊"]
const LUNAR_DAY_NAMES = [
  "初一","初二","初三","初四","初五","初六","初七","初八","初九","初十",
  "十一","十二","十三","十四","十五","十六","十七","十八","十九","二十",
  "廿一","廿二","廿三","廿四","廿五","廿六","廿七","廿八","廿九","三十"
]
// 配合每年的农历偏移数据表使用

实现完整的农历查算是比较复杂的,这里仅作思路提示,具体实现不展开。

4.5 日期选择器替代输入框

ArkTS 提供了原生的 DatePicker 组件,可以替代手动输入:

@State selectedDate: Date = new Date()
@State dateString: string = ""

build() {
  Column({ space: 25 }) {
    DatePicker({
      start: new Date("1900-01-01"),
      end: new Date("2100-12-31"),
      selected: this.selectedDate
    })
    .onChange((date) => {
      this.selectedDate = date
      this.dateString = this.formatDate(date)
      this.checkWeekWithDate(date)
    })

    Button("查询星期")
      .width(130)
      .onClick(() => this.checkWeekWithDate(this.selectedDate))

    Text(this.weekResult)
      .fontSize(20)
  }
}
typescript
new Date("2026-06-14")

在 ArkTS 运行时(方舟运行时)中,这种行为是标准化的。但在 Web 端开发时,不同浏览器对 Date 构造函数的参数解析存在细微差异。ArkTS 的方舟运行时遵循 ECMAScript 标准,表现为:

  • YYYY-MM-DD 格式被解析为UTC 时间 00:00:00
  • YYYY/MM/DD 格式被解析为本地时间 00:00:00

这种差异对于 getDay() 计算星期没有影响,但如果后续要计算时间差(如 "距离今天还有 X 天"),就需要考虑时区问题。

修复方案:显式指定本地时间构造:

function createLocalDate(dateStr: string): Date {
  const parts = dateStr.split("-")
  return new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]))
}

这里的 parseInt(parts[1]) - 1 是因为 JavaScript 的月份是从 0 开始计数的(0=一月,11=十二月),这是 JavaScript 设计中常被诟病的坑。

4.6空字符串处理

当用户清空输入框时,this.inputDate 变成空字符串,new Date("") 会返回一个有效日期(有些浏览器解析为 Invalid Date,但部分引擎中 new Date("") 的行为不一致)。

所以建议在 checkWeek() 方法开头增加空字符串检查:

if (this.inputDate.trim() === "") {
  this.weekResult = "请输入日期"
  return
}

各个引擎对 new Date("") 的行为:

引擎 new Date("") 结果 isNaN(date.getTime())
V8 (Chrome/Edge) Invalid Date true (安全)
SpiderMonkey (Firefox) Invalid Date true (安全)
JavaScriptCore (Safari) Invalid Date true (安全)
方舟运行时 Invalid Date true (安全)

从表格看,主流引擎都会把空字符串解析为无效日期,所以即使不加空字符串检查,也能被 isNaN 捕获。但显式检查的代码意图更清晰,可读性更好。

4.7日期格式未尾的空格

用户在输入时可能在末尾不小心输入了空格,比如 2026-06-14

处理方式:

let dateStr = this.inputDate.trim()  // 去掉首尾空格
typescript
// 按周一开始的排列
const weekArrMonStart = ["周一","周二","周三","周四","周五","周六","周日"]
// date.getDay() 返回 0=周日, 1=周一, ..., 6=周六
// 映射:周日→周日(索引6),周一→周一(索引0),... 周六→周六(索引5)
function getWeekIndex(day: number): number {
  return day === 0 ? 6 : day - 1  // 周日映射到6,其余减1
}
let week = weekArrMonStart[getWeekIndex(date.getDay())]

两种排列方式各有各的用户习惯,建议提供切换选项,或者在页面中同时显示"周日"和"一周第X天"。


五、单元测试思路

虽然 ArkTS 的单元测试框架还在演进中,但我们可以从纯逻辑层面拆出可测试的函数:

// 将纯逻辑提取为独立函数(不依赖 @State,不依赖 UI)
export function getWeekdayString(dateStr: string): string {
  const date = new Date(dateStr)
  if (isNaN(date.getTime())) {
    return "日期格式错误,请重输"
  }
  const weekArr = ["周日","周一","周二","周三","周四","周五","周六"]
  const week = weekArr[date.getDay()]
  return `${dateStr} 是 ${week}`
}

然后用常规的 JavaScript 测试框架就可以测试:

// 测试用例示例
describe("getWeekdayString", () => {
  test("正常日期返回正确星期", () => {
    expect(getWeekdayString("2026-06-14")).toBe("2026-06-14 是 周日")
  })

  test("无效日期返回错误提示", () => {
    expect(getWeekdayString("abc")).toBe("日期格式错误,请重输")
  })

  test("闰年日期正常", () => {
    expect(getWeekdayString("2024-02-29")).toBe("2024-02-29 是 周四")
  })

  test("空字符串返回错误", () => {
    expect(getWeekdayString("")).toBe("日期格式错误,请重输")
  })

  test("跨年边界", () => {
    // 2026-01-01 是周四
    expect(getWeekdayString("2026-01-01")).toBe("2026-01-01 是 周四")
  })
})

将纯逻辑从 UI 组件中剥离出来,是 ArkTS 应用架构中值得养成的习惯。它让代码更容易测试、更容易复用、也更容易理解。


六、总结

6.1 本期新增知识点

通过日期查星期这个项目,除了巩固上期学到的 @StateColumnTextInputButtonText 之外,新增了以下知识点:

  1. Date 对象处理:构造、解析、校验、getDay() 方法
  2. isNaN() 校验模式:不是校验数字,而是校验 Date 是否有效
  3. textAlign 属性:控制文本对齐方式
  4. 日期格式化:标准 ISO 格式的使用
  5. 边界处理扩展:空字符串、空格、非法格式等场景

6.2 从代码量看学习曲线

项目 代码行数 知识点
Hello World 15 @Entry, @Component, build, Text
生肖查询 38 @State, TextInput, Button, 数组索引, parseInt
日期查星期 30 Date对象, getDay, isNaN校验, textAlign

代码量不大,但每一步都在夯实对 ArkTS 核心机制的理解。掌握这两个小项目的基础,已经可以写出大部分简单交互页面了。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐