Dar formato a una fecha usando la nueva API de fecha y hora

2 minutos de lectura

avatar de usuario
usuario2336315

Estaba jugando con la nueva API de fecha y hora, pero cuando ejecuté esto:

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

lanza:

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)

Al mirar el código fuente de la clase LocalDate, veo:

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

Como se describe en el documento:

Este método creará un formateador basado en un patrón simple de letras y símbolos como se describe en la documentación de la clase.

Y todas estas letras son definido.

Entonces por qué DateTimeFormatter.ofPattern no nos permite usar algunas letras patrón?

LocalDate representa solo una fecha, no un DateTime. Así que “HH:mm:ss” no tiene sentido al formatear un LocalDate. Utilizar una LocalDateTime en su lugar, suponiendo que desea representar tanto una fecha como una hora.

  • ¿Cómo puedo votar a favor de esta respuesta y rechazar el hecho de que hay un objeto LocalDate y LocalDateTime …

    – Xiales

    4 oct 2019 a las 21:47

  • Me gustaría trabajar simplemente con LocalTime, ¿cómo realiza el formateo sin encontrarse con esta excepción? java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfWeek

    – Samuel Owino

    11 de enero de 2020 a las 23:11


  • No importa: esto funciona DateTimeFormatter.ofPattern("HH:mm:ss")

    – Samuel Owino

    11 de enero de 2020 a las 23:22

  • @samuelowino Para mí esto funcionó: DateTimeFormatter.ofPattern("HH:mm").withZone(ZoneId.systemDefault()).format(...). .withZone(…).

    – Ene

    12 oct 2020 a las 16:15


  • he utilizado DateTime dateTime = DateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME.withLocale(defaultLocale).withZone(defaultTimeZone.toZoneId()); esto me pone Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds Como @isapir abajo, a la derecha: ZonedDateTime. Entonces también sirve para esto. Así que terminé con ZonedDateTime dateTime = ZonedDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.RFC_1123_DATE_TIME.withLocale(defaultLocale).withZone(defaultTimeZone.toZoneId());

    – usuario637338

    17 dic 2021 a las 23:49


Me gustaría agregar los siguientes detalles a la respuesta correcta de @James_D:

Fondo: La mayoría de las bibliotecas de fecha y hora (java.util.Calendar en Java, consulte también .Net-DateTime o Date en JavaScript o DateTime en Perl) se basan en el concepto de un tipo temporal único universal para todo uso (en alemán existe la expresión poética “eierlegende Wollmilchsau”). En este diseño no puede haber un campo no soportado. Pero el precio es alto: muchos problemas de tiempo no se pueden manejar adecuadamente con un enfoque tan poco flexible porque es difícil o imposible encontrar un denominador común para todo tipo de objetos temporales.

JSR-310 ha elegido otro camino, es decir, para permitir diferentes tipos temporales que consisten en conjuntos específicos de tipo de campos incorporados admitidos. La consecuencia natural es que no todos los campos posibles son compatibles con todos los tipos (y los usuarios pueden incluso definir sus propios campos especializados). También es posible preguntar programáticamente cada objeto de tipo TemporalAccessor para su conjunto específico de campos admitidos. Para LocalDate encontramos:

•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 

No hay campo HOUR_OF_DAY que explique el problema de UnsupportedTemporalTypeException. Y si nos fijamos en el JSR-310-asignación de símbolos de patrones a campos vemos que el símbolo H está asignado a HOUR_OF_DAY no compatible:

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

Esta asignación de campo no significa que el campo sea compatible con el tipo concreto. El análisis ocurre en varios pasos. El mapeo de campos es solo el primer paso. El segundo paso es analizar un objeto sin formato de tipo TemporalAccessor. Y, finalmente, analizar los delegados al tipo de destino (aquí: LocalDate) y dejar que decida si acepta todos los valores de campo en el objeto intermedio analizado.

  • en.wiktionary.org/wiki/eierlegende_Wollmilchsau (literalmente “lana-leche-cerda que pone huevos”) Un dispositivo o persona todo en uno que tiene (o dice tener) solo atributos positivos y que puede (o intenta) hacer el trabajo de varias herramientas especializadas. 🙂

    – Trevor Robinson

    11 mayo 2017 a las 20:32

avatar de usuario
isapir

La clase correcta para mí fue ZonedDateTime que incluye Hora y Zona horaria.

LocalDate no tiene la información de tiempo, por lo que obtiene un UnsupportedTemporalTypeException: Unsupported field: HourOfDay.

Puedes usar LocalDateTime pero luego no tiene la información de la zona horaria, por lo que si intenta acceder a eso (incluso usando uno de los formateadores predefinidos) obtendrá un UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds.

  • Esta respuesta debe ser la respuesta aceptada o debe agregarse a la respuesta aceptada actual.

    – gourabix

    1 de febrero a las 14:10

  • Inconvertible types; cannot cast 'java.time.LocalDateTime' to 'java.time.ZonedDateTime' :/

    – MarkHu

    26 de febrero a las 0:53


Tuve un problema similar con este código:

DateTimeFormatter.RFC_1123_DATE_TIME.format(new Date().toInstant())

java.time.temporal.UnsupportedTemporalTypeException: campo no admitido: DayOfMonth

Lo resolví de esta manera:

DateTimeFormatter.RFC_1123_DATE_TIME.format(
    ZonedDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()))

¿Ha sido útil esta solución?