[kotlin 메모] [LocalDate] 기간이 연속적인지 여부를 결정하고 싶습니다.

9246 단어 Kotlin

2021/07/14 추가



↑ 꽤 스마트하게 정리된 개선안이 있으므로,
부디 그쪽을 봐 주세요.

소개



기간 목록에 대해 목록의 개별 기간을 결합했을 때 치아가 나오지 않는지 확인합니다.
리스트내의 기간의 중첩은 허용하는 것으로 합니다.
예 1: 1/1~1/151/16~1/30 는 연속되어 있으므로 OK로 한다
예 2: 1/1~1/151/12~1/30 는 일부 겹쳐져 있지만 OK로 한다
예 3: 1/1~1/151/17~1/30 는 잇몸(1/16)이 있으므로 NG로 한다

용어 정의


  • 기간
  • 여기서는 java.time.LocalDate 형식의 시작일과 종료일의 쌍

  • // 例
        // String->LocalDate変換を逐一書くのが面倒なので拡張関数化します
        val formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd")
        val String.toLocalDate(): LocalDate = LocalDate.parse(
            this, formatter.withResolverStyle(ResolverStyle.STRICT)
        )
    
        // 期間のリスト
        val ranges = listOf(
            "2020-01-01".toLocalDate() to "2020-01-02".toLocalDate(),
            "2020-01-03".toLocalDate() to "2020-01-04".toLocalDate(),
            "2020-01-05".toLocalDate() to "2020-01-31".toLocalDate(),
            "2020-02-01".toLocalDate() to "2020-02-28".toLocalDate()
        )
    

    코드


        /** 複数の期間が重なるか連続する */
        fun overlaps(vararg ranges: Pair<LocalDate, LocalDate>): Boolean {
            if (ranges.size < 2) return true
            ranges.sortBy { (start, _) -> start }
            if (overlaps(ranges[0], ranges[1])) {
                val (aStart, aEnd) = ranges[0]
                val (_, bEnd) = ranges[1]
                val laterEnd = if (aEnd.isAfter(bEnd)) aEnd else bEnd
                return overlaps(
                    Pair(aStart, laterEnd),
                    *ranges.slice(2 until ranges.size)
                        .toTypedArray()
                )
            }
            return false
        }
    
        /** 2つの期間が重なるか連続する */
        fun overlaps(
            aRange: Pair<LocalDate, LocalDate>, bRange: Pair<LocalDate, LocalDate>
        ): Boolean {
            val (aStart, aEnd) = aRange
            val (bStart, bEnd) = bRange
            // 期間Aの開始日 <= 期間Bの終了日+1 && 期間Bの開始日 <= 期間Aの終了日+1
            return aStart.isBefore(bEnd.plusDays(2)) 
                && bStart.isBefore(aEnd.plusDays(2))
        }
    

    사고방식



    설명 잘못이므로 그림입니다.
    색 차이의 띠가, 각각의 기간을 나타내고 있습니다.
    밴드의 왼쪽 끝이 시작일, 오른쪽 끝이 종료일이라고 생각하십시오.

    우선 시작일이 젊은 순서로 정렬합니다.

    기간 목록에서 index0과 index1을 추출하여 연속 또는 중복되었는지 확인하고 결합합니다.
    이 때 기간에 치아가 있으면 중단하고 false를 반환합니다.
    병합 할 수 있으면 병합 된 기간과 index2 이후의 나머지 기간으로 목록을 다시 만들고,
    해당 목록에 대해 위의 조인 작업을 목록 크기가 1이 될 때까지 계속합니다.

    리스트 사이즈가 1 이 될 때까지 무사히 결합할 수 있으면, 연속하고 있는 것으로 간주해 true 를 돌려줍니다.

    마지막으로



    우선 확실히, 이것보다 좋은 수법이 존재한다고 생각합니다. 여러가지 이마이치입니다.
    하지만, 찾은 범위에서는 발견되지 않았기 때문에, 스스로 조립했습니다.
    원래 수요가 전혀 없을지도 모른다.

    개량안・대체안등 있으면, 아무쪼록 잘 부탁드립니다.

    좋은 웹페이지 즐겨찾기