DateTimeFormatter и ZonedDateTime - генерировать исключение для неоднозначных и несуществующих дат (DST) - PullRequest
0 голосов
/ 27 апреля 2020

Я пытаюсь создать действительно строгую фабрику DateTimeFormatter / ZonedDateTime. Если datetetime не имеет смысла как есть, тогда оно должно выдать исключение - никаких корректировок. Пример: если во время летнего времени пропущен час.

В настоящее время эти тесты не пройдены, исключение не выдается. ZonedDateTime угадывает / корректирует на основе правил, описанных в документации.

    @Test
    fun `Ambiguous hour during  +0200 to +0100 DST change`() {
        val input = "2019-10-27T02:30:00Europe/Prague"
        val formatter = createFormatter()

        Assertions.assertThrows(DateTimeParseException::class.java) {
            ZonedDateTime.from(formatter.parse(input))
            // Gets instead adjusted as 2019-10-27T02:30:00Europe/Prague+02:00

        }
    }

    @Test
    fun `Skipped hour during +0100 to +0200 DST change`() {
        val formatter = createFormatter()

        Assertions.assertThrows(DateTimeParseException::class.java) {
            ZonedDateTime.from(formatter.parse("2019-03-31T02:30:00Europe/Prague"))
            // Gets instead adjusted as 2019-03-31T03:30:00Europe/Prague+02:00
        }
    }


    @Test
    fun `Test impossible offset during DST change`() {
        val input = "2019-10-27T02:30:00Europe/Prague+05:00"
        val formatter = createFormatter()

        Assertions.assertThrows(DateTimeParseException::class.java) {
            ZonedDateTime.from(formatter.parse(input))
            // Gets instead  adjusted as 2019-10-26T23:30:00Europe/Prague+02:00
        }
    }

Изменения для области Europe/Prague относятся к здесь .

Код, который я имею:

fun createFormatter(): DateTimeFormatter {
    return DateTimeFormatterBuilder()
        .parseCaseSensitive()
        .parseStrict()
        .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
        .appendZoneRegionId()
        .optionalStart()
        .appendOffsetId()
        .optionalEnd()
        .toFormatter()
        .withResolverStyle(ResolverStyle.STRICT)
}

ZonedDateTime настраивается, как описано в документации. Я хотел бы изменить это поведение, чтобы бросать каждый раз, когда что-то не совсем понятно.

1 Ответ

2 голосов
/ 27 апреля 2020

Вам нужно добавить дополнительные логи c для проверки результата.

Ниже код в Java, но вы можете легко преобразовать в Kotlin.

static ZonedDateTime parseVeryStrict(String text) {
    TemporalAccessor parsed = createFormatter().parse(text);
    ZonedDateTime zonedDateTime = ZonedDateTime.from(parsed);

    if (parsed.isSupported(OFFSET_SECONDS)) {
        // Verify given offset was correct
        ZoneOffset zoneOffset = ZoneOffset.from(parsed);
        if (! zoneOffset.equals(zonedDateTime.getOffset()))
            throw new DateTimeParseException("Incorrect offset: '" + text + "'", text, 0);
    } else {
        // Without offset, fail if in DST overlap time range
        if (! zonedDateTime.withEarlierOffsetAtOverlap().isEqual(zonedDateTime.withLaterOffsetAtOverlap()))
            throw new DateTimeParseException("Ambiguous time (DST overlap): '" + text + "'", text, 0);
    }

    // Verify time wasn't adjusted because it was in DST gap time range
    LocalTime localTime = LocalTime.from(parsed);
    if (! localTime.equals(zonedDateTime.toLocalTime()))
        throw new DateTimeParseException("Invalid time (DST gap): '" + text + "'", text, 0);

    return zonedDateTime;
}

Тест

public static void main(String[] args) {
    test("2019-10-27T02:30:00");
    test("2019-10-27T02:30:00Europe/Prague");
    test("2019-03-31T02:30:00Europe/Prague");
    test("2019-10-27T02:30:00Europe/Prague+05:00");
    test("2019-10-27T02:30:00Europe/Prague+02:00");
    test("2019-10-27T02:30:00Europe/Prague+01:00");
}

static void test(String text) {
    try {
        System.out.println(text + " -> " + parseVeryStrict(text));
    } catch (DateTimeParseException e) {
        System.out.println(text + " - " + e.getMessage());
    }
}

Выход

2019-10-27T02:30:00 - Text '2019-10-27T02:30:00' could not be parsed at index 19
2019-10-27T02:30:00Europe/Prague - Ambiguous time (DST overlap): '2019-10-27T02:30:00Europe/Prague'
2019-03-31T02:30:00Europe/Prague - Invalid time (DST gap): '2019-03-31T02:30:00Europe/Prague'
2019-10-27T02:30:00Europe/Prague+05:00 - Incorrect offset: '2019-10-27T02:30:00Europe/Prague+05:00'
2019-10-27T02:30:00Europe/Prague+02:00 -> 2019-10-27T02:30+02:00[Europe/Prague]
2019-10-27T02:30:00Europe/Prague+01:00 -> 2019-10-27T02:30+01:00[Europe/Prague]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...