У меня есть формат даты, например Apr 1 2022 12:00:00:000AM
, и я хочу преобразовать его в 20220401
(ггггММдд). Как мы можем преобразовать формат даты
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class Demo {
public static void main(String[] args) {
String value = "Apr 1 2022 12:00:00:000AM";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM d yyyy hh:mm:ss.SSSa");
LocalDateTime dateTime = LocalDateTime.parse(value, formatter);
System.out.println(dateTime);
}
}
При запуске этого кода выдается ошибка ниже
Exception in thread "main" java.time.format.DateTimeParseException: Text 'Apr 1 2022 12:00:00:000AM' could not be parsed at index 4
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
at Demo.main(Demo.java:10)
РЕДАКТИРОВАТЬ-1 Согласно предложениям Василия Бурка, ниже все еще не работает
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class Demo {
public static void main(String[] args) {
String value = "Apr 1 2022 12:00:00:000AM".replace( " " , " " ) ;
Locale locale = new Locale.Builder().setLanguage("en").setRegion("US").build();
DateTimeFormatter formatter =
DateTimeFormatter.
ofPattern("MMM d yyyy hh:mm:ss.SSSa")
.withLocale( locale ) ;
LocalDateTime dateTime = LocalDateTime.parse(value, formatter);
System.out.println(dateTime);
}
}
Все та же ошибка
Exception in thread "main" java.time.format.DateTimeParseException: Text 'Apr 1 2022 12:00:00:000AM' could not be parsed at index 19
could not be parsed at index 19
— В шаблоне формата вместо двоеточия стоит точка.
вы не можете ожидать, что 12:00:00:000
соответствует hh:mm:ss.SSS
- сначала используется :
для отделения секунд от миллисекунд, а в шаблоне используется .
Кстати: LocalDateTime.parse("Apr 1 2022 12:00:00:000AM", DateTimeFormatter.ofPattern("MMM [ ]d yyyy hh:mm:ss:SSSa", Locale.ROOT))
работает ( replace
не нужно)
Не храните даты в строках. Тогда вам также не нужно будет преобразовывать строку в одном формате в строку в другом формате. Если ваша дата взята из базы данных, получите LocalDate
, OffsetDateTime
или другой подходящий тип даты и времени в зависимости от типа данных в базе данных. Если вам нужен yyyymmdd
для обмена данными, используйте встроенный BASIC_ISO_DATE
форматтер.
У вас есть два пробела после Apr
? Что происходит, когда день месяца равен 10 или больше? Если число дополнено пробелами (выровнено по правому краю), посмотрите на букву шаблона формата p
, чтобы узнать, как его проанализировать.
Я хотел закрыть этот вопрос. Из вопроса неясно, является ли исходный вопрос, который я назвал обманом, правильным, но я нашел более полезным закрыть его как дубликат, а не как «неясный» или «нуждается в деталях отладки», что также будет по моему самому честному мнению, обе причины являются вескими.
Locale
при анализе локализованного текста.Между первыми двумя частями есть два символа ПРОБЕЛ. Замените одним ПРОБЕЛОМ.
String value = "Apr 1 2022 12:00:00:000AM".replace( " " , " " ) ;
Альтернативно вы можете указать необязательный ПРОБЕЛ в шаблоне форматирования с помощью квадратных скобок: [ ]
.
COLON
персонажВ вашем шаблоне форматирования указано, что между секундой и AM/PM следует ожидать символ ПОЛНОЙ СТОП (.
). Но в вашей входной строке есть Двоеточие (:
).
Измените шаблон форматирования в соответствии с введенными вами данными.
Locale
Укажите Locale
явно, а не неявно, полагаясь на текущее значение по умолчанию JVM.
Locale locale = new Locale( "en" , "US" ) ; // In Java 19+, use `Locale.of`.
DateTimeFormatter formatter =
DateTimeFormatter
.ofPattern("MMM d yyyy hh:mm:ss:SSSa");
.withLocale( locale ) ;
Locale
указывает человеческий язык и культурные нормы, которые будут использоваться при локализации. Сюда входит написание названия месяца и использование знаков препинания.
Чтобы создать желаемый текст, извлеките LocalDate
, чтобы сосредоточиться на дате без времени суток.
LocalDate ld = myLocalDateTime.toLocalDate() ;
Затем определите форматировщик для вашего формата.
В вашем случае нет необходимости определять собственный форматтер. Требуемый форматтер соответствует «базовой» форме стандартного формата ISO 8601, которая сводит к минимуму знаки препинания. Java предоставляет константу для этого конкретного формата: BASIC_ISO_DATE.
String output = ld.format( DateTimeFormatter.BASIC_ISO_DATE ) ;
String input = "Apr 1 2022 12:00:00:000AM".replace( " " , " " ) ;
Locale locale =new Locale( "en" , "US" ) ; // In Java 19+, use `Locale.of`.
DateTimeFormatter formatter =
DateTimeFormatter
.ofPattern("MMM d yyyy hh:mm:ss:SSSa")
.withLocale( locale );
LocalDateTime ldt = LocalDateTime.parse( input , formatter ) ;
LocalDate ld = ldt.toLocalDate();
String output = ld.format( DateTimeFormatter.BASIC_ISO_DATE ) ;
System.out.println( output ) ;
Посмотрите этот код запустите на Ideone.com.
Избегайте анализа текста в пользовательских форматах и избегайте анализа локализованного текста.
Международный стандарт ISO 8601 определяет понятные, простые и надежные форматы для обмена значениями даты и времени в виде текста. Они легко читаются как машинами, так и людьми разных культур.
Классы java.time по умолчанию используют ISO 8601 при синтаксическом анализе/генерации текста.
Идеальным решением является информирование издателя ваших данных о преимуществах использования ISO 8601.
Ваш вклад должен быть: 2022-04-01T00:00
.
Спасибо Locale locale = Locale.of( "en" , "US" ) ;
не работает с Java8?
@Prateek Тогда используйте более старую технику для создания экземпляра Locale
.
С чего вы взяли, что это проблема локализации?
У вас есть несколько ошибок в DateTimeFormatter
.
java.time.format.DateTimeParseException: текст «1 апреля 2022 г., 12:00:00:000AM» не удалось проанализировать по индексу 4
Как написал @Basil в своем ответе, вам лучше передать Locale
для правильного анализа вашей даты, поскольку Apr
не будет анализироваться, если вы используете другую систему, например, итальянскую, которая принимает только apr
.
Наконец, у вас есть двоеточие (:
) для разделения секунд и миллисекунд, а ваше значение содержит точку (.
).
Это фиксированная версия вашего кода:
public class Demo {
public static void main(String[] args) {
String value = "Apr 1 2022 12:00:00:000AM";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM d yyyy hh:mm:ss:SSSa", Locale.US);
LocalDateTime dateTime = LocalDateTime.parse(value, formatter);
System.out.println(dateTime);
}
}
Отказ от ответственности: я внес изменения только в ваш DateTimeFormatter
, предполагая, что вам придется придерживаться этого конкретного формата. Однако если вы создаете значения для анализа, не добавляйте лишние пробелы и используйте стандартные символы для разделения каждой группы.
Я писал свой ответ с ошибкой пробела и точки, когда @Basil опубликовал свой ответ только с решением Locale
. Я был недостаточно быстр.
Будут ли их строки дат с двухзначными датами иметь один пробел или два? Apr 13
или Apr 13
?
@SotiriosDelimanolis форматтеру все равно понадобятся два пробела независимо от количества цифр в дне (Apr 1
или Apr 11
), если им нужно/хотят соблюдать этот формат.
Это то, чего я добивался. Если их даты будут иметь вид Apr 13
, то ваш формат подойдет. Но если второй пробел предназначен для механизма выравнивания, а их даты на самом деле заканчиваются как Apr 13
(одиночный пробел), тогда ваш формат не будет работать.
@Sotirios Delimanolis Нет, это не уловка, чтобы восполнить пропущенную цифру. Это работает как для Apr 1
, так и для Apr 13
. Попробуйте это на onecompiler.com/java/42kbe6jh5
Это не работает для Apr 13
(разделитель одинарного пробела). Я говорю, что мы не знаем, как форматируются строки OP для дат, отличных от однозначных цифр.
@SotiriosDelimanolis ок, я наконец-то понял твою точку зрения. Я могу основывать свой ответ только на том, что опубликовано в вопросе. Как я сказал в своем ответе: «предполагая, что вам придется придерживаться этого конкретного формата». Я предполагаю, что это (двойное пространство) формат, который они хотят/должны использовать (по какой-либо причине).
Кстати, необязательный пробел в шаблоне может быть представлен как "MMM [ ]d yyyy hh:mm:ss:SSSa"
Вопрос, который вы задаете в заголовке и первом предложении, не связан с упомянутой вами ошибкой. Ваш фрагмент кода не преобразуется в другой формат, он просто пытается проанализировать дату в одном конкретном формате и терпит неудачу.