Pregunta Conversión de cadenas compatibles con ISO 8601 en java.util.Date


Estoy tratando de convertir un ISO 8601 Cadena formateada a java.util.Date.

Encontré el patrón yyyy-MM-dd'T'HH:mm:ssZ para ser compatible con ISO8601 si se usa con una configuración regional (comparar muestra).

Sin embargo, usando el java.text.SimpleDateFormat, No puedo convertir la cadena correctamente formateada 2010-01-01T12:00:00+01:00. Tengo que convertirlo primero a 2010-01-01T12:00:00+0100sin los dos puntos

Entonces, la solución actual es

SimpleDateFormat ISO8601DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.GERMANY);
String date = "2010-01-01T12:00:00+01:00".replaceAll("\\+0([0-9]){1}\\:00", "+0$100");
System.out.println(ISO8601DATEFORMAT.parse(date));

que obviamente no es tan agradable. ¿Me estoy perdiendo algo o hay una mejor solución?


Responder

Gracias al comentario de JuanZe, encontré el Joda-Time magia, también es descrito aquí.

Entonces, la solución es

DateTimeFormatter parser2 = ISODateTimeFormat.dateTimeNoMillis();
String jtdate = "2010-01-01T12:00:00+01:00";
System.out.println(parser2.parseDateTime(jtdate));

O más simplemente, use el analizador predeterminado a través del constructor:

DateTime dt = new DateTime( "2010-01-01T12:00:00+01:00" ) ;

Para mí, esto es bueno.


548
2018-02-04 17:52


origen


Respuestas:


Lamentablemente, los formatos de zona horaria disponibles para SimpleDateFormat (Java 6 y anteriores) no son ISO 8601 obediente. SimpleDateFormat entiende cadenas de zonas horarias como "GMT + 01: 00" o "+0100", esta última de acuerdo con RFC # 822.

Incluso si Java 7 agrega soporte para las descripciones de zona horaria según ISO 8601, SimpleDateFormat aún no puede analizar correctamente una cadena de fecha completa, ya que no admite piezas opcionales.

Reformatear su cadena de entrada utilizando regexp es ciertamente una posibilidad, pero las reglas de reemplazo no son tan simples como en su pregunta:

  • Algunas zonas horarias no son horas completas UTC, por lo que la cadena no necesariamente termina con ": 00".
  • ISO8601 permite solo el número de horas que se incluirán en la zona horaria, por lo que "+01" es equivalente a "+01: 00"
  • ISO8601 permite el uso de "Z" para indicar UTC en lugar de "+00: 00".

La solución más fácil es posiblemente usar el convertidor de tipos de datos en JAXB, ya que JAXB debe poder analizar la cadena de fechas ISO8601 de acuerdo con la especificación del Esquema XML. javax.xml.bind.DatatypeConverter.parseDateTime("2010-01-01T12:00:00Z") te dará un Calendar objeto y simplemente puede usar getTime () en él, si necesita una Date objeto.

Probablemente podrías usar Joda-Time también, pero no sé por qué deberías molestarte con eso.


423
2018-02-04 18:51



De acuerdo, esta pregunta ya está respondida, pero dejaré mi respuesta de todos modos. Podría ayudar a alguien.

He estado buscando un solución para Android (API 7).

  • Joda estaba fuera de cuestión: es enorme y sufre una inicialización lenta. También parecía una gran exageración para ese propósito en particular.
  • Respuestas que involucran javax.xml no funcionará en Android API 7.

Terminó implementando esta clase simple. Cubre solo la forma más común de cadenas ISO 8601, pero esto debería ser suficiente en algunos casos (cuando esté seguro de que la entrada estará en esta formato).

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * Helper class for handling a most common subset of ISO 8601 strings
 * (in the following format: "2008-03-01T13:00:00+01:00"). It supports
 * parsing the "Z" timezone, but many other less-used features are
 * missing.
 */
public final class ISO8601 {
    /** Transform Calendar to ISO 8601 string. */
    public static String fromCalendar(final Calendar calendar) {
        Date date = calendar.getTime();
        String formatted = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
            .format(date);
        return formatted.substring(0, 22) + ":" + formatted.substring(22);
    }

    /** Get current date and time formatted as ISO 8601 string. */
    public static String now() {
        return fromCalendar(GregorianCalendar.getInstance());
    }

    /** Transform ISO 8601 string to Calendar. */
    public static Calendar toCalendar(final String iso8601string)
            throws ParseException {
        Calendar calendar = GregorianCalendar.getInstance();
        String s = iso8601string.replace("Z", "+00:00");
        try {
            s = s.substring(0, 22) + s.substring(23);  // to get rid of the ":"
        } catch (IndexOutOfBoundsException e) {
            throw new ParseException("Invalid length", 0);
        }
        Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(s);
        calendar.setTime(date);
        return calendar;
    }
}

Nota de rendimiento: I instanciar nuevo SimpleDateFormat cada vez como medio para evitar un insecto en Android 2.1. Si estás tan asombrado como yo estaba, mira este enigma. Para otros motores Java, puede almacenar en caché la instancia en un campo estático privado (usando ThreadLocal, para que sea seguro para la ejecución de subprocesos).


188
2018-05-16 15:16



La forma en que es bendecido por la documentación de Java 7:

DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
String string1 = "2001-07-04T12:08:56.235-0700";
Date result1 = df1.parse(string1);

DateFormat df2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
String string2 = "2001-07-04T12:08:56.235-07:00";
Date result2 = df2.parse(string2);

Puedes encontrar más ejemplos en la sección Ejemplos a SimpleDateFormat javadoc.


168
2017-08-13 19:07



java.time

los java.time API (integrado en Java 8 y posterior), hace esto un poco más fácil.

Si sabes que la entrada está en UTC, tales como el Z (para Zulu) al final, el Instant la clase puede analizar.

java.util.Date date = Date.from( Instant.parse( "2014-12-12T10:39:40Z" ));

Si su entrada puede ser otra offset-from-UTC valores en lugar de UTC indicado por el Z (Zulu) al final, usa el OffsetDateTime clase para analizar.

OffsetDateTime odt = OffsetDateTime.parse( "2010-01-01T12:00:00+01:00" );

Luego extrae un Instanty convertir a un java.util.Date llamando from.

Instant instant = odt.toInstant();  // Instant is always in UTC.
java.util.Date date = java.util.Date.from( instant );

66
2017-12-15 07:52



los Biblioteca de enlace de datos de Jackson también tiene Clase ISO8601DateFormat eso lo hace (implementación real en ISO8601Utils.

ISO8601DateFormat df = new ISO8601DateFormat();
Date d = df.parse("2010-07-28T22:25:51Z");

60
2017-07-05 14:23



tl; dr

OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" )

Usando java.time

El nuevo java.time El paquete en Java 8 y posterior fue inspirado por Joda-Time.

los OffsetDateTime clase representa un momento en la línea de tiempo con un offset-from-UTC pero no un huso horario

OffsetDateTime odt = OffsetDateTime.parse ( "2010-01-01T12:00:00+01:00" );

Vocación toString genera una cadena en formato estándar ISO 8601:

2010-01-01T12: 00 + 01: 00

Para ver el mismo valor a través de la lente de UTC, extraiga un Instant o ajustar la compensación de +01:00 a 00:00.

Instant instant = odt.toInstant();  

…o…

OffsetDateTime odtUtc = odt.withOffsetSameInstant( ZoneOffset.UTC );

Ajústelo en un huso horario si lo desea. UN zona horaria es una historia de offset-from-UTC valores para una región, con un conjunto de reglas para manejar anomalías como el horario de verano (DST). Así que aplique una zona horaria en lugar de un mero desplazamiento siempre que sea posible.

ZonedDateTime zonedDateTimeMontréal = odt.atZoneSameInstant( ZoneId.of( "America/Montreal" ) );

Acerca de java.time

los java.time framework está integrado en Java 8 y posterior. Estas clases suplantan a los viejos problemas legado clases de fecha y hora como java.util.Date, Calendar, Y SimpleDateFormat.

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

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

Puedes intercambiar java.time objetos directamente con su base de datos. Usar una Controlador JDBC quejarse con JDBC 4.2 o después. Sin necesidad de cadenas, sin necesidad de java.sql.* clases

¿Dónde obtener las clases de java.time?

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

los ThreeTen-Extra proyecto extiende java.time con clases adicionales. Este proyecto es un terreno de prueba para posibles adiciones futuras a java.time. Puede encontrar algunas clases útiles aquí, como Interval, YearWeek, YearQuartery Más.



31
2017-12-14 01:48



Para Java versión 7

Puede seguir la documentación de Oracle: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

X - se utiliza para la zona horaria ISO 8601

TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
df.setTimeZone(tz);
String nowAsISO = df.format(new Date());

System.out.println(nowAsISO);

DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
//nowAsISO = "2013-05-31T00:00:00Z";
Date finalResult = df1.parse(nowAsISO);

System.out.println(finalResult);

26
2018-04-08 14:20



La solución DatatypeConverter no funciona en todas las máquinas virtuales. Lo siguiente funciona para mí:

javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar("2011-01-01Z").toGregorianCalendar().getTime()

He descubierto que joda no funciona de la caja (específicamente para el ejemplo que di más arriba con la zona horaria en una fecha, que debería ser válida)


20
2018-04-13 17:27