在这里插入图片描述

在排班管理、会议室预定、资源使用监控等场景中,我们经常需要处理一批可能重叠的时间段:
这些时间段合并后,一共占用了多长时间?最终的占用区间有哪些?

本案例基于 Kotlin Multiplatform(KMP)与 OpenHarmony,实现了一个「区间合并与时间占用统计」工具:

  • 支持多条时间区间输入(如 09:00-10:3010:15-11:00);
  • 自动将重叠或相接的区间合并;
  • 统计合并后的总占用时长和每个合并区间的长度;
  • 通过 ArkTS 单页面板可视化展示计算结果。

一、问题背景与应用场景

典型应用场景包括:

  1. 会议室占用分析
    多个会议预约时间段可能重叠,想知道一天中会议室总共被占用了多久。

  2. 排班与值班时段统计
    多个人的工作时间段交织,需要统计某个岗位「有人值守」的总时长。

  3. 资源使用时间轴分析
    服务器 CPU 高负载时间段、带宽峰值时间段等合并后,统计总体资源「忙碌时间」。

  4. 用户在线时长统计
    用户多次上下线,合并在线区间,计算总在线时长。

本质上,这类问题都可以抽象为:

给定若干闭开区间(或闭区间),将其中重叠或相接的部分合并,并统计合并后区间的总长度。


二、Kotlin 区间合并与统计引擎

1. 输入格式设计

为与现有案例保持一致,本案例使用简单的键值形式输入,每行描述一个时间段:

SLOT-A:09:00-10:30
SLOT-B:10:15-11:00
SLOT-C:13:00-14:15

也支持使用 |; 作为分隔符,例如:

SLOT-A:09:00-10:30|SLOT-B:10:15-11:00|SLOT-C:13:00-14:15

解析后,每一条记录会被转换为:

  • 名称:SLOT-A 等,仅用于展示;
  • 起始时间 / 结束时间:转换为自凌晨 00:00 起的分钟数。

2. 数据结构与解析实现

App.kt 中,我们定义了时间段数据结构:

private data class TimeSlot(
    val name: String,
    val start: Int, // minutes since 00:00
    val end: Int
)

对应的解析函数如下:

private fun parseTimeIntervals(raw: String): List<TimeSlot> {
    val result = mutableListOf<TimeSlot>()
    val lines = raw.split("\n", "|", ";")
        .map { it.trim() }
        .filter { it.isNotEmpty() }

    for (line in lines) {
        val colonIndex = line.indexOf(':')
        if (colonIndex <= 0) continue
        val name = line.substring(0, colonIndex).trim()
        val rangePart = line.substring(colonIndex + 1).trim()
        val dashIndex = rangePart.indexOf('-')
        if (dashIndex <= 0) continue
        val startStr = rangePart.substring(0, dashIndex).trim()
        val endStr = rangePart.substring(dashIndex + 1).trim()

        val startMinutes = parseTimeToMinutes(startStr) ?: continue
        val endMinutes = parseTimeToMinutes(endStr) ?: continue
        if (endMinutes <= startMinutes) continue

        result += TimeSlot(name, startMinutes, endMinutes)
    }
    return result
}

private fun parseTimeToMinutes(time: String): Int? {
    // 支持 HH:MM 或 H:MM
    val parts = time.split(":")
    if (parts.size != 2) return null
    val h = parts[0].toIntOrNull() ?: return null
    val m = parts[1].toIntOrNull() ?: return null
    if (h !in 0..23 || m !in 0..59) return null
    return h * 60 + m
}

说明

  • 严格校验时间格式与范围(小时 0–23,分钟 0–59);
  • 丢弃结束时间不大于起始时间的非法区间;
  • 支持多种分隔符,方便从日志 / 配置中直接粘贴。

3. 区间合并算法

区间合并采用经典的「先排序,后扫描」策略:

private fun mergeIntervals(sorted: List<TimeSlot>): List<TimeSlot> {
    if (sorted.isEmpty()) return emptyList()
    val merged = mutableListOf<TimeSlot>()
    var currentStart = sorted[0].start
    var currentEnd = sorted[0].end

    for (i in 1 until sorted.size) {
        val slot = sorted[i]
        if (slot.start <= currentEnd) {
            // 重叠或相接,扩展区间
            if (slot.end > currentEnd) {
                currentEnd = slot.end
            }
        } else {
            merged += TimeSlot(name = "MERGED", start = currentStart, end = currentEnd)
            currentStart = slot.start
            currentEnd = slot.end
        }
    }
    merged += TimeSlot(name = "MERGED", start = currentStart, end = currentEnd)
    return merged
}

步骤说明

  1. 按起始时间对所有区间排序;
  2. 维护当前合并区间 [currentStart, currentEnd)
  3. 依次扫描后续区间:
    • 若新区间起点 <= currentEnd,说明有重叠或紧接,扩展 currentEnd
    • 否则,将当前合并区间加入结果列表,重新开始新的合并区间。

复杂度

  • 排序:\( O(n \log n) \);
  • 单次线性扫描:\( O(n) \);
  • 总体时间复杂度:\( O(n \log n) \),空间复杂度 \( O(n) \)。

4. 分析入口:intervalMergeAnalyzer

核心分析入口函数如下(已在 App.kt 中通过 @JsExport 暴露):

@JsExport
fun intervalMergeAnalyzer(inputData: String): String {
    val sanitized = inputData.trim()
    if (sanitized.isEmpty()) {
        return "❌ 输入为空,请按 SLOT-A:09:00-10:30 形式提供时间区间"
    }

    val intervals = parseTimeIntervals(sanitized)
    if (intervals.isEmpty()) {
        return "❌ 未解析到任何有效时间区间,请检查格式是否为 NAME:HH:MM-HH:MM"
    }

    val sorted = intervals.sortedBy { it.start }
    val merged = mergeIntervals(sorted)

    val totalMinutes = merged.sumOf { it.end - it.start }
    val builder = StringBuilder()

    builder.appendLine("⏱ 区间合并与时间占用统计报告")
    builder.appendLine("━━━━━━━━━━━━━━━━━━━━━━━━━━")
    builder.appendLine("原始区间数量: ${intervals.size}")
    builder.appendLine("合并后区间数量: ${merged.size}")
    builder.appendLine("总占用时长: ${totalMinutes} 分钟 (~${round2(totalMinutes / 60.0)} 小时)")
    builder.appendLine()

    builder.appendLine("📋 合并前区间列表(按起始时间排序)")
    sorted.forEach { slot ->
        builder.appendLine("  ${slot.name}: ${formatMinutes(slot.start)} - ${formatMinutes(slot.end)}")
    }
    builder.appendLine()

    builder.appendLine("✅ 合并后区间列表")
    merged.forEachIndexed { index, slot ->
        val duration = slot.end - slot.start
        builder.appendLine("  区间 ${index + 1}: ${formatMinutes(slot.start)} - ${formatMinutes(slot.end)} (${duration} 分钟)")
    }

    return builder.toString().trim()
}

格式化时间的辅助函数:

private fun formatMinutes(totalMinutes: Int): String {
    val h = totalMinutes / 60
    val m = totalMinutes % 60
    val hh = if (h < 10) "0$h" else h.toString()
    val mm = if (m < 10) "0$m" else m.toString()
    return "$hh:$mm"
}

三、JavaScript / ArkTS 集成

通过 @JsExportintervalMergeAnalyzer 已经出现在 hellokjs.js 中,并在 hellokjs.d.ts 声明:

export declare function intervalMergeAnalyzer(inputData: string): string;

在 ArkTS 中使用时,直接导入:

import { intervalMergeAnalyzer } from './hellokjs';

// ...
this.result = intervalMergeAnalyzer(this.inputData);

其中 inputData 为包含多个 NAME:HH:MM-HH:MM 的文本。


四、ArkTS 单页面板设计建议

你可以像前几个案例一样,将首页 Index 页面切换为「区间合并与时间占用统计」主题:

  • 顶部标题:

    • 中文:「区间合并与时间占用统计」
    • 英文副标题:「Interval Merge & Time Utilization」
  • 输入卡片:

    • 标题:「时间区间输入」;

    • 右侧角标显示当前解析到的区间数量;

    • TextArea 中展示示例:

      SLOT-A:09:00-10:30
      SLOT-B:10:15-11:00
      SLOT-C:13:00-14:15
      
  • 操作区:

    • 「▶ 运行区间合并分析」按钮;
    • 「↻ 恢复示例」按钮。
  • 结果区:

    • 上半部分列出原始区间(排序后);
    • 下半部分列出合并区间及各自时长,总占用时长汇总。

配合你现有的深蓝卡片风格,可以很自然地融入整个算法案例系列。


五、应用场景回顾

  1. 会议室 / 资源占用统计
    快速得到一天中资源实际被占用的总时长与合并后时间段。

  2. 排班合并与可视化
    将多位员工的排班时间区间合并,查看岗位整体覆盖情况。

  3. 监控指标高负载时间段总结
    将 CPU 使用率超过阈值的时间片合并,生成高负载总时长与区间报告。

  4. 多次登录会话的在线时长统计
    将用户多次登录-登出时间段合并,统计总在线时间。


六、总结

本案例展示了如何在 KMP + OpenHarmony 环境下实现一个实用的区间合并与时间占用统计算法:

  1. 使用时间字符串解析为分钟数,构建统一的数值区间模型;
  2. 采用排序 + 扫描线的经典区间合并算法,时间复杂度 \( O(n \log n) \);
  3. 通过详细的文本报告输出,让区间合并结果与总时长一目了然;
  4. 借助 ArkTS UI,将算法结果直观呈现在 OpenHarmony 终端设备上。

与前面已经实现的「时间序列分析」「任务依赖调度」「字符串编辑距离」等案例相比,本篇引入了时间区间与线段合并这一重要算法主题,进一步丰富了你的 KMP + OpenHarmony 算法示例库。***

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

Logo

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

更多推荐