Отформатируйте дату, используя новый API времени дат

Я играл с новым API времени, но при запуске:

public class Test {         
    public static void main(String[] args){
        String dateFormatted = LocalDate.now()
                                        .format(DateTimeFormatter
                                              .ofPattern("yyyy-MM-dd HH:mm:ss"));
        System.out.println(dateFormatted);
    }
}

Он выбрасывает:

Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay
    at java.time.LocalDate.get0(LocalDate.java:680)
    at java.time.LocalDate.getLong(LocalDate.java:659)
    at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
    at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2543)
    at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2182)
    at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1745)
    at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1719)
    at java.time.LocalDate.format(LocalDate.java:1685)
    at Test.main(Test.java:23)

Когда вы смотрите на исходный код класса LocalDate, я вижу:

  private int get0(TemporalField field) {
        switch ((ChronoField) field) {
            case DAY_OF_WEEK: return getDayOfWeek().getValue();
            case ALIGNED_DAY_OF_WEEK_IN_MONTH: return ((day - 1) % 7) + 1;
            case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ((getDayOfYear() - 1) % 7) + 1;
            case DAY_OF_MONTH: return day;
            case DAY_OF_YEAR: return getDayOfYear();
            case EPOCH_DAY: throw new UnsupportedTemporalTypeException("Invalid field 'EpochDay' for get() method, use getLong() instead");
            case ALIGNED_WEEK_OF_MONTH: return ((day - 1) / 7) + 1;
            case ALIGNED_WEEK_OF_YEAR: return ((getDayOfYear() - 1) / 7) + 1;
            case MONTH_OF_YEAR: return month;
            case PROLEPTIC_MONTH: throw new UnsupportedTemporalTypeException("Invalid field 'ProlepticMonth' for get() method, use getLong() instead");
            case YEAR_OF_ERA: return (year >= 1 ? year : 1 - year);
            case YEAR: return year;
            case ERA: return (year >= 1 ? 1 : 0);
        }
        throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
    }

Как описано в документе:

Этот метод создаст форматировщик, основанный на простой схеме букв и символов, как описано в документации по классу.

И все эти буквы определены.

Итак, почему DateTimeFormatter.ofPattern не позволяет нам использовать некоторые буквы шаблонов?

Ответ 1

LocalDate представляет собой дату, а не DateTime. Так что "HH: mm: ss" не имеет смысла при форматировании a LocalDate. Вместо этого используйте LocalDateTime, предполагая, что вы хотите представить дату и время.

Ответ 2

Я хотел бы добавить следующие данные к правильному ответу @James_D:

Фон: Большинство библиотек даты и времени (java.util.Calendar в Java, см. также .Net-DateTime или Date в JavaScript или DateTime в Perl) основаны на концепция универсального универсального уникального временного типа (на немецком языке есть поэтическое выражение "eierlegende Wollmilchsau" ). В этом дизайне не может быть неподдерживаемого поля. Но цена высока: многие проблемы времени не могут быть адекватно обработаны с помощью такого негибкого подхода, потому что трудно найти общий знаменатель для всех видов временных объектов.

JSR-310 выбрал другой способ, а именно разрешить разные временные типы, которые состоят из наборов встроенных встроенных полей. Естественным следствием является то, что не все возможные поля поддерживаются каждым типом (и пользователи могут даже определять свои собственные специализированные поля). Также возможно запрограммировать по умолчанию каждый объект типа TemporalAccessor для своего определенного набора поддерживаемых полей. Для LocalDate находим:

•DAY_OF_WEEK 
•ALIGNED_DAY_OF_WEEK_IN_MONTH 
•ALIGNED_DAY_OF_WEEK_IN_YEAR 
•DAY_OF_MONTH 
•DAY_OF_YEAR 
•EPOCH_DAY 
•ALIGNED_WEEK_OF_MONTH 
•ALIGNED_WEEK_OF_YEAR 
•MONTH_OF_YEAR 
•PROLEPTIC_MONTH 
•YEAR_OF_ERA 
•YEAR 
•ERA 

Не существует поля HOUR_OF_DAY, которое объясняет проблему UnsupportedTemporalTypeException. И если мы посмотрим на сопоставление символов шаблонов JSR-310- с полями, мы увидим, что символ H отображается на неподдерживаемый HOUR_OF_DAY:

/** Map of letters to fields. */  
private static final Map<Character, TemporalField> FIELD_MAP = new HashMap<>();
static {
  FIELD_MAP.put('G', ChronoField.ERA);
  FIELD_MAP.put('y', ChronoField.YEAR_OF_ERA);
  FIELD_MAP.put('u', ChronoField.YEAR);
  FIELD_MAP.put('Q', IsoFields.QUARTER_OF_YEAR);
  FIELD_MAP.put('q', IsoFields.QUARTER_OF_YEAR);
  FIELD_MAP.put('M', ChronoField.MONTH_OF_YEAR);
  FIELD_MAP.put('L', ChronoField.MONTH_OF_YEAR);
  FIELD_MAP.put('D', ChronoField.DAY_OF_YEAR);
  FIELD_MAP.put('d', ChronoField.DAY_OF_MONTH);
  FIELD_MAP.put('F', ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH);
  FIELD_MAP.put('E', ChronoField.DAY_OF_WEEK);
  FIELD_MAP.put('c', ChronoField.DAY_OF_WEEK);
  FIELD_MAP.put('e', ChronoField.DAY_OF_WEEK);
  FIELD_MAP.put('a', ChronoField.AMPM_OF_DAY);
  FIELD_MAP.put('H', ChronoField.HOUR_OF_DAY);
  FIELD_MAP.put('k', ChronoField.CLOCK_HOUR_OF_DAY);
  FIELD_MAP.put('K', ChronoField.HOUR_OF_AMPM);
  FIELD_MAP.put('h', ChronoField.CLOCK_HOUR_OF_AMPM);
  FIELD_MAP.put('m', ChronoField.MINUTE_OF_HOUR);
  FIELD_MAP.put('s', ChronoField.SECOND_OF_MINUTE);
  FIELD_MAP.put('S', ChronoField.NANO_OF_SECOND);
  FIELD_MAP.put('A', ChronoField.MILLI_OF_DAY);
  FIELD_MAP.put('n', ChronoField.NANO_OF_SECOND);
  FIELD_MAP.put('N', ChronoField.NANO_OF_DAY);    
}

Это сопоставление полей не означает, что поле поддерживается конкретным типом. Разбор происходит в несколько этапов. Отображение поля - это только первый шаг. Затем на втором этапе выполняется синтаксический анализ исходного объекта типа TemporalAccessor. И, наконец, разбор делегатов на целевой тип (здесь: LocalDate) и пусть он решит, принимает ли он все значения полей в анализируемом промежуточном объекте.

Ответ 3

Правильный класс для меня был ZonedDateTime который включает в себя как Time, так и Time Zone.

LocalDate не имеет информации о времени, поэтому вы получаете UnsupportedTemporalTypeException: Unsupported field: HourOfDay.

Вы можете использовать LocalDateTime но тогда у вас нет информации о часовом поясе, поэтому, если вы попытаетесь получить к нему доступ (даже с помощью одного из предопределенных форматеров), вы получите UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds.