Преобразование строки в 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).
Что пошло не так в вашем коде?
Причина вашего исключения объясняется здесь: