Formato instantáneo a cadena

227

Estoy tratando de formatear un Instant a una Cadena usando la nueva API de tiempo de Java 8 y un patrón:

Instant instant = ...;
String out = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(instant);

Usando el código anterior, obtengo una excepción que se queja de un campo no compatible:

java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: YearOfEra
    at java.time.Instant.getLong(Instant.java:608)
    at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
    ...
Trozo de cuero
fuente

Respuestas:

310

Zona horaria

Para formatear una Instantuna zona de tiempo se requiere. Sin una zona horaria, el formateador no sabe cómo convertir el instante en campos de fecha y hora humanos y, por lo tanto, lanza una excepción.

La zona horaria se puede agregar directamente al formateador usando withZone().

DateTimeFormatter formatter =
    DateTimeFormatter.ofLocalizedDateTime( FormatStyle.SHORT )
                     .withLocale( Locale.UK )
                     .withZone( ZoneId.systemDefault() );

Generando cadena

Ahora usa ese formateador para generar la representación de cadena de tu Instant.

Instant instant = Instant.now();
String output = formatter.format( instant );

Volcado a la consola.

System.out.println("formatter: " + formatter + " with zone: " + formatter.getZone() + " and Locale: " + formatter.getLocale() );
System.out.println("instant: " + instant );
System.out.println("output: " + output );

Cuando corre

formatter: Localized(SHORT,SHORT) with zone: US/Pacific and Locale: en_GB
instant: 2015-06-02T21:34:33.616Z
output: 02/06/15 14:34
JodaStephen
fuente
51
¡¡Gracias!! Por cierto, la excepción "campo no admitido" que señala, por ejemplo, año, es espectacularmente obtusa. ¡Tal vez esta situación debería ser detectada y debería lanzarse una excepción que apunta directamente a la identificación de la zona que falta en el Instant!
davidbak
1
Más extrañamente, si incluye .withZone (por ejemplo, .withZone (ZoneId.of ("Z"))) y formatea un LocalDateTime, ¡la zona está IGNORADA! Por lo tanto, siempre que se incluya .withZone (), se puede usar el mismo formateador para Instant y LocalDateTime, sin afectar el tiempo que se muestra para este último.
Glenn
1
¿Por qué es tan difícil aceptar el hecho de que Instant tiene una TimeZone que es GMT?
Koray Tugay
1
@KorayTugay Debido a que si bien los comentarios pedantes "Instantáneo ya es GMT" pueden ser ciertos, están lejos de ser útiles cuando se enfrenta un seguimiento de excepción dado porque formatear un Instant sin especificar una zona horaria no funciona. Hubiera sido agradable si el formateador se hubiera predeterminado a GMT, pero bueno.
Ti Strga
Ahora esto le da yyyy-MM-dd hh:mm:ssformato:DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").withZone(ZoneId.of("Europe/Paris"));
WesternGun
39
public static void main(String[] args) {

    DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
            .withZone(ZoneId.systemDefault());

    System.out.println(DATE_TIME_FORMATTER.format(new Date().toInstant()));

}
二叉树
fuente
3
Si bien este código puede responder la pregunta, proporcionar un contexto adicional sobre cómo y por qué resuelve el problema mejoraría el valor a largo plazo de la respuesta.
Alexander
1
Si bien este fragmento de código puede resolver la pregunta, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo la pregunta para los lectores en el futuro, y que esas personas podrían no conocer los motivos de su sugerencia de código.
Rosário Pereira Fernandes
23
DateTimeFormatter.ISO_INSTANT.format(Instant.now())

Esto le ahorra tener que convertir a UTC. Sin embargo, los marcos de tiempo de otros idiomas pueden no ser compatibles con los milisegundos, por lo que debe hacerlo

DateTimeFormatter.ISO_INSTANT.format(Instant.now().truncatedTo(ChronoUnit.SECONDS))
Arquímedes Trajano
fuente
¿Qué quiere decir con "marcos de tiempo de otro idioma"? ¿ISO_INSTANT.format () se truncará automáticamente a segundos?
Guangtong Shen
Algunos marcos esperan solo el tiempo para subir a los segundos porque no siguen la convención ISO completa y se rompen cuando hay milisegundos como parte de la cadena de entrada.
Arquímedes Trajano
21

La Instantclase no contiene información de zona, solo almacena la marca de tiempo en milisegundos de la época UNIX, es decir, el 1 de enero de 1070 de UTC. Entonces, el formateador no puede imprimir una fecha porque la fecha siempre se imprime para la zona horaria concreta. Debe configurar la zona horaria en formateador y todo estará bien, así:

Instant instant = Instant.ofEpochMilli(92554380000L);
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).withLocale(Locale.UK).withZone(ZoneOffset.UTC);
assert formatter.format(instant).equals("07/12/72 05:33");
assert instant.toString().equals("1972-12-07T05:33:00Z");
Sergey Ponomarev
fuente
4

O si aún desea usar el formateador creado a partir del patrón, puede usar LocalDateTime en lugar de Instant:

LocalDateTime datetime = LocalDateTime.now();
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(datetime)
Nik.exe
fuente
4

Los instantes ya están en UTC y ya tienen un formato de fecha predeterminado de aaaa-MM-dd . Si está contento con eso y no quiere meterse con las zonas horarias o el formato, también podría toString():

Instant instant = Instant.now();
instant.toString()
output: 2020-02-06T18:01:55.648475Z


¿No quieres la T y la Z? (Z indica que esta fecha es UTC. Z significa "Zulu", también conocido como "Compensación de hora cero", también conocido como UTC):

instant.toString().replaceAll("[TZ]", " ")
output: 2020-02-06 18:01:55.663763


¿Quieres milisegundos en lugar de nanosegundos? (Para que pueda colocarlo en una consulta SQL):

instant.truncatedTo(ChronoUnit.MILLIS).toString().replaceAll("[TZ]", " ")
output: 2020-02-06 18:01:55.664

etc.

cs_pupil
fuente
-3
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
String text = date.toString(formatter);
LocalDate date = LocalDate.parse(text, formatter);

Creo que esto podría ayudar, es posible que deba usar algún tipo de variación de fecha local en lugar de instantánea


fuente