Преобразовать строку в LocalDateTime в Java - PullRequest
0 голосов
/ 06 августа 2020

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

Пт, 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 ]

4 голосов
/ 07 августа 2020

tl; dr

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

См. Этот код, запускаемый в реальном времени на IdeOne.com .

LocalDateTime - неправильный класс

Ваша входная строка содержит +0000, что указывает на смещение-от-UT C.

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

OffsetDateTime

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

Анализ

Формат вашего ввода - RF C 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

2 голосов
/ 09 августа 2020

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

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

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

    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, вам необходимо подтвердить, что это так, иначе go ошибки будут незамеченными, и пользователи получат неверные результаты. Например:

    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).

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

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

2 голосов
/ 06 августа 2020

Я бы сказал, используйте 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);
...