Недавно я обнаружил ошибку java.time. его очень легко воспроизвести;
Я тестировал его на JDK 17.0.10 и JDK 21.0.4 на MacOS X и Centos Stream 9.
Кодируйте как:
public class JakartaDateTime {
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy");
LocalDateTime now = LocalDateTime.now();
System.out.println(formatter.format(now));
}
Этот код напечатает строку
Fri Sept 06 23:10:23 2024
Конечно, я этого не ожидал, ожидаемый результат должен быть:
Fri Sep 06 23:10:23 2024
Я сообщил об этом как об ошибке в отчете об ошибках Java и в базе данных ошибок Oracle с идентификатором: 9077542. Я уверен, что это не будет исправлено в ближайшее время. Поэтому мне нужно найти обходной путь, чтобы избежать этого.
Я пытаюсь использовать gson для сопоставления json дохода с нашим классом Java, парой ключ/значение объекта json как:
{"date":"Fri Sep 06 23:10:23 2024"}
Итак, мне нужно написать десериализатор для анализа этой строки в java.time.LocalDateTime. Я сделал что-то вроде этого:
public class EuDateTimeDeserializer implements JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
String dateTimeStr =jsonElement.getAsString();
if (!ThssUtils.isValidString(dateTimeStr)){
return null;
}else {
return LocalDateTime.parse(dateTimeStr, DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy"));
}
}
}
Но этот десериализатор работает только в другие месяцы, кроме сентября.
выдает ошибку как:
java.time.format.DateTimeParseException: Text 'Fri Sep 6 15:42:12 2024' could not be parsed at index 4
Это означает, что десериализатор не распознает указанную выше строку.
Есть ли у кого-нибудь такой опыт, чтобы найти способ избежать предыдущей ошибки java.time?
Пожалуйста, порекомендуйте!
Я тестировал его на JDK 17.0.10 и JDK 21.0.4 на MacOS X и Centos Stream 9.
В чем конкретно у вас "ошибка"? Вы имеете в виду отсутствующий t
в Sept
и Sep
?
Это не ошибка. Укажите Locale
.
DateTimeFormatter.ofPattern( … ).withLocale( locale )
Хотя вы забыли упомянуть, что именно вы считаете «ошибкой», я предполагаю, что вы имеете в виду отсутствующий t
в Sept
и Sep
.
Орфография, сокращения и пунктуация в формате даты и времени зависят от Locale
. Человеческий язык и культурные нормы, содержащиеся в Locale
объекте, управляют процессом локализации.
Locale
Здесь мы указываем английский в США 🇺🇸. Мы получаем Sep
без t
.
LocalDateTime ldt = LocalDateTime.of( 2024 , Month.SEPTEMBER , 6 , 23 , 10 , 23 , 0 );
System.out.println( "ldt = " + ldt );
Locale locale = Locale.of( "en" , "US" );
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "EEE MMM dd HH:mm:ss yyyy" ).withLocale( locale );
String output = formatter.format( ldt );
System.out.println( "output = " + output );
лдт = 2024-09-06T23:10:23
выход = пт, 6 сентября, 23:10:23 2024 г.
Измените этот язык на английский в Австралии 🇦🇺. Мы получаем Sept
с помощью t
.
Locale locale = Locale.of( "en" , "AU" );
лдт = 2024-09-06T23:10:23
выход = пятница, 6 сентября, 23:10:23 2024 г.
Измените этот язык на французский в Канаде 🇨🇦. Получаем sept.
строчными буквами со знаками препинания.
Locale locale = Locale.of( "fr" , "CA" );
выход = вен. сентябрь 06 23:10:23 2024
Пожалуйста, отзовите свой ошибочный отчет об ошибке.
Совет: Всегда подозревайте свой собственный код или собственное непонимание, прежде чем сомневаться в библиотеке, поставляемой в комплекте с Java. Да, ошибка может проникнуть в кодовую базу реализации Java. Но такие ошибки, как ваше утверждение, встречаются редко. Кодовая база OpenJDK, используемая в большинстве реализаций Java, является одним из наиболее тщательно изученных и протестированных кодов, когда-либо созданных (за исключением таких специальностей, как военная и аэрокосмическая промышленность).
LocalDateTime.now
Кстати, ваш код вызывает LocalDateTime.now
. Я не могу представить сценарий, в котором это было бы разумно. Этот класс не может представлять момент.
Объект LocalDateTime
представляет дату с указанием времени суток, но не имеет контекста смещения от UTC или часового пояса. Без этого контекста у нас нет возможности узнать, имели ли вы в виду 23:00 в Тувумбе, Австралия, 23:00 в Тулузе, Франция, или 23:00 в Толедо, штат Огайо, США — три совершенно разных момента, разделенных несколькими часами.
Чтобы запечатлеть текущий момент, используйте один из трех классов, отслеживающих определенную точку на временной шкале:
Instant
OffsetDateTime
ZonedDateTime
Первый и третий наиболее распространены для бизнес-целей, а второй используется в основном для обмена с базой данных SQL.
Instant now = Instant.now() ; // Capture current moment as seen "in UTC", meaning an offset from UTC of zero hours-minutes-seconds.
Увидьте тот же момент через настенные часы/календарь определенного часового пояса.
ZoneId z = ZoneId.of( "Australia/Brisbane" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Вероятно, поэтому я живу в Австралии и использую AU в качестве локали по умолчанию. Спасибо Василий!
@cidy.long Извлеченный урок: явно укажите желаемую/ожидаемую локаль, а не неявно полагайтесь на значение по умолчанию. То же самое и с часовым поясом: всегда указывайте желаемый/ожидаемый ZoneId
.
Какую версию JDK вы используете?