Formate uma data usando a nova API de data e hora

118

Eu estava brincando com a nova API de data e hora, mas ao executar:

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

Ele lança:

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)

Ao examinar o código-fonte da classe LocalDate, vejo:

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

Conforme descrito no doc:

Este método criará um formatador baseado em um padrão simples de letras e símbolos, conforme descrito na documentação da classe.

E todas essas letras estão definidas .

Então, por DateTimeFormatter.ofPatternque não nos permite usar algumas letras padrão?

user2336315
fonte

Respostas:

219

LocalDaterepresenta apenas uma data, não um DateTime. Portanto, "HH: mm: ss" não faz sentido ao formatar a LocalDate. Em LocalDateTimevez disso, use um , supondo que você deseja representar a data e a hora.

James_D
fonte
3
Como posso votar a favor desta resposta e negar o fato de que há um objeto LocalDate e um LocalDateTime ...
Xials
Eu gostaria de trabalhar apenas com LocalTime, como você executa a formatação sem encontrar essa exceçãojava.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfWeek
samuel owino
Não se preocupe: isso funcionaDateTimeFormatter.ofPattern("HH:mm:ss")
samuel owino
36

Eu gostaria de adicionar os seguintes detalhes à resposta correta de @James_D:

Contexto: A maioria das bibliotecas de data e hora ( java.util.Calendarem Java, consulte também .Net-DateTime ou Dateem JavaScript ou DateTimeem Perl) são baseadas no conceito de um tipo temporal único universal para todos os fins (em alemão, há a expressão poética " eierlegende Wollmilchsau "). Neste design, não pode haver um campo não compatível. Mas o preço é alto: muitas vezes os problemas não podem ser tratados adequadamente com uma abordagem tão inflexível porque é difícil ou impossível encontrar um denominador comum para todos os tipos de objetos temporais.

A JSR-310 escolheu outra maneira , a saber, permitir diferentes tipos temporais que consistem em conjuntos específicos de tipo de campos integrados com suporte. A consequência natural é que nem todos os campos possíveis são suportados por todos os tipos (e os usuários podem até definir seus próprios campos especializados). Também é possível solicitar programaticamente a cada objeto do tipo TemporalAccessorseu conjunto específico de campos com suporte. Pois LocalDateencontramos:

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 

Não há campo HOUR_OF_DAY que explique o problema de UnsupportedTemporalTypeException. E se olharmos para o mapeamento JSR-310- de símbolos de padrão para campos , vemos que o símbolo H é mapeado para HOUR_OF_DAY sem suporte:

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

Este mapeamento de campo não significa que o campo é suportado pelo tipo concreto. A análise ocorre em várias etapas. O mapeamento de campo é apenas a primeira etapa. A segunda etapa é então analisar para um objeto bruto do tipo TemporalAccessor. E, finalmente, analisar delegados para o tipo de destino (aqui LocalDate:) e deixá-lo decidir se aceita todos os valores de campo no objeto intermediário analisado.

Meno Hochschild
fonte
4
en.wiktionary.org/wiki/eierlegende_Wollmilchsau (literalmente "porca de lã e leite para colocar ovos") Um dispositivo ou pessoa tudo-em-um que tem (ou afirma ter) apenas atributos positivos e que pode (ou tenta) fazer o trabalho de várias ferramentas especializadas. :-)
Trevor Robinson
6

A aula certa para mim era ZonedDateTimeaquela que inclui tanto o horário quanto o fuso horário.

LocalDatenão tem as informações de tempo, então você obtém um UnsupportedTemporalTypeException: Unsupported field: HourOfDay.

Você pode usar, LocalDateTimemas não tem as informações de fuso horário, portanto, se tentar acessá-las (mesmo usando um dos formatadores predefinidos), obterá um UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds.

isapir
fonte