You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kotlin中计算排除闭区间后的非相交时间范围?

Kotlin 实现时间区间减法(拆分非相交可用时段)

问题分析

你需要从多个可用时间区间中扣除关闭区间,得到剩余的非相交可用时段——标准库的Range类型仅支持单一连续区间的简单减法,无法处理多区间重叠、跨区间覆盖这类复杂场景,因此需要自定义区间处理逻辑。

实现方案

首先定义时间区间的数据结构,再实现区间相交判断、区间拆分减法的核心逻辑,最后完成多区间的批量扣除计算。

1. 定义时间区间数据类

使用LocalTime表示一天内的时间,封装区间的相交判断、减法拆分和格式化输出:

import java.time.LocalTime
import java.time.format.DateTimeFormatter

data class TimeRange(val start: LocalTime, val end: LocalTime) {
    // 判断当前区间与另一区间是否相交
    fun overlapsWith(other: TimeRange): Boolean {
        return start.isBefore(other.end) && end.isAfter(other.start)
    }

    // 从当前区间扣除相交的关闭区间,返回剩余的子区间(0-2个)
    fun subtract(other: TimeRange): List<TimeRange> {
        val result = mutableListOf<TimeRange>()
        when {
            // 关闭区间在当前区间之外,直接保留原区间
            other.end <= start || other.start >= end -> result.add(this)
            // 关闭区间完全覆盖当前区间,无剩余
            other.start <= start && other.end >= end -> {}
            // 关闭区间覆盖当前区间左半部分,保留右侧
            other.start <= start && other.end < end -> 
                result.add(TimeRange(other.end, end))
            // 关闭区间覆盖当前区间右半部分,保留左侧
            other.start > start && other.end >= end -> 
                result.add(TimeRange(start, other.start))
            // 关闭区间在当前区间中间,拆分为左右两个子区间
            else -> {
                result.add(TimeRange(start, other.start))
                result.add(TimeRange(other.end, end))
            }
        }
        return result
    }

    // 格式化输出为易读字符串(如"7am-10am")
    fun format(): String {
        val formatter = DateTimeFormatter.ofPattern("h a")
        val startStr = start.format(formatter).lowercase().replace(" ", "")
        val endStr = end.format(formatter).lowercase().replace(" ", "")
        return "$startStr-$endStr"
    }
}

2. 核心计算函数

对可用区间和关闭区间排序后,依次将每个关闭区间从可用区间中扣除,最终得到剩余的可用时段:

fun calculateAvailableTime(availableRanges: List<TimeRange>, closedRanges: List<TimeRange>): List<TimeRange> {
    // 按起始时间排序,确保处理顺序正确
    val sortedAvailable = availableRanges.sortedBy { it.start }
    val sortedClosed = closedRanges.sortedBy { it.start }

    var currentAvailable = sortedAvailable.toMutableList()
    for (closed in sortedClosed) {
        val temp = mutableListOf<TimeRange>()
        for (available in currentAvailable) {
            if (available.overlapsWith(closed)) {
                // 拆分并保留剩余区间
                temp.addAll(available.subtract(closed))
            } else {
                // 无相交,直接保留原区间
                temp.add(available)
            }
        }
        currentAvailable = temp
        if (currentAvailable.isEmpty()) break // 无可用区间时提前退出
    }
    return currentAvailable.sortedBy { it.start }
}

3. 测试场景

fun main() {
    // 场景1:多个独立关闭区间
    val available1 = listOf(
        TimeRange(LocalTime.of(7, 0), LocalTime.of(12, 0)),
        TimeRange(LocalTime.of(14, 0), LocalTime.of(22, 0))
    )
    val closed1 = listOf(
        TimeRange(LocalTime.of(10, 0), LocalTime.of(11, 0)),
        TimeRange(LocalTime.of(15, 0), LocalTime.of(17, 0))
    )
    val result1 = calculateAvailableTime(available1, closed1)
    println("场景1结果:${result1.joinToString { it.format() }}")
    // 输出:场景1结果:7am-10am, 11am-12pm, 2pm-3pm, 5pm-10pm

    // 场景2:跨可用区间的关闭区间
    val closed2 = listOf(TimeRange(LocalTime.of(10, 0), LocalTime.of(17, 0)))
    val result2 = calculateAvailableTime(available1, closed2)
    println("场景2结果:${result2.joinToString { it.format() }}")
    // 输出:场景2结果:7am-10am, 5pm-10pm
}

为什么标准Range不行?

Kotlin标准库的Range(如LocalTimeRange)是单一连续区间,仅支持简单的区间减法(返回单个区间或空),无法处理拆分出多个子区间、多区间依次扣除这类复杂场景,因此必须自定义区间拆分逻辑。

内容的提问来源于stack exchange,提问作者I.A

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 18:00:52