Преобразование строки в LocalDateTime в Java

У меня есть эта строка

Пт, 07 авг.2020 18:00:00 +0000

И мне нужно преобразовать его в LocalDateTime

Я пробовал несколько способов:

создать DateTimeFormatter и проанализировать строку

String dateString = "Fri, 07 Aug 2020 18:00:00 +0000";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E, dd MMM yyyy HH:mm:ss", Locale.getDefault());
LocalDateTime parsedDate = LocalDateTime.parse(publishedString, formatter);

Преобразуйте его в дату с помощью SimpleDateFormat, а затем преобразуйте resultDate в LocalDateTime

String dateString = "Fri, 07 Aug 2020 18:00:00 +0000";
SimpleDateFormat dateFormatter = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z");
Date date = dateFormatter.parse(publishedString);
LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();

оба решения дают мне одно и то же исключение:

java.time.format.DateTimeParseException: Text 'Fri, 07 Aug 2020 18:00:00 +0000' could not be parsed 
at index 0 at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)

Как я могу преобразовать эту строку?

3 ответа

Решение

Я бы сказал, используйте Locale.ROOT и не забывайте Z в классе DateTimeFormatter

String dateString = "Fri, 07 Aug 2020 18:00:00 +0000";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E, dd MMM yyyy HH:mm:ss Z", Locale.ROOT);
LocalDateTime parsedDate = LocalDateTime.parse(dateString, formatter);

tl;dr

OffsetDateTime.parse( 
    "Fri, 07 Aug 2020 18:00:00 +0000" , 
    DateTimeFormatter.RFC_1123_DATE_TIME 
)

Посмотрите, как этот код запускается вживую на IdeOne.com.

LocalDateTime это неправильный класс

Ваша строка ввода содержит +0000 что указывает на смещение от UTC.

Итак, вы не должны использовать LocalDateTime. В этом классе намеренно отсутствует понятие часового пояса или смещения. С участиемLocalDateTime, ваша строка Fri, 07 Aug 2020 18:00:00 +0000 станет 6M 7 августа 2020 года, но мы не узнаем, будет ли это 18:00 в Токио, Япония, 18:00 в Тулузе, Франция, или 18:00 в Толедо, штат Огайо, США - все разные моменты с разницей в несколько часов.

OffsetDateTime

Вместо этого это значение следует анализировать как OffsetDateTime.

Парсинг

Формат вашего ввода - RFC 1123. Этот конкретный формат предопределен в java.time.

String input = "Fri, 07 Aug 2020 18:00:00 +0000";
DateTimeFormatter f = DateTimeFormatter.RFC_1123_DATE_TIME;
OffsetDateTime odt = OffsetDateTime.parse( input , f );

odt.toString(): 2020-08-07T18:00Z

Я так понимаю вам нужен LocalDateTime для API, который из-за не зависящей от вас проблемы проектирования пытается использовать LocalDateTime на определенный момент времени.

Если внешний контракт диктует, в каком часовом поясе или в каком UTC смещается LocalDateTime следует понимать, LocalDateTimeможно заставить работать, по крайней мере, в 99,977 % случаев. У вас по-прежнему будет ошибка программирования, ожидающая своего появления в тот день, когда какой-нибудь коллега-программист не прочитает контракт, проблема, которую мы не можем решить в коде, только попытайтесь смягчить ее с помощью хороших комментариев.

Если, например, в контракте указано UTC, то нам нужно убедиться, что мы конвертируем время в UTC. И для этого нам понадобится смещение от строки.

    ZoneOffset contractualOffset = ZoneOffset.UTC;
    String stringWeveGot = "Fri, 07 Aug 2020 18:00:00 +0000";
    LocalDateTime convertedDateTime = OffsetDateTime
            .parse(stringWeveGot, DateTimeFormatter.RFC_1123_DATE_TIME)
            .withOffsetSameInstant(contractualOffset)
            .toLocalDateTime();
    System.out.println(convertedDateTime);

Выход:

2020-08-07T18: 00

Если требуется, чтобы смещение в строке уже было равно 0, вам необходимо подтвердить, что это так, иначе ошибки останутся незамеченными, и пользователи получат неверные результаты. Например:

    OffsetDateTime parsedOdt = OffsetDateTime
            .parse(stringWeveGot, DateTimeFormatter.RFC_1123_DATE_TIME);
    if (! parsedOdt.getOffset().equals(contractualOffset)) {
        throw new IllegalStateException("Offset must be " + contractualOffset
                + ", was " + parsedOdt.getOffset());
    }
    LocalDateTime convertedDateTime = parsedOdt.toLocalDateTime();

Если в контракте упоминается какой-то часовой пояс, конвертируйте в этот часовой пояс. Я беру в качестве примера Австралию / Викторию.

    ZoneId contractualZone = ZoneId.of("Australia/Victoria");
    String stringWeveGot = "Fri, 07 Aug 2020 18:00:00 +0000";
    LocalDateTime convertedDateTime = OffsetDateTime
            .parse(stringWeveGot, DateTimeFormatter.RFC_1123_DATE_TIME)
            .atZoneSameInstant(contractualZone)
            .toLocalDateTime();
    System.out.println(convertedDateTime);

2020-08-08T04: 00

Вы получите неоднозначный результат при аномалиях времени, когда часы повернуты назад, например, при падении назад, когда заканчивается летнее время (DST).

Что пошло не так в вашем коде?

Причина вашего исключения объясняется здесь:

Другие вопросы по тегам