LoginSignup
2
1

More than 1 year has passed since last update.

【Kotlin】連続した期間を結合する

Last updated at Posted at 2021-07-13

↑これをやってみた。

本体

import java.time.LocalDate

/**
 * すべての要素の期間を結合した期間を返す。
 * 期間が連続していない場合は null を返す。
 */
fun Iterable<ClosedRange<LocalDate>>.joinOrNull(): ClosedRange<LocalDate>? =
    sortedBy { it.start }
        .takeIf { it.isNotEmpty() }
        ?.reduce { acc, range ->
            joinOrNull(acc, range)
                ?: return null
        }

/**
 * 2つの期間を結合する。
 * 期間が連続していない場合は null を返す。
 *
 * @param startEarly 開始日が早い方の期間。
 * @param startLate 開始日が遅い方の期間。
 */
private fun joinOrNull(
    startEarly: ClosedRange<LocalDate>,
    startLate: ClosedRange<LocalDate>
): ClosedRange<LocalDate>? {
    if (startEarly.endInclusive < LocalDate.MAX) {
        if (startEarly.endInclusive.plusDays(1) < startLate.start) {
            return null
        }
    } else {
        if (startEarly.endInclusive < startLate.start.minusDays(1)) {
            return null
        }
    }

    return startEarly.start..maxOf(
        startEarly.endInclusive,
        startLate.endInclusive
    )
}

plusDays(1)minusDays(1) が美しくない…。

動作確認

import java.time.LocalDate

fun String.toLocalDate(): LocalDate =
    LocalDate.parse(this)

fun main() {
    // 隣接する範囲
    check(
        listOf(
            "1999-12-29".toLocalDate().."1999-12-31".toLocalDate(),
            "2000-01-01".toLocalDate().."2000-01-03".toLocalDate(),
        ).joinOrNull() ==
                "1999-12-29".toLocalDate().."2000-01-03".toLocalDate()
    )
    // 離れた範囲
    check(
        listOf(
            "1999-12-29".toLocalDate().."1999-12-31".toLocalDate(),
            "2000-01-02".toLocalDate().."2000-01-03".toLocalDate(),
        ).joinOrNull() == null
    )
    // 一部重なった範囲
    check(
        listOf(
            "1999-12-29".toLocalDate().."1999-12-31".toLocalDate(),
            "1999-12-31".toLocalDate().."2000-01-03".toLocalDate(),
        ).joinOrNull() ==
                "1999-12-29".toLocalDate().."2000-01-03".toLocalDate()
    )
    // 包含した範囲
    check(
        listOf(
            "1999-12-29".toLocalDate().."1999-12-31".toLocalDate(),
            "1999-12-28".toLocalDate().."2000-01-03".toLocalDate(),
        ).joinOrNull() ==
                "1999-12-28".toLocalDate().."2000-01-03".toLocalDate()
    )
    // 一致した範囲
    check(
        listOf(
            "1999-12-29".toLocalDate().."1999-12-31".toLocalDate(),
            "1999-12-29".toLocalDate().."1999-12-31".toLocalDate(),
        ).joinOrNull() ==
                "1999-12-29".toLocalDate().."1999-12-31".toLocalDate()
    )
    // 単一の範囲
    check(
        listOf(
            "1999-12-29".toLocalDate().."1999-12-31".toLocalDate(),
        ).joinOrNull() ==
                "1999-12-29".toLocalDate().."1999-12-31".toLocalDate()
    )
    // 空の範囲
    check(
        listOf<ClosedRange<LocalDate>>().joinOrNull() == null
    )
    // 2つより多い範囲
    check(
        listOf(
            "1999-12-29".toLocalDate().."1999-12-30".toLocalDate(),
            "1999-12-31".toLocalDate().."2000-01-01".toLocalDate(),
            "2000-01-02".toLocalDate().."2000-01-03".toLocalDate(),
        ).joinOrNull() ==
                "1999-12-29".toLocalDate().."2000-01-03".toLocalDate()
    )

    println("OK") // > OK
}

/以上

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1