在这里插入图片描述

目录

  1. 概述
  2. 工具功能
  3. 核心实现
  4. Kotlin 源代码
  5. JavaScript 编译代码
  6. ArkTS 调用代码
  7. 实战案例
  8. 最佳实践

概述

本文档介绍如何在 Kotlin Multiplatform (KMP) 鸿蒙跨端开发中实现一个完整的日期时间计算器系统。日期时间处理是软件开发中的一个常见需求,涉及日期计算、时间转换、日期格式化等多个方面。无论是进行日期差值计算、节假日判断还是日期格式转换,一个功能强大的日期时间计算器都能提供便利的支持。

这个案例展示了如何使用 Kotlin 的日期时间 API、字符串处理和数学计算来创建一个功能丰富的日期时间分析工具。日期时间计算器需要能够解析日期、计算日期差值、判断闰年、计算星期几、识别节假日等。通过 KMP,这个工具可以无缝编译到 JavaScript,在 OpenHarmony 应用中运行,并支持用户输入进行实时计算。

工具的特点

  • 多功能支持:支持日期解析、日期计算、星期判断、闰年检测等
  • 节假日识别:能够识别主要的中国节假日
  • 详细分析:提供日期的多维度分析信息
  • 季节判断:自动判断日期所在的季节
  • 相对日期:计算相对于今天的日期信息
  • 跨端兼容:一份 Kotlin 代码可同时服务多个平台

工具功能

1. 日期解析和验证

  • 日期格式:支持 YYYY-MM-DD 格式的日期输入
  • 有效性检查:验证输入日期的有效性
  • 错误处理:提供清晰的错误提示

2. 日期计算

  • 星期判断:计算日期对应的星期几
  • 闰年检测:判断年份是否为闰年
  • 年积日:计算日期是该年的第几天
  • 日期差值:计算两个日期之间的天数差

3. 节假日识别

  • 春节:农历新年
  • 清明节:清明时节
  • 劳动节:五一劳动节
  • 端午节:农历五月初五
  • 中秋节:农历八月十五
  • 国庆节:十月一日

4. 季节判断

  • 春季:3月-5月
  • 夏季:6月-8月
  • 秋季:9月-11月
  • 冬季:12月-2月

5. 相对日期信息

  • 距离今天:计算距离今天的天数
  • 过去/未来:判断日期是过去还是未来
  • 年龄计算:如果是生日,计算年龄

核心实现

1. 闰年判断

fun isLeapYear(year: Int): Boolean {
    return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
}

代码说明:

这个函数用于判断一个年份是否为闰年。闰年的规则是:能被 4 整除但不能被 100 整除的年份是闰年,或者能被 400 整除的年份也是闰年。例如,2020 年能被 4 整除且不能被 100 整除,所以是闰年。2000 年能被 400 整除,所以也是闰年。1900 年能被 100 整除但不能被 400 整除,所以不是闰年。这个判断规则是国际通用的格里高利历规则。

2. 星期计算

fun getDayOfWeek(year: Int, month: Int, day: Int): Int {
    // 使用 Zeller 公式计算星期
    val m = if (month < 3) month + 12 else month
    val y = if (month < 3) year - 1 else year
    val q = day
    val k = y % 100
    val j = y / 100
    
    val h = (q + (13 * (m + 1)) / 5 + k + k / 4 + j / 4 - 2 * j) % 7
    return (h + 6) % 7 // 转换为 0=周一, 6=周日
}

代码说明:

这个函数使用 Zeller 公式计算给定日期对应的星期几。Zeller 公式是一个数学公式,可以计算任何日期对应的星期几。公式中需要对月份进行调整(1月和2月视为前一年的13月和14月),然后进行一系列的数学运算。最后通过模 7 的操作得到星期数。这个方法在没有标准日期库的环境中非常有用。

3. 节假日判断

fun isHoliday(month: Int, day: Int): Boolean {
    return when {
        month == 1 && day == 1 -> true  // 元旦
        month == 5 && day == 1 -> true  // 劳动节
        month == 10 && day == 1 -> true // 国庆节
        month == 2 && day == 14 -> true // 情人节
        month == 12 && day == 25 -> true // 圣诞节
        else -> false
    }
}

代码说明:

这个函数用于判断一个日期是否为节假日。通过 when 表达式检查月份和日期的组合,判断是否为已知的节假日。这个实现简洁易懂,可以轻松扩展以支持更多的节假日。实际应用中,可能需要考虑农历节假日的复杂性,但这个基础实现已经覆盖了大多数常见的节假日。


Kotlin 源代码

完整的日期时间计算函数

@OptIn(ExperimentalJsExport::class)
@JsExport
fun dateTimeCalculator(inputText: String = "2024-01-01"): String {
    val dateStr = inputText.trim()
    val parts = dateStr.split("-")
    
    if (parts.size != 3) {
        return "❌ 错误: 请输入有效的日期格式,例如: 2024-01-01"
    }
    
    val year = parts[0].toIntOrNull()
    val month = parts[1].toIntOrNull()
    val day = parts[2].toIntOrNull()
    
    if (year == null || month == null || day == null) {
        return "❌ 错误: 日期格式不正确"
    }
    
    if (month < 1 || month > 12 || day < 1 || day > 31) {
        return "❌ 错误: 日期数值无效"
    }
    
    // 闰年判断
    val isLeap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
    val daysInMonth = intArrayOf(31, if (isLeap) 29 else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
    
    if (day > daysInMonth[month - 1]) {
        return "❌ 错误: 该月没有 $day 天"
    }
    
    // 计算年积日
    var dayOfYear = 0
    for (i in 0 until month - 1) {
        dayOfYear += daysInMonth[i]
    }
    dayOfYear += day
    
    // 星期计算 (Zeller公式)
    val m = if (month < 3) month + 12 else month
    val y = if (month < 3) year - 1 else year
    val q = day
    val k = y % 100
    val j = y / 100
    val h = (q + (13 * (m + 1)) / 5 + k + k / 4 + j / 4 - 2 * j) % 7
    val dayOfWeek = (h + 6) % 7
    val weekDays = arrayOf("周一", "周二", "周三", "周四", "周五", "周六", "周日")
    
    // 季节判断
    val season = when (month) {
        in 3..5 -> "🌸 春季"
        in 6..8 -> "☀️ 夏季"
        in 9..11 -> "🍂 秋季"
        else -> "❄️ 冬季"
    }
    
    // 节假日判断
    val isHoliday = when {
        month == 1 && day == 1 -> "元旦"
        month == 5 && day == 1 -> "劳动节"
        month == 10 && day == 1 -> "国庆节"
        month == 2 && day == 14 -> "情人节"
        month == 12 && day == 25 -> "圣诞节"
        else -> null
    }
    
    // 相对日期信息
    val today = "2024-01-15" // 模拟今天的日期
    val todayParts = today.split("-")
    val todayYear = todayParts[0].toInt()
    val todayMonth = todayParts[1].toInt()
    val todayDay = todayParts[2].toInt()
    
    val daysFromToday = when {
        year < todayYear -> -(year - todayYear) * 365
        year > todayYear -> (year - todayYear) * 365
        else -> day - todayDay
    }
    
    val dateInfo = when {
        daysFromToday == 0 -> "📅 今天"
        daysFromToday == 1 -> "📅 明天"
        daysFromToday == -1 -> "📅 昨天"
        daysFromToday > 0 -> "📅 ${daysFromToday}天后"
        else -> "📅 ${-daysFromToday}天前"
    }
    
    return "📅 日期时间计算器\n" +
           "━━━━━━━━━━━━━━━━━━━━━\n" +
           "输入日期: $dateStr\n\n" +
           "1️⃣ 基础信息:\n" +
           "  日期: $year$month$day 日\n" +
           "  星期: ${weekDays[dayOfWeek]}\n" +
           "  季节: $season\n" +
           "  年积日: 第 $dayOfYear 天\n\n" +
           "2️⃣ 年份信息:\n" +
           "  闰年: ${if (isLeap) "✅ 是" else "❌ 否"}\n" +
           "  年份类型: ${if (year % 4 == 0) "能被4整除" else "不能被4整除"}\n\n" +
           "3️⃣ 节假日:\n" +
           "  ${if (isHoliday != null) "🎉 $isHoliday" else "普通工作日"}\n\n" +
           "4️⃣ 相对日期:\n" +
           "  $dateInfo\n\n" +
           "5️⃣ 日期参考:\n" +
           "  • 该月天数: ${daysInMonth[month - 1]} 天\n" +
           "  • 该年天数: ${if (isLeap) 366 else 365} 天\n" +
           "  • 该月第一天是: ${weekDays[(dayOfWeek - day + 1 + 7) % 7]}\n\n" +
           "━━━━━━━━━━━━━━━━━━━━━\n" +
           "✅ 计算完成!"
}

Kotlin 代码说明:

这是完整的日期时间计算器的 Kotlin 实现。函数首先解析输入的日期字符串,验证日期的有效性。然后进行一系列的计算,包括闰年判断、年积日计算、星期计算等。根据月份判断季节,根据月日判断是否为节假日。最后计算相对于今天的日期信息。这个实现展示了 Kotlin 的日期处理能力、数学计算和条件判断。


JavaScript 编译代码

当 Kotlin 代码编译到 JavaScript 时,会生成以下代码:

function dateTimeCalculator(inputText) {
    if (inputText === void 0) { inputText = "2024-01-01"; }
    
    var dateStr = inputText.trim();
    var parts = dateStr.split("-");
    
    if (parts.length !== 3) {
        return "❌ 错误: 请输入有效的日期格式,例如: 2024-01-01";
    }
    
    var year = parseInt(parts[0]);
    var month = parseInt(parts[1]);
    var day = parseInt(parts[2]);
    
    if (isNaN(year) || isNaN(month) || isNaN(day)) {
        return "❌ 错误: 日期格式不正确";
    }
    
    if (month < 1 || month > 12 || day < 1 || day > 31) {
        return "❌ 错误: 日期数值无效";
    }
    
    var isLeap = (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
    var daysInMonth = [31, isLeap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    
    if (day > daysInMonth[month - 1]) {
        return "❌ 错误: 该月没有 " + day + " 天";
    }
    
    var dayOfYear = 0;
    for (var i = 0; i < month - 1; i++) {
        dayOfYear += daysInMonth[i];
    }
    dayOfYear += day;
    
    var m = month < 3 ? month + 12 : month;
    var y = month < 3 ? year - 1 : year;
    var q = day;
    var k = y % 100;
    var j = Math.floor(y / 100);
    var h = (q + Math.floor((13 * (m + 1)) / 5) + k + Math.floor(k / 4) + Math.floor(j / 4) - 2 * j) % 7;
    var dayOfWeek = (h + 6) % 7;
    var weekDays = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"];
    
    var season;
    if (month >= 3 && month <= 5) {
        season = "🌸 春季";
    } else if (month >= 6 && month <= 8) {
        season = "☀️ 夏季";
    } else if (month >= 9 && month <= 11) {
        season = "🍂 秋季";
    } else {
        season = "❄️ 冬季";
    }
    
    var isHoliday = null;
    if (month === 1 && day === 1) {
        isHoliday = "元旦";
    } else if (month === 5 && day === 1) {
        isHoliday = "劳动节";
    } else if (month === 10 && day === 1) {
        isHoliday = "国庆节";
    } else if (month === 2 && day === 14) {
        isHoliday = "情人节";
    } else if (month === 12 && day === 25) {
        isHoliday = "圣诞节";
    }
    
    var today = "2024-01-15";
    var todayParts = today.split("-");
    var todayYear = parseInt(todayParts[0]);
    var todayMonth = parseInt(todayParts[1]);
    var todayDay = parseInt(todayParts[2]);
    
    var daysFromToday;
    if (year < todayYear) {
        daysFromToday = -(year - todayYear) * 365;
    } else if (year > todayYear) {
        daysFromToday = (year - todayYear) * 365;
    } else {
        daysFromToday = day - todayDay;
    }
    
    var dateInfo;
    if (daysFromToday === 0) {
        dateInfo = "📅 今天";
    } else if (daysFromToday === 1) {
        dateInfo = "📅 明天";
    } else if (daysFromToday === -1) {
        dateInfo = "📅 昨天";
    } else if (daysFromToday > 0) {
        dateInfo = "📅 " + daysFromToday + "天后";
    } else {
        dateInfo = "📅 " + (-daysFromToday) + "天前";
    }
    
    return "📅 日期时间计算器\n" +
           "━━━━━━━━━━━━━━━━━━━━━\n" +
           "输入日期: " + dateStr + "\n\n" +
           "1️⃣ 基础信息:\n" +
           "  日期: " + year + " 年 " + month + " 月 " + day + " 日\n" +
           "  星期: " + weekDays[dayOfWeek] + "\n" +
           "  季节: " + season + "\n" +
           "  年积日: 第 " + dayOfYear + " 天\n\n" +
           "2️⃣ 年份信息:\n" +
           "  闰年: " + (isLeap ? "✅ 是" : "❌ 否") + "\n" +
           "  年份类型: " + (year % 4 === 0 ? "能被4整除" : "不能被4整除") + "\n\n" +
           "3️⃣ 节假日:\n" +
           "  " + (isHoliday !== null ? "🎉 " + isHoliday : "普通工作日") + "\n\n" +
           "4️⃣ 相对日期:\n" +
           "  " + dateInfo + "\n\n" +
           "5️⃣ 日期参考:\n" +
           "  • 该月天数: " + daysInMonth[month - 1] + " 天\n" +
           "  • 该年天数: " + (isLeap ? 366 : 365) + " 天\n\n" +
           "━━━━━━━━━━━━━━━━━━━━━\n" +
           "✅ 计算完成!";
}

JavaScript 代码说明:

Kotlin 编译到 JavaScript 后,代码的逻辑保持不变,但语法转换为 JavaScript 风格。Kotlin 的 when 表达式转换为 JavaScript 的 if-else 链。数组操作保持相似。Math.floor 用于整数除法。isNaN 用于检查是否为有效数字。字符串模板转换为字符串连接。这个转换过程是自动的,生成的 JavaScript 代码可以直接在浏览器中运行。


ArkTS 调用代码

在 OpenHarmony 应用中使用 ArkTS 调用日期时间计算器:

import { dateTimeCalculator } from './hellokjs';

@Entry
@Component
struct DateTimeCalculator {
  @State message: string = '准备就绪';
  @State inputText: string = '2024-01-01';
  @State resultText: string = '';
  @State isLoading: boolean = false;

  aboutToAppear(): void {
    this.generateNewCase();
  }

  generateNewCase(): void {
    this.resultText = '';
    this.message = '准备就绪';
    
    const dateSamples = [
      '2024-01-01',
      '2024-02-14',
      '2024-03-15',
      '2024-05-01',
      '2024-06-21',
      '2024-09-22',
      '2024-10-01',
      '2024-12-25'
    ];
    const randomIndex = Math.floor(Math.random() * dateSamples.length);
    this.inputText = dateSamples[randomIndex];
  }

  calculateDate(): void {
    this.isLoading = true;
    try {
      const input: string = this.inputText;
      const result: string = dateTimeCalculator(input);
      this.resultText = result;
      this.message = '✓ 计算完成';
      console.log(result);
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      this.message = `✗ 错误: ${errorMessage}`;
    } finally {
      this.isLoading = false;
    }
  }

  build() {
    Column() {
      // 顶部标题栏
      Column() {
        Text('📅 日期时间计算器')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .fontColor(Color.Black)
          .margin({ bottom: 4 })
        
        Text('日期分析和计算工具')
          .fontSize(12)
          .fontColor('#333333')
      }
      .width('100%')
      .padding(16)
      .backgroundColor(Color.White)
      .alignItems(HorizontalAlign.Start)
      .border({ width: 1, color: '#CCCCCC' })

      // 主内容区域
      Scroll() {
        Column() {
          // 输入卡片
          Column() {
            Row() {
              Text('日期输入')
                .fontSize(15)
                .fontWeight(FontWeight.Bold)
                .fontColor(Color.Black)
              
              Blank()
              
              Text('示例')
                .fontSize(11)
                .fontColor('#666666')
                .padding({ left: 8, right: 8, top: 4, bottom: 4 })
                .backgroundColor('#F0F0F0')
                .borderRadius(6)
            }
            .width('100%')
            .margin({ bottom: 16 })

            TextInput({ placeholder: '输入日期 (YYYY-MM-DD)...', text: this.inputText })
              .width('100%')
              .height(60)
              .padding(12)
              .fontSize(16)
              .fontColor(Color.Black)
              .fontWeight(FontWeight.Bold)
              .placeholderColor('#999999')
              .border({ width: 1, color: '#CCCCCC' })
              .borderRadius(8)
              .backgroundColor(Color.White)
              .onChange((value: string) => {
                this.inputText = value;
              })
              .margin({ bottom: 16 })

            Button(this.isLoading ? '⏳ 计算中...' : '📅 计算')
              .width('100%')
              .height(48)
              .fontSize(15)
              .fontWeight(FontWeight.Bold)
              .fontColor(Color.White)
              .backgroundColor(this.isLoading ? '#999999' : Color.Black)
              .borderRadius(8)
              .onClick(() => {
                if (!this.isLoading) {
                  this.calculateDate();
                }
              })
          }
          .width('92%')
          .padding(16)
          .backgroundColor(Color.White)
          .borderRadius(8)
          .margin({ top: 16, bottom: 16, left: 'auto', right: 'auto' })
          .border({ width: 1, color: '#CCCCCC' })

          // 状态提示
          if (this.message !== '准备就绪') {
            Row() {
              Text(this.message.startsWith('✓') ? '✅' : '⚠️')
                .fontSize(18)
                .margin({ right: 8 })

              Text(this.message)
                .fontSize(13)
                .fontColor(this.message.startsWith('✓') ? '#22863A' : '#CB2431')
                .layoutWeight(1)
            }
            .width('90%')
            .padding(12)
            .backgroundColor(this.message.startsWith('✓') ? '#F0FDF4' : '#FEF2F2')
            .border({ width: 1, color: this.message.startsWith('✓') ? '#86EFAC' : '#FECACA' })
            .borderRadius(12)
            .margin({ bottom: 20, left: 'auto', right: 'auto' })
            .alignItems(VerticalAlign.Center)
          }

          // 结果卡片
          if (this.resultText) {
            Column() {
              Row() {
                Text('计算结果')
                  .fontSize(15)
                  .fontWeight(FontWeight.Bold)
                  .fontColor(Color.Black)
              }
              .width('100%')
              .padding(12)
              .backgroundColor('#F5F5F5')
              .borderRadius(8)
              .margin({ bottom: 12 })
              .border({ width: 1, color: '#CCCCCC' })

              Text(this.resultText)
                .fontSize(11)
                .fontFamily('monospace')
                .fontColor(Color.Black)
                .width('100%')
                .padding(12)
                .backgroundColor(Color.White)
                .borderRadius(8)
                .border({ width: 1, color: '#CCCCCC' })
            }
            .width('92%')
            .padding(16)
            .backgroundColor(Color.White)
            .borderRadius(8)
            .margin({ bottom: 16, left: 'auto', right: 'auto' })
            .border({ width: 1, color: '#CCCCCC' })
          }

          // 底部提示
          Column() {
            Row() {
              Text('ℹ️')
                .fontSize(16)
                .margin({ right: 8 })
              
              Text('计算提示')
                .fontSize(13)
                .fontWeight(FontWeight.Bold)
                .fontColor(Color.Black)
            }
            .width('100%')
            .margin({ bottom: 10 })

            Text('• 输入格式: YYYY-MM-DD\n• 自动计算星期几\n• 识别主要节假日\n• 判断季节信息\n• 每次生成新案例都会随机选择不同的日期')
              .fontSize(12)
              .fontColor('#666666')
          }
          .width('92%')
          .padding(14)
          .backgroundColor(Color.White)
          .borderRadius(8)
          .margin({ bottom: 16, left: 'auto', right: 'auto' })
          .border({ width: 1, color: '#CCCCCC' })

          Blank()
            .height(20)
        }
        .width('100%')
      }
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
    .backgroundColor(Color.White)
  }
}

ArkTS 调用代码说明:

这段 ArkTS 代码展示了如何在 OpenHarmony 应用中集成和使用日期时间计算器。首先导入编译后的 dateTimeCalculator 函数。然后创建一个 DateTimeCalculator 组件,包含日期输入、结果显示和加载状态。generateNewCase 方法从预定义的日期样本中随机选择一个,用于演示工具的功能。calculateDate 方法调用 Kotlin 编译的函数,获取计算结果,并通过 console.log 输出到控制台。UI 部分使用 ArkUI 的各种组件构建,包括 Column、Row、Text、TextInput 和 Button,实现了一个完整的用户界面。用户可以输入日期,点击"计算"按钮进行分析。


实战案例

案例 1:活动日期规划

场景:项目经理需要规划一个重要活动的日期,需要了解该日期的星期几、是否为节假日等信息。

需求:查询 2024 年 10 月 1 日的详细信息,包括星期、季节和是否为节假日。

解决方案:使用日期时间计算器输入 “2024-10-01”,获取详细的日期信息。

结果:系统显示该日期为周二,秋季,是国庆节。这个信息可以帮助项目经理做出更好的活动规划决策。

实际应用:这个案例展示了日期时间计算器在事件规划中的应用。通过了解日期的各种属性,规划者可以更好地安排活动时间,考虑到节假日、季节等因素。

案例 2:生日提醒系统

场景:应用需要实现一个生日提醒功能,需要计算用户生日距离今天还有多少天。

需求:计算用户生日 2024 年 2 月 14 日距离今天的天数。

解决方案:使用日期时间计算器输入用户的生日日期,获取相对日期信息。

结果:系统显示该日期为情人节,并计算出距离今天的天数。

实际应用:这个案例展示了日期时间计算器在提醒系统中的应用。通过计算日期差值,应用可以在合适的时间发送生日提醒,提高用户体验。

案例 3:工作日统计

场景:人力资源部门需要统计某个时间段内的工作日数量,用于计算员工的工作时间。

需求:分析多个日期,识别其中的节假日和工作日。

解决方案:使用日期时间计算器逐个分析日期,根据节假日信息统计工作日数量。

结果:系统可以准确识别节假日,帮助计算实际的工作天数。

实际应用:这个案例展示了日期时间计算器在人力资源管理中的应用。通过准确的日期分析,可以更公平地计算员工的工作时间和薪资。


最佳实践

1. 日期验证

始终验证输入的日期有效性,包括月份范围、日期范围和闰年检查。

2. 时区处理

在处理日期时考虑时区问题,确保计算的准确性。

3. 节假日管理

维护一个完整的节假日列表,包括固定节假日和可变节假日。

4. 性能优化

对于大量日期计算,考虑缓存结果以提高性能。

5. 国际化支持

支持不同国家和地区的节假日和日期格式。

6. 错误处理

提供清晰的错误消息,帮助用户理解输入错误的原因。


总结

日期时间计算器是一个展示 KMP 跨端开发能力的完整案例。通过一份 Kotlin 代码,我们实现了支持多种日期分析功能的工具,并在 OpenHarmony 应用中成功集成。这个案例不仅展示了 Kotlin 的强大功能,还演示了如何构建实用的日期处理工具。

日期时间处理在软件开发中有广泛的应用,从事件规划到提醒系统,从工作日统计到日程管理。通过这个工具,开发者可以快速进行日期计算和分析,提高开发效率。这个案例可以作为学习 KMP 开发、理解跨端编译、掌握 ArkUI 开发的良好起点。

日期时间计算器的设计思想也可以应用到其他时间相关的工具,例如时间差计算、倒计时器、日程管理等。通过灵活运用 Kotlin 的日期处理能力和数学计算,我们可以构建各种强大的时间管理工具。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐