Как разобрать строку с датой, но без времени в локальном формате в ZonedDateTime?

Этот вопрос похож на Как анализировать ZonedDateTime с зоной по умолчанию?, но дополнительное условие.

У меня есть строковый параметр, представляющий дату в формате Великобритании: «3/6/09». Он не содержит времени, только дату. Но может содержать его и даже часовой пояс. И я хочу разобрать его на ZonedDateTime.

public static ZonedDateTime parse(String value) {
    DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(SHORT).withLocale(Locale.UK).withZone(ZoneId.systemDefault());
    TemporalAccessor temporalAccessor = formatter.parseBest(value, ZonedDateTime::from, LocalDateTime::from, LocalDate::from);
    if (temporalAccessor instanceof ZonedDateTime) {
        return ((ZonedDateTime) temporalAccessor);
    }
    if (temporalAccessor instanceof LocalDateTime) {
        return ((LocalDateTime) temporalAccessor).atZone(ZoneId.systemDefault());
    }
    return ((LocalDate) temporalAccessor).atStartOfDay(ZoneId.systemDefault());
}

Но это терпит неудачу с исключением:

java.time.format.DateTimeParseException: Text '3/6/2009' could not be parsed at index 6

Это баг у меня или нет?


person Sergey Ponomarev    schedule 15.12.2014    source источник
comment
Основное возражение: вы используете DateTimeFormatter.ofLocalizedDateTime(...), поэтому в вашем вводе должен присутствовать как минимум час или любой компонент времени. Еще одно наблюдение: со старым pre8-кодом Date d = DateFormat.getDateInstance(DateFormat.SHORT, Locale.UK).parse(input); все нормально, а DateTimeFormatter.ofLocalizedDate(...) не работает! Возможно, Oracle изменила формат SHORT для библиотеки Threeten. Вероятно, вам следует предпочесть явный шаблон, а затем повторить попытку. Кстати, такой код с instanceof-выражениями мне порядком резал глаза (извините).   -  person Meno Hochschild    schedule 15.12.2014
comment
Вы также можете поэкспериментировать с DateTimeFormatterBuilder.parseDefaulting() и/или необязательными разделами, чтобы дополнить необязательные части вашего ввода, хотя в прошлом у меня не было только положительного опыта с этим.   -  person Meno Hochschild    schedule 15.12.2014


Ответы (2)


По моему это не баг. Ваш подход ошибочен.

Прежде всего, вы возвращаете ZonedDateTime, поэтому ожидается, что строка содержит полную информацию о дате, времени и зоне. Строка "3/6/09" должна быть преобразована в LocalDate.

Во-вторых, вы делегируете обнаружение формата во время выполнения библиотеке. Опять же, вы должны анализировать/форматировать ожидаемый формат. Ваше приложение должно знать, ожидает ли полная дата и время или частичная (только дата или только время).

В любом случае вам больше повезет, если вы обнаружите формат, а затем будете использовать различные методы анализа.

Только местная дата:

DateTimeFormatter
  .ofLocalizedDate(FormatStyle.SHORT)
  .parse(value, LocalDate::from)`

Дата и время зоны:

DateTimeFormatter
  .ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT)
  .parse(value, ZonedDateTime::from)`
person sargue    schedule 22.12.2014

Используемый формат можно увидеть с помощью метода getLocalizedDateTimePattern():

String fmt = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
    FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.UK);

Результат "dd/MM/yy HH:mm".

Таким образом, формат ожидает даты и времени с разделителем пробелов, поэтому это то, что необходимо предоставить.

Кроме того, формат/анализ предполагает наличие двух цифр для дня месяца и двух цифр для месяца года. Таким образом, вам нужно передать «06.03.09 00:00», чтобы получить ожидаемый результат, и в этом случае вы можете выполнить синтаксический анализ непосредственно в LocalDateTime.

В качестве альтернативы используйте ofLocalizedDate():

DateTimeFormatter formatter =
    DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(Locale.UK);
LocalDate date = LocalDate.parse("03/06/99", formatter);

Обратите внимание, что ввод должен по-прежнему содержать две цифры дня и месяца.

В качестве альтернативы выполните синтаксический анализ с использованием определенного шаблона, который может обрабатывать отсутствующие начальные нули:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yy");
LocalDate date = LocalDate.parse("3/6/99", formatter);
LocalDate date = LocalDate.parse("03/06/99", formatter);
// handles both "3/6/99" and "03/06/99"

Обновление: мягкий анализ также обрабатывает этот случай:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .parseLenient().appendPattern("dd/MM/yy").toFormatter();
LocalDate date = LocalDate.parse("3/6/99", formatter);
LocalDate date = LocalDate.parse("03/06/99", formatter);
// handles both "3/6/99" and "03/06/99"
person JodaStephen    schedule 23.12.2014