毎回ロジックを考えている気がするのでメモしておく。
- 期間の終了がnull (つまり開始からこの先ずっと対象期間) というパターンもよくあるので、それにも対応。
- 「10:00 ~ 11:00」と「11:00 ~ 12:00」のような、期間1の終了と期間2の開始がぴったり一致している時も重複あり判定にしている。このパターンを重複なし判定にする場合は、
!start2.isAfter(end1)
をstart2.isBefore(end1)
に、!end2.isBefore(start1)
をend2.isAfter(start1)
にする。
/**
* 期間の重複チェック(LocalTime版)
* 単一のポイントで期間が重なったときも重複したとみなす。
* e.g.)
* 期間1 => 10:00 ~ 11:00
* 期間2 => 11:00 ~ 12:00
* 結果 => 重複あり(true)
*
* @param start1 期間1の開始
* @param end1 期間1の終了
* @param start2 期間2の開始
* @param end2 期間2の終了
* @return 重複してたらtrue
*/
def isOverlapped(start1: LocalTime, end1: LocalTime, start2: LocalTime, end2: LocalTime): Boolean =
!start2.isAfter(end1) && !end2.isBefore(start1)
/**
* 期間の重複チェック(LocalTime版, 期間終了Option可)
* 単一のポイントで期間が重なったときも重複したとみなす。
* e.g.)
* 期間1 => 10:00 ~ 11:00
* 期間2 => 11:00 ~ 12:00
* 結果 => 重複あり(true)
*
* @param start1 期間1の開始
* @param end1Opt 期間1の終了 (Noneなら開始以降ずっと対象期間)
* @param start2 期間2の開始
* @param end2Opt 期間2の終了 (Noneなら開始以降ずっと対象期間)
* @return 重複してたらtrue
*/
def isOverlapped(start1: LocalTime, end1Opt: Option[LocalTime], start2: LocalTime, end2Opt: Option[LocalTime]): Boolean =
end1Opt.fold(true)(!start2.isAfter(_)) && end2Opt.fold(true)(!_.isBefore(start1))
/**
* 期間の重複チェック(LocalDate版)
* 単一のポイントで期間が重なったときも重複したとみなす。
* e.g.)
* 期間1 => 2020-10-01 ~ 2020-10-10
* 期間2 => 2020-10-10 ~ 2020-10-20
* 結果 => 重複あり(true)
*
* @param start1 期間1の開始
* @param end1 期間1の終了
* @param start2 期間2の開始
* @param end2 期間2の終了
* @return 重複してたらtrue
*/
def isOverlapped(start1: LocalDate, end1: LocalDate, start2: LocalDate, end2: LocalDate): Boolean =
!start2.isAfter(end1) && !end2.isBefore(start1)
/**
* 期間の重複チェック(LocalDate版, 期間終了Option可)
* 単一のポイントで期間が重なったときも重複したとみなす。
* e.g.)
* 期間1 => 2020-10-01 ~ 2020-10-10
* 期間2 => 2020-10-10 ~ 2020-10-20
* 結果 => 重複あり(true)
*
* @param start1 期間1の開始
* @param end1Opt 期間1の終了 (Noneなら開始以降ずっと対象期間)
* @param start2 期間2の開始
* @param end2Opt 期間2の終了 (Noneなら開始以降ずっと対象期間)
* @return 重複してたらtrue
*/
def isOverlapped(start1: LocalDate, end1Opt: Option[LocalDate], start2: LocalDate, end2Opt: Option[LocalDate]): Boolean =
end1Opt.fold(true)(!start2.isAfter(_)) && end2Opt.fold(true)(!_.isBefore(start1))
/**
* 期間の重複チェック(LocalDateTime版)
* 単一のポイントで期間が重なったときも重複したとみなす。
* e.g.)
* 期間1 => 2020-10-01 10:00:00 ~ 2020-10-10 20:00:00
* 期間2 => 2020-10-10 20:00:00 ~ 2020-10-20 20:00:00
* 結果 => 重複あり(true)
*
* @param start1 期間1の開始
* @param end1 期間1の終了
* @param start2 期間2の開始
* @param end2 期間2の終了
* @return 重複してたらtrue
*/
def isOverlapped(start1: LocalDateTime, end1: LocalDateTime, start2: LocalDateTime, end2: LocalDateTime): Boolean =
!start2.isAfter(end1) && !end2.isBefore(start1)
/**
* 期間の重複チェック(LocalDateTime版, 期間終了Option可)
* 単一のポイントで期間が重なったときも重複したとみなす。
* e.g.)
* 期間1 => 2020-10-01 10:00:00 ~ 2020-10-10 20:00:00
* 期間2 => 2020-10-10 20:00:00 ~ 2020-10-20 20:00:00
* 結果 => 重複あり(true)
*
* @param start1 期間1の開始
* @param end1Opt 期間1の終了 (Noneなら開始以降ずっと対象期間)
* @param start2 期間2の開始
* @param end2Opt 期間2の終了 (Noneなら開始以降ずっと対象期間)
* @return 重複してたらtrue
*/
def isOverlapped(start1: LocalDateTime, end1Opt: Option[LocalDateTime], start2: LocalDateTime, end2Opt: Option[LocalDateTime]): Boolean =
end1Opt.fold(true)(!start2.isAfter(_)) && end2Opt.fold(true)(!_.isBefore(start1))