it-swarm.com.ru

Отформатируйте дату, используя новый 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 не позволяет нам использовать некоторые шаблоны букв?

93
user2336315

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

165
James_D

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

32
Meno Hochschild

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

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

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

3
isapir