↑これをやってみた。
本体
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
}
/以上