DateTimeParseException: текст «2019-06-07 12:18:16» не может быть проанализирован

У меня есть следующий код для преобразования Instant в String, а затем обратно в I

String timestampString = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));
LOGGER.info("timestampString: " + timestampString);

Instant instant =
    LocalDateTime.parse(timestampString,
        DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss")).toInstant(ZoneOffset.UTC);

он печатает временную метку как: 2019-06-07 12:45:57

и не удалось разобрать строку:

java.time.format.DateTimeParseException: Text '2019-06-07 12:45:57' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MinuteOfHour=45, HourOfAmPm=0, NanoOfSecond=0, SecondOfMinute=57, MilliOfSecond=0, MicroOfSecond=0},ISO resolved to 2019-06-07 of type java.time.format.Parsed

почему он не может разобрать его, хотя это тот же формат, в который я конвертирую метку времени?


person user468587    schedule 07.06.2019    source источник
comment
Совет: большее количество тегов поможет привлечь больше людей, знающих тему, о которой вы спрашиваете.   -  person Ole V.V.    schedule 08.06.2019
comment
По сути, вам нужно HH, а не hh. hh — это 12-часовой час дня; вы хотите 24 часа в сутки.   -  person Jon Skeet    schedule 08.06.2019
comment
Мне интересно, почему вы конвертируете его в строку и обратно, заметьте, что LocalDateTime.now() использует системный часовой пояс, тогда как вы утверждаете, что он в формате UTC. Если бы вы могли предоставить больше контекста для того, чего вы пытаетесь достичь, мы могли бы помочь вам не только в анализе.   -  person Jon Skeet    schedule 08.06.2019
comment
LocalDateTime — это неправильный класс для отслеживания момента, как объясняется в его Javadoc. Вместо этого используйте Instant, OffsetDateTime и ZonedDateTime.   -  person Basil Bourque    schedule 08.06.2019


Ответы (1)


Используйте HH для часа дня вместо hh

Проблема, о которой вы спрашиваете, заключается в том, что вы используете строчные буквы hh в строке шаблона формата (оба раза). Вам нужен верхний регистр HH для часового дня с 00 до 23. hh для часа в пределах AM или PM с 01 до 12. Итак, что пошло не так, так это то, что java.time не знал, относится ли 12 в вашей строке к 12 AM или 12 PM и отказался сделать предположение для вас.

Если вы внимательно прочитаете сообщение об исключении, вы также заметите, что в нем говорится, что HourOfAmPm=0 было проанализировано. Он не говорит HourOfDay.

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String timestampString = LocalDateTime.now().format(formatter);
    System.out.println("timestampString: " + timestampString);

    Instant instant = LocalDateTime.parse(timestampString, formatter)
            .toInstant(ZoneOffset.UTC);

    System.out.println("instant: " + instant);

Когда я только что запустил этот фрагмент, я получил такой вывод:

timestampString: 2019-06-08 19:22:51
instant: 2019-06-08T19:22:51Z

Это неправильно! Я запустил фрагмент примерно в 17:22 UTC, а не в 19:22. Поскольку в Дании все еще используется летнее время (черт возьми), местное время здесь было 19:22, которое было использовано для результата и переведено в то же время настенных часов в формате UTC, а не в тот же самый момент. Вы должны всегда передавать желаемый часовой пояс методу now, чтобы избежать таких ошибок. Поскольку вы хотели UTC:

    String timestampString = LocalDateTime.now(ZoneOffset.UTC).format(formatter);
timestampString: 2019-06-08 17:27:57
instant: 2019-06-08T17:27:57Z

Еще лучше, не используйте LocalDateTime для хранения чего-то, что вы хотите использовать как момент времени. Вместо этого используйте Instant, OffsetDateTime или ZonedDateTime.

Дополнительная информация об использовании hh, HH или kk для форматирования и анализа значений часов в этом вопросе и ответах на него: Разница между java HH:mm и hh:mm в SimpleDateFormat. Вопрос касается общеизвестно неприятного SimpleDateFormat, но ответы действительны и для DateTimeFormatter.

person Ole V.V.    schedule 08.06.2019