Formatta una data utilizzando la nuova API data e ora


118

Stavo giocando con la nuova API data e ora ma durante l'esecuzione:

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

Lancia:

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)

Quando guardo il codice sorgente della classe LocalDate, vedo:

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

Come descritto nel documento:

Questo metodo creerà un formattatore basato su un semplice modello di lettere e simboli come descritto nella documentazione della classe.

E tutte queste lettere sono definite .

Allora perché DateTimeFormatter.ofPatternnon ci permette di usare alcune lettere modello?

Risposte:


219

LocalDaterappresenta solo una data, non un DateTime. Quindi "HH: mm: ss" non ha senso quando si formatta un file LocalDate. Utilizza LocalDateTimeinvece un , supponendo che tu voglia rappresentare sia una data che un'ora.


3
Come posso votare a favore di questa risposta e sottovalutare il fatto che ci sono sia un oggetto LocalDate che LocalDateTime ...
Xials

Vorrei lavorare solo con LocalTime, come esegui la formattazione senza java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfWeek
incappare

Non importa: funzionaDateTimeFormatter.ofPattern("HH:mm:ss")
samuel owino

36

Vorrei aggiungere i seguenti dettagli alla risposta corretta di @James_D:

Background: la maggior parte delle librerie di data e ora ( java.util.Calendarin Java, vedere anche .Net-DateTime o Datein JavaScript o DateTimein Perl) sono basate sul concetto di un tipo temporale unico universale per tutti gli usi (in tedesco c'è l'espressione poetica " eierlegende Wollmilchsau "). In questo design non può esserci un campo non supportato. Ma il prezzo è alto: molte volte i problemi non possono essere adeguatamente gestiti con un approccio così poco flessibile perché è difficile o impossibile trovare un denominatore comune per tutti i tipi di oggetti temporali.

JSR-310 ha scelto un altro modo , ovvero consentire diversi tipi temporali che consistono in insiemi specifici del tipo di campi integrati supportati. La conseguenza naturale è che non tutti i campi possibili sono supportati da ogni tipo (e gli utenti possono persino definire i propri campi specializzati). È anche possibile richiedere a livello di codice ogni oggetto di tipo TemporalAccessorper il suo insieme specifico di campi supportati. Per LocalDatenoi troviamo:

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 

Non esiste alcun campo HOUR_OF_DAY che spieghi il problema di UnsupportedTemporalTypeException. E se guardiamo la mappatura JSR-310 dei simboli di pattern ai campi , vediamo che il simbolo H è mappato a HOUR_OF_DAY non supportato:

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

Questa mappatura del campo non significa che il campo sia supportato dal tipo di calcestruzzo. L'analisi avviene in diversi passaggi. La mappatura del campo è solo il primo passo. Il secondo passaggio è quindi l'analisi di un oggetto di tipo non elaborato TemporalAccessor. E infine analizzare i delegati per il tipo di destinazione (qui LocalDate:) e lasciare che decida se accetta tutti i valori di campo nell'oggetto intermedio analizzato.


4
en.wiktionary.org/wiki/eierlegende_Wollmilchsau (letteralmente "uova che depongono lana-latte-scrofa") Un dispositivo all-in-one o persona che ha (o afferma di avere) solo attributi positivi e che può (o tenta di) fare il lavoro di diversi strumenti specializzati. :-)
Trevor Robinson

6

La classe giusta per me era ZonedDateTimeche includesse sia l'ora che il fuso orario.

LocalDatenon dispone delle informazioni sull'ora, quindi ottieni un file UnsupportedTemporalTypeException: Unsupported field: HourOfDay.

Puoi usare LocalDateTimema poi non hai le informazioni sul fuso orario quindi se provi ad accedervi (anche usando uno dei formattatori predefiniti) otterrai un file UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.