Форматирование даты с помощью нового 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 не позволяет нам использовать некоторые буквы шаблона?

3 74

3 ответа:

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

Я хотел бы добавить следующие детали к правильному ответу @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) и пусть он решает, принимает ли он все значения полей в разбираемом промежуточном объекте.

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

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

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