kmp openharmony 区间合并与时间占用统计
本文介绍了一个基于Kotlin Multiplatform的区间合并与时间占用统计工具。该工具可处理会议室预定、排班管理等场景中重叠的时间段,通过解析输入的时间区间(如09:00-10:30),自动合并重叠或相接的区间,并计算总占用时长和每个合并区间的长度。核心算法采用先排序后扫描的策略,时间复杂度为O(n log n)。工具支持多种输入格式,提供可视化统计报告,包括原始区间数量、合并后区间数量、

在排班管理、会议室预定、资源使用监控等场景中,我们经常需要处理一批可能重叠的时间段:
这些时间段合并后,一共占用了多长时间?最终的占用区间有哪些?
本案例基于 Kotlin Multiplatform(KMP)与 OpenHarmony,实现了一个「区间合并与时间占用统计」工具:
- 支持多条时间区间输入(如
09:00-10:30、10:15-11:00); - 自动将重叠或相接的区间合并;
- 统计合并后的总占用时长和每个合并区间的长度;
- 通过 ArkTS 单页面板可视化展示计算结果。
一、问题背景与应用场景
典型应用场景包括:
-
会议室占用分析:
多个会议预约时间段可能重叠,想知道一天中会议室总共被占用了多久。 -
排班与值班时段统计:
多个人的工作时间段交织,需要统计某个岗位「有人值守」的总时长。 -
资源使用时间轴分析:
服务器 CPU 高负载时间段、带宽峰值时间段等合并后,统计总体资源「忙碌时间」。 -
用户在线时长统计:
用户多次上下线,合并在线区间,计算总在线时长。
本质上,这类问题都可以抽象为:
给定若干闭开区间(或闭区间),将其中重叠或相接的部分合并,并统计合并后区间的总长度。
二、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
}
步骤说明:
- 按起始时间对所有区间排序;
- 维护当前合并区间
[currentStart, currentEnd); - 依次扫描后续区间:
- 若新区间起点
<= 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 集成
通过 @JsExport,intervalMergeAnalyzer 已经出现在 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
-
-
操作区:
- 「▶ 运行区间合并分析」按钮;
- 「↻ 恢复示例」按钮。
-
结果区:
- 上半部分列出原始区间(排序后);
- 下半部分列出合并区间及各自时长,总占用时长汇总。
配合你现有的深蓝卡片风格,可以很自然地融入整个算法案例系列。
五、应用场景回顾
-
会议室 / 资源占用统计:
快速得到一天中资源实际被占用的总时长与合并后时间段。 -
排班合并与可视化:
将多位员工的排班时间区间合并,查看岗位整体覆盖情况。 -
监控指标高负载时间段总结:
将 CPU 使用率超过阈值的时间片合并,生成高负载总时长与区间报告。 -
多次登录会话的在线时长统计:
将用户多次登录-登出时间段合并,统计总在线时间。
六、总结
本案例展示了如何在 KMP + OpenHarmony 环境下实现一个实用的区间合并与时间占用统计算法:
- 使用时间字符串解析为分钟数,构建统一的数值区间模型;
- 采用排序 + 扫描线的经典区间合并算法,时间复杂度 \( O(n \log n) \);
- 通过详细的文本报告输出,让区间合并结果与总时长一目了然;
- 借助 ArkTS UI,将算法结果直观呈现在 OpenHarmony 终端设备上。
与前面已经实现的「时间序列分析」「任务依赖调度」「字符串编辑距离」等案例相比,本篇引入了时间区间与线段合并这一重要算法主题,进一步丰富了你的 KMP + OpenHarmony 算法示例库。***
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)