Sformatuj datę za pomocą nowego interfejsu API daty i czasu

118

Bawiłem się nowym API daty i czasu, ale podczas uruchamiania tego:

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);
    }
}

Rzuca:

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)

Patrząc na kod źródłowy klasy LocalDate, widzę:

  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);
    }

Jak opisano w dokumencie:

Ta metoda utworzy program formatujący oparty na prostym wzorze liter i symboli zgodnie z opisem w dokumentacji klasy.

I wszystkie te litery są zdefiniowane .

Dlaczego więc DateTimeFormatter.ofPatternnie pozwala nam użyć niektórych wzorców?

user2336315
źródło

Odpowiedzi:

219

LocalDatereprezentuje tylko datę, a nie DateTime. Zatem „HH: mm: ss” nie ma sensu podczas formatowania pliku LocalDate. Użyj LocalDateTimezamiast tego, zakładając, że chcesz przedstawić zarówno datę, jak i godzinę.

James_D
źródło
3
Jak mogę głosować za tą odpowiedzią i negatywnie oceniać fakt, że istnieje zarówno obiekt LocalDate, jak i obiekt LocalDateTime ...
Xials
Chciałbym po prostu popracować z LocalTime, jak wykonać formatowanie bez java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfWeek
wpadania
Nieważne: to działaDateTimeFormatter.ofPattern("HH:mm:ss")
samuel owino
36

Chciałbym dodać następujące szczegóły do ​​poprawnej odpowiedzi @James_D:

Kontekst: Większość bibliotek daty i czasu ( java.util.Calendarw Javie, patrz także .Net-DateTime, DateJavaScript lub DateTimePerl) jest opartych na koncepcji uniwersalnego, uniwersalnego, unikalnego typu czasowego (w języku niemieckim występuje poetyckie wyrażenie " eierlegende Wollmilchsau ”). W tym projekcie nie może być nieobsługiwanego pola. Ale cena jest wysoka: wiele problemów czasowych nie może być odpowiednio rozwiązanych przy tak nieelastycznym podejściu, ponieważ trudno jest znaleźć wspólny mianownik dla wszystkich rodzajów obiektów czasowych.

JSR-310 wybrał inny sposób , a mianowicie zezwolenie na różne typy czasowe, które składają się z zestawów obsługiwanych pól wbudowanych specyficznych dla typu. Naturalną konsekwencją jest to, że nie każde możliwe pole jest obsługiwane przez każdy typ (a użytkownicy mogą nawet definiować własne wyspecjalizowane pola). Możliwe jest również programowe zapytanie każdego typu obiektu o TemporalAccessorokreślony zestaw obsługiwanych pól. Dla LocalDateznajdziemy:

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 

Brak pola HOUR_OF_DAY wyjaśniającego problem UnsupportedTemporalTypeException. A jeśli spojrzymy na JSR-310- mapowanie symboli wzorców na pola , zobaczymy, że symbol H jest odwzorowany na nieobsługiwany 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);    
}

To odwzorowanie pola nie oznacza, że ​​pole jest obsługiwane przez konkretny typ. Parsowanie przebiega w kilku krokach. Mapowanie pola to tylko pierwszy krok. Drugim krokiem jest przetworzenie do surowego obiektu typuTemporalAccessor . I na koniec analizuje delegatów do typu docelowego (tutaj LocalDate:) i pozwala mu zdecydować, czy akceptuje wszystkie wartości pól w przeanalizowanym obiekcie pośrednim.

Meno Hochschild
źródło
4
en.wiktionary.org/wiki/eierlegende_Wollmilchsau (dosłownie „locha wełniana z mlekiem składająca jaja”) Urządzenie lub osoba typu „wszystko w jednym”, które ma (lub twierdzi, że posiada) tylko pozytywne cechy i które może (lub próbuje) wykonać pracę kilku specjalistycznych narzędzi. :-)
Trevor Robinson
6

Właściwa dla mnie klasa ZonedDateTimeobejmowała zarówno czas, jak i strefę czasową.

LocalDatenie ma informacji o czasie, więc otrzymasz plik UnsupportedTemporalTypeException: Unsupported field: HourOfDay.

Możesz użyć, LocalDateTimeale nie masz informacji o strefie czasowej, więc jeśli spróbujesz uzyskać do nich dostęp (nawet przy użyciu jednego z predefiniowanych elementów formatujących), otrzymasz plik UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds.

isapir
źródło