¿Cuál es este formato de fecha? 2011-08-12T20: 17: 46.384Z

385

Tengo la siguiente fecha: 2011-08-12T20:17:46.384Z. ¿Qué formato es este? Estoy tratando de analizarlo con Java 1.4 a través DateFormat.getDateInstance().parse(dateStr)y estoy obteniendo

java.text.ParseException: fecha no analizable: "2011-08-12T20: 17: 46.384Z"

Creo que debería usar SimpleDateFormat para analizar, pero primero tengo que conocer la cadena de formato. Todo lo que tengo para eso hasta ahora es yyyy-MM-dd, porque no sé qué Tsignifica en esta cadena, ¿algo relacionado con la zona horaria? Esta cadena de fecha proviene de la lcmis:downloadedOnetiqueta que se muestra en el tipo de medio de historial de descargas de archivos CMIS .

Sarah Vessels
fuente
44
Es ISO 8601
Tomasz Nurkiewicz
3
@TomaszNurkiewicz, no lo es. ISO8601 no tiene la Z al final.
t1gor
55
La gama ISO8601 permite una Z al final. Vea el enlace de arriba, busque UTC.
Jonathan Rosenne
2
@ t1gor El Zal final es la abreviatura de Zuluy significa UTC . Este formato ciertamente forma parte de la colección ISO 8601 de formatos de texto estándar de fecha y hora. Por cierto, estos formatos estándar se usan por defecto en las clases java.time .
Basil Bourque
3
Para su información, las viejas clases problemáticas de fecha y hora como java.util.Date, java.util.Calendary java.text.SimpleDateFormatahora son heredadas , suplantadas por las clases java.time integradas en Java 8 y Java 9. Consulte el Tutorial de Oracle .
Basil Bourque

Respuestas:

513

La T es solo un literal para separar la fecha de la hora, y la Z significa "desplazamiento de hora cero", también conocido como "hora Zulú" (UTC). Si sus cadenas siempre tienen una "Z", puede usar:

SimpleDateFormat format = new SimpleDateFormat(
    "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("UTC"));

O usando Joda Time , puedes usar ISODateTimeFormat.dateTime().

Jon Skeet
fuente
77
¿Por qué necesitamos las comillas simples Ty Z?
Maroun
77
@MarounMaroun: Básicamente queremos esos caracteres literales . Puede que no sea necesario para T(no recuerdo cómo SimpleDateFormat maneja especificadores desconocidos) pero Zqueremos que sea el carácter 'Z' en lugar de "un valor de desplazamiento UTC" (por ejemplo, "00").
Jon Skeet
2
@nyedidikeke: en la página de Wikipedia a la que se vinculó, muestra "zona horaria zulú" para UTC. No estoy seguro de lo que crees que estás corrigiendo.
Jon Skeet
99
@ JonSkeet: puede generar un debate innecesario; no disputó su respuesta, pero tenía la intención de llamar la atención sobre la Z que obtuvo su letra inicial de "cero desplazamiento UTC". La letra Z se conoce como "Zulu" en el alfabeto fonético de la OTAN . A su vez, el enfoque militar para referirse al desplazamiento UTC cero está anclado en la letra Z, que identifican como zulú, lo que le otorga su nombre codificado: zona horaria zulú. Es importante tener en cuenta que la Z no ha perdido su significado y sigue siendo el designador de zona para el desplazamiento UTC cero, ya que la zona horaria Zulu (de Z) es simplemente un lenguaje codificado heredado para referirse a ella.
nyedidikeke
2
@nyedidikeke: sigo en desacuerdo sobre si a alguien más le importará la distinción con referencia a mi respuesta, pero la actualicé. Sin embargo, no voy a entrar en todos los detalles, ya que la historia es ampliamente irrelevante para la respuesta.
Jon Skeet
86

tl; dr

El formato estándar ISO 8601 es utilizado por su cadena de entrada.

Instant.parse ( "2011-08-12T20:17:46.384Z" ) 

ISO 8601

Este formato está definido por el estándar práctico sensible, ISO 8601 .

El Tsepara la parte de fecha desde la porción de tiempo de días. El Zfinal significa UTC (es decir, un desplazamiento desde UTC de cero horas-minutos-segundos). El Zse pronuncia "Zulu" .

java.time

Las antiguas clases de fecha y hora incluidas con las primeras versiones de Java han demostrado ser deficientemente diseñadas, confusas y problemáticas. Evítales.

En su lugar, use java.time marco integrado en Java 8 y posteriores. Las clases java.time suplantan tanto a las antiguas clases de fecha y hora como a la exitosa biblioteca Joda-Time.

Las clases java.time usan ISO 8601 de manera predeterminada al analizar / generar representaciones textuales de valores de fecha y hora.

La Instantclase representa un momento en la línea de tiempo en UTC con una resolución de nanosegundos . Esa clase puede analizar directamente su cadena de entrada sin molestarse en definir un patrón de formato.

Instant instant = Instant.parse ( "2011-08-12T20:17:46.384Z" ) ;

Tabla de tipos de fecha y hora en Java, tanto modernos como heredados


Acerca de java.time

El marco java.time está integrado en Java 8 y versiones posteriores. Estas clases suplantar la vieja problemáticos heredados clases de fecha y hora como java.util.Date, Calendar, y SimpleDateFormat.

El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases java.time .

Para obtener más información, consulte el Tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .

¿Dónde obtener las clases java.time?

  • Java SE 8 , Java SE 9 y posterior
    • Incorporado.
    • Parte de la API Java estándar con una implementación integrada.
    • Java 9 agrega algunas características menores y correcciones.
  • Java SE 6 y Java SE 7
    • Gran parte de la funcionalidad java.time se transfiere a Java 6 y 7 en ThreeTen-Backport .
  • Androide
    • Versiones posteriores de las implementaciones de paquetes de Android de las clases java.time.
    • Para principios de Android, el ThreeTenABP proyecto se adapta ThreeTen-Backport (mencionado anteriormente). Ver Cómo utilizar ThreeTenABP ... .

Tabla de qué biblioteca java.time usar con qué versión de Java o Android

El proyecto ThreeTen-Extra extiende java.time con clases adicionales. Este proyecto es un campo de pruebas para posibles adiciones futuras a java.time. Usted puede encontrar algunas clases útiles aquí, como Interval, YearWeek, YearQuarter, y más .

Albahaca Bourque
fuente
3
@star El "zulú" proviene de la tradición militar y de la aviación, donde 25 letras del alfabeto AZ (sin "J"), cada letra con un nombre pronunciable, representa su versión de zonas horarias . La zona "Zulú" es cero horas compensadas de UTC. Mira esto y esto .
Basil Bourque
27

No estoy seguro sobre el análisis de Java, pero eso es ISO8601: http://en.wikipedia.org/wiki/ISO_8601

smparkes
fuente
¿Es este "2016-01-27T17: 44: 55UTC", ISO8601 también?
user1997292
No lo creo Está cerca, pero UTC como sufijo no está permitido. Tiene que ser Z o un desplazamiento de zona horaria, por ejemplo, +0100. Sin embargo, Z y UTC tienen el mismo significado, por lo que cambiar el UTC a Z produciría un ISO 8601. válido
smparkes
9

Hay otras formas de analizarlo en lugar de la primera respuesta. Para analizarlo:

(1) Si desea obtener información sobre la fecha y la hora, puede analizarlo en un objeto ZonedDatetime(desde Java 8 ) o Date(antiguo):

// ZonedDateTime's default format requires a zone ID(like [Australia/Sydney]) in the end.
// Here, we provide a format which can parse the string correctly.
DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME;
ZonedDateTime zdt = ZonedDateTime.parse("2011-08-12T20:17:46.384Z", dtf);

o

// 'T' is a literal.
// 'X' is ISO Zone Offset[like +01, -08]; For UTC, it is interpreted as 'Z'(Zero) literal.
String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX";

// since no built-in format, we provides pattern directly.
DateFormat df = new SimpleDateFormat(pattern);

Date myDate = df.parse("2011-08-12T20:17:46.384Z");

(2) Si no le importa la fecha y la hora y solo desea tratar la información como un momento en nanosegundos, puede usar Instant:

// The ISO format without zone ID is Instant's default.
// There is no need to pass any format.
Instant ins = Instant.parse("2011-08-12T20:17:46.384Z");
VeryLazyBoy
fuente
1

Si están buscando una solución para Android, pueden usar el siguiente código para obtener los segundos de la cadena de la marca de tiempo.

public static long timestampToEpochSeconds(String srcTimestamp) {
    long epoch = 0;

    try {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            Instant instant = Instant.parse(srcTimestamp);
            epoch = instant.getEpochSecond();
        } else {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSSSS'Z'", Locale.getDefault());
            sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
            Date date = sdf.parse(srcTimestamp);
            if (date != null) {
                epoch = date.getTime() / 1000;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    return epoch;
}

Entrada de muestra: 2019-10-15T05: 51: 31.537979Z

Salida de muestra: 1571128673

Rahul Raveendran
fuente
0

Puedes usar el siguiente ejemplo.

    String date = "2011-08-12T20:17:46.384Z";

    String inputPattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

    String outputPattern = "yyyy-MM-dd HH:mm:ss";

    LocalDateTime inputDate = null;
    String outputDate = null;


    DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern(inputPattern, Locale.ENGLISH);
    DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern(outputPattern, Locale.ENGLISH);

    inputDate = LocalDateTime.parse(date, inputFormatter);
    outputDate = outputFormatter.format(inputDate);

    System.out.println("inputDate: " + inputDate);
    System.out.println("outputDate: " + outputDate);
Salih Kesepara
fuente
No deberías poner apóstrofos alrededor del Z. Eso significa esperar pero ignorar esa carta. Pero esa carta no debe ser ignorada. Esa carta proporciona información valiosa, el hecho de que la cadena estaba destinada a UTC, un desplazamiento de cero. Su formato está descartando este hecho importante. Además, no hay necesidad de molestarse en definir este patrón de formato. Se incorpora un formateador para este patrón.
Basil Bourque
-1

Esta técnica traduce java.util.Date al formato UTC (o cualquier otro) y viceversa.

Defina una clase así:

import java.util.Date;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class UtcUtility {

public static DateTimeFormatter UTC = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZoneUTC();


public static Date parse(DateTimeFormatter dateTimeFormatter, String date) {
    return dateTimeFormatter.parseDateTime(date).toDate();
}

public static String format(DateTimeFormatter dateTimeFormatter, Date date) {
    return format(dateTimeFormatter, date.getTime());
}

private static String format(DateTimeFormatter dateTimeFormatter, long timeInMillis) {
    DateTime dateTime = new DateTime(timeInMillis);
    String formattedString = dateTimeFormatter.print(dateTime);
    return formattedString;
}

}

Entonces úsalo así:

Date date = format(UTC, "2020-04-19T00:30:07.000Z")

o

String date = parse(UTC, new Date())

También puede definir otros formatos de fecha si lo requiere (no solo UTC)

Gapmeister66
fuente
Ambos java.util.Datey el proyecto Joda-Time fueron suplantados hace años por las modernas clases java.time definidas en JSR 310. El consejo aquí está desactualizado hace mucho tiempo.
Basil Bourque
java.time se introdujo en Java 8. Esta pregunta hace referencia específicamente a Java 1.4 y, por lo tanto, he evitado deliberadamente el uso de las clases más nuevas. Esta solución satisface las bases de código más antiguas. Supongo que el autor podría mover su base de código a Java 8, pero eso no siempre es sencillo, eficiente o necesario.
Gapmeister66
-1

@ John-Skeet me dio la pista para solucionar mi propio problema en torno a esto. Como programador más joven, este pequeño problema es fácil de perder y difícil de diagnosticar. Así que lo comparto con la esperanza de que ayude a alguien.

Mi problema era que quería analizar la siguiente cadena contraviniendo una marca de tiempo de un JSON sobre el que no tengo influencia y ponerla en variables más útiles. Pero seguí recibiendo errores.

Entonces, dado lo siguiente (preste atención al parámetro de cadena dentro de Pattern ();

String str = "20190927T182730.000Z"

LocalDateTime fin;
fin = LocalDateTime.parse( str, DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss.SSSZ") );

Error:

Exception in thread "main" java.time.format.DateTimeParseException: Text 
'20190927T182730.000Z' could not be parsed at index 19

¿El problema? La Z al final del Patrón debe estar envuelta en 'Z' al igual que la 'T'. Cambiar "yyyyMMdd'T'HHmmss.SSSZ"a "yyyyMMdd'T'HHmmss.SSS'Z'" y funciona.

Eliminar la Z del patrón en conjunto también condujo a errores.

Francamente, esperaría que una clase Java hubiera anticipado esto.

spencemw
fuente
1
Esto ya ha sido preguntado y respondido aquí y aquí . Y tu solución está mal. Mientras que Tes un literal y necesita ser citado, Zes un desplazamiento (de cero) y debe analizarse como tal, o obtendrá resultados incorrectos. No sé a qué te refieres con que no se ha previsto, creo que sí.
Ole VV
1
No, no, no, no ignores el Z. Estás descartando información valiosa. ¡Procesar un valor de fecha y hora mientras se ignora la zona horaria o el desplazamiento desde UTC es como procesar una cantidad de dinero mientras se ignora la moneda!
Basil Bourque
1
El Tsimplemente separa la parte de la fecha de la parte de la hora del día, y no agrega ningún significado. El Zpor otro lado definitivamente agrega significado.
Basil Bourque
Ole, gracias por los enlaces. Ciertamente los leeré. Y acepto que ciertamente podría estar equivocado cuando era joven. Todo lo que digo es que envolviendo la Z entre comillas simples como un carácter en el patrón resolvió el error. Mi crítica es que quien diseñó parte de la clase "patrón" podría haber codificado alrededor de la presencia o ausencia de 'Z' (¿o una zona horaria de diferencia?) Y 'T' envuelto '' o no. Porque no lo hicieron único. El formato con el que estoy tratando proviene de JSON de una API comercial analizada en una cadena. Pero necesito analizar todo eso en el calendario de fecha y hora, etc. para que sean más útiles.
spencemw