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

Estoy intentando convertir un String con formato ISO 8601 a java.util.Date .

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

Sin embargo, al utilizar 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+0100 , sin 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é la magia de Joda-Time , que también se describe 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.

Desafortunadamente, los formatos de zona horaria disponibles para SimpleDateFormat (Java 6 y versiones anteriores) no son compatibles con ISO 8601 . 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 tienen horas completas fuera de horario UTC , por lo que la cadena no termina necesariamente 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") le proporcionará un objeto de Calendar y simplemente puede usar getTime () si necesita un objeto de Date .

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

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

He estado buscando una 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.
  • Las respuestas que involucren javax.xml no funcionarán 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 tendrá este 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: instanciar nuevo SimpleDateFormat cada vez como medio para evitar un error en Android 2.1. Si estás tan asombrado como yo, 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).

La forma en que está bendecida 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); 

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

java.time

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

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

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

Si su entrada puede ser otro valor de compensación de UTC en lugar de UTC indicado por Z (Zulu) al final, use la clase OffsetDateTime para analizar.

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

A continuación, extraiga un Instant y java.util.Date en java.util.Date llamando from .

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

La biblioteca Jackson-databind también tiene una clase ISO8601DateFormat que hace eso (implementación real en ISO8601Utils .

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

tl; dr

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

Usando java.time

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

La clase OffsetDateTime representa un momento en la línea de tiempo con un desplazamiento desde UTC pero no un huso horario.

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

Llamar a String genera una cadena en formato estándar ISO 8601:

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

Para ver el mismo valor a través del objective de UTC, extraiga un Instant o ajuste el desplazamiento 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. Una zona horaria es un historial de valores de compensación de UTC 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

El marco java.time está integrado en Java 8 y posterior. Estas clases suplantan a las problemáticas antiguas clases heredadas 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 .

Puede intercambiar objetos java.time directamente con su base de datos. Use un controlador JDBC que cumpla con JDBC 4.2 o posterior. Sin necesidad de cadenas, sin necesidad de clases java.sql.* .

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

  • Java SE 8 , Java SE 9 , Java SE 10 y posterior
    • 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 proyecto ThreeTenABP adapta ThreeTen-Backport (mencionado anteriormente). Consulte Cómo utilizar ThreeTenABP ….

El proyecto ThreeTen-Extra amplía 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 , YearQuarter y más .


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

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)

Creo que deberíamos usar

 DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") 

para la fecha 2010-01-01T12:00:00Z

Otra forma muy simple de analizar las marcas de tiempo ISO8601 es usar org.apache.commons.lang.time.DateUtils :

 import static org.junit.Assert.assertEquals; import java.text.ParseException; import java.util.Date; import org.apache.commons.lang.time.DateUtils; import org.junit.Test; public class ISO8601TimestampFormatTest { @Test public void parse() throws ParseException { Date date = DateUtils.parseDate("2010-01-01T12:00:00+01:00", new String[]{ "yyyy-MM-dd'T'HH:mm:ssZZ" }); assertEquals("Fri Jan 01 12:00:00 CET 2010", date.toString()); } } 

java.time

Tenga en cuenta que en Java 8, puede utilizar la clase java.time.ZonedDateTime y su método de parse(CharSequence text) estático parse(CharSequence text) .

Enfrenté el mismo problema y lo resolví con el siguiente código.

  public static Calendar getCalendarFromISO(String datestring) { Calendar calendar = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault()) ; SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()); try { Date date = dateformat.parse(datestring); date.setHours(date.getHours() - 1); calendar.setTime(date); String test = dateformat.format(calendar.getTime()); Log.e("TEST_TIME", test); } catch (ParseException e) { e.printStackTrace(); } return calendar; } 

Anteriormente estaba usando SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.getDefault());

Pero más tarde descubrí que la principal causa de la excepción era el yyyy-MM-dd'T'HH:mm:ss.SSSZ ,

Entonces usé

SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());

Funcionó bien para mí

También puedes usar la siguiente clase:

 org.springframework.extensions.surf.util.ISO8601DateFormat Date date = ISO8601DateFormat.parse("date in iso8601"); 

Enlace al Java Doc – Hierarchy For Package org.springframework.extensions.surf.maven.plugin.util

Apache Jackrabbit utiliza el formato ISO 8601 para las fechas persistentes, y hay una clase de ayuda para analizarlas:

org.apache.jackrabbit.util.ISO8601

Viene con jackrabbit-jcr-commons .

Como otros han mencionado, Android no tiene una buena manera de soportar el análisis / formato de las fechas ISO 8601 usando las clases incluidas en el SDK. He escrito este código varias veces, así que finalmente creé un Gist que incluye una clase DateUtils que admite el formateo y el análisis de las fechas ISO 8601 y RFC 1123. The Gist también incluye un caso de prueba que muestra lo que admite.

https://gist.github.com/mraccola/702330625fad8eebe7d3

La solución para Java 7+ es usar SimpleDateFormat:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);

Este código puede analizar el formato ISO8601 como:

  • 2017-05-17T06:01:43.785Z
  • 2017-05-13T02:58:21.391+01:00

Pero en Java6, SimpleDateFormat no entiende el carácter X y arrojará
IllegalArgumentException: Unknown pattern character 'X'
Necesitamos normalizar la fecha ISO8601 con el formato legible en Java 6 con SimpleDateFormat .

 public static Date iso8601Format(String formattedDate) throws ParseException { try { DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US); return df.parse(formattedDate); } catch (IllegalArgumentException ex) { // error happen in Java 6: Unknown pattern character 'X' if (formattedDate.endsWith("Z")) formattedDate = formattedDate.replace("Z", "+0000"); else formattedDate = formattedDate.replaceAll("([+-]\\d\\d):(\\d\\d)\\s*$", "$1$2"); DateFormat df1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US); return df1.parse(formattedDate); } } 

El método anterior reemplaza [ Z con +0000 ] o [ +01:00 con +0100 ] cuando se produce un error en Java 6 (puede detectar la versión de Java y reemplazar try / catch con la instrucción if).

SimpleDateFormat para JAVA 1.7 tiene un patrón genial para el formato ISO 8601.

Clase SimpleDateFormat

Aquí esta lo que hice:

 Date d = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.ENGLISH).format(System.currentTimeMillis()); 

Java tiene una docena de maneras diferentes de analizar un fecha y hora, como lo demuestran las excelentes respuestas aquí. Pero algo sorprendente es que ninguna de las clases de tiempo de Java implementa completamente ISO 8601.

Con Java 8, recomendaría:

 ZonedDateTime zp = ZonedDateTime.parse(string); Date date = Date.from(zp.toInstant()); 

Eso manejará ejemplos tanto en UTC como con un desplazamiento, como “2017-09-13T10: 36: 40Z” o “2017-09-13T10: 36: 40 + 01: 00”. Lo hará para la mayoría de los casos de uso.

Pero no manejará ejemplos como “2017-09-13T10: 36: 40 + 01”, que es una fecha-hora ISO 8601 válida.
Tampoco maneja la fecha solamente, por ejemplo, “2017-09-13”.

Si tienes que manejarlos, te sugiero usar una expresión regular primero para olfatear la syntax.

Aquí hay una buena lista de ejemplos de ISO 8601 con muchos casos de esquina: https://www.myintervals.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/ I’m no está al tanto de ninguna clase de Java que pueda hacer frente a todos ellos.

Use cadena como LocalDate.parse(((String) data.get("d_iso8601")),DateTimeFormatter.ISO_DATE)

Tenía una necesidad similar: necesitaba poder analizar cualquier fecha compatible con ISO8601 sin conocer el formato exacto de antemano, y quería una solución liviana que también funcionara en Android.

Cuando busqué en Google mis necesidades, me encontré con esta pregunta, y noté que AFAIU, ninguna respuesta se ajustaba por completo a mis necesidades. Así que desarrollé jISO8601 y lo puse en Maven Central.

Solo agrega en tu pom.xml :

  fr.turri jISO8601 0.2  

y entonces estás listo para ir:

 import fr.turri.jiso8601.*; ... Calendar cal = Iso8601Deserializer.toCalendar("1985-03-04"); Date date = Iso8601Deserializer.toDate("1985-03-04T12:34:56Z"); 

Espera ayuda.

Para formatear una fecha como esta, lo siguiente me funcionó en una aplicación basada en Java 6. Hay una clase DateFormat JacksonThymeleafISO8601DateFormat en el proyecto thymeleaf que inserta el colon faltante:

https://github.com/thymeleaf/thymeleaf/blob/40d27f44df7b52eda47d1bc6f1b3012add6098b3/src/main/java/org/thymeleaf/standard/serializer/StandardJavaScriptSerializer.java

Lo usé para la compatibilidad del formato de fecha de ECMAScript.

Hazlo asi:

 public static void main(String[] args) throws ParseException { String dateStr = "2016-10-19T14:15:36+08:00"; Date date = javax.xml.bind.DatatypeConverter.parseDateTime(dateStr).getTime(); System.out.println(date); } 

Aquí está el resultado:

Mié 19 de octubre a las 15:15:36 CST 2016

Cortesía de la función base: @wrygiel.

Esta función puede convertir el formato ISO8601 a la fecha de Java, que puede manejar los valores de desplazamiento. Según la definición de ISO 8601, el desplazamiento se puede mencionar en diferentes formatos.

 ±[hh]:[mm] ±[hh][mm] ±[hh] Eg: "18:30Z", "22:30+04", "1130-0700", and "15:00-03:30" all mean the same time. - 06:30PM UTC 

Esta clase tiene métodos estáticos para convertir

  • Cadena ISO8601 hasta el objeto Fecha (Local TimeZone)
  • Fecha en la cadena ISO8601
  • El horario de verano se calcula automáticamente

Cuerdas de muestra ISO8601

 /* "2013-06-25T14:00:00Z"; "2013-06-25T140000Z"; "2013-06-25T14:00:00+04"; "2013-06-25T14:00:00+0400"; "2013-06-25T140000+0400"; "2013-06-25T14:00:00-04"; "2013-06-25T14:00:00-0400"; "2013-06-25T140000-0400";*/ public class ISO8601DateFormatter { private static final DateFormat DATE_FORMAT_1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); private static final DateFormat DATE_FORMAT_2 = new SimpleDateFormat("yyyy-MM-dd'T'HHmmssZ"); private static final String UTC_PLUS = "+"; private static final String UTC_MINUS = "-"; public static Date toDate(String iso8601string) throws ParseException { iso8601string = iso8601string.trim(); if(iso8601string.toUpperCase().indexOf("Z")>0){ iso8601string = iso8601string.toUpperCase().replace("Z", "+0000"); }else if(((iso8601string.indexOf(UTC_PLUS))>0)){ iso8601string = replaceColon(iso8601string, iso8601string.indexOf(UTC_PLUS)); iso8601string = appendZeros(iso8601string, iso8601string.indexOf(UTC_PLUS), UTC_PLUS); }else if(((iso8601string.indexOf(UTC_MINUS))>0)){ iso8601string = replaceColon(iso8601string, iso8601string.indexOf(UTC_MINUS)); iso8601string = appendZeros(iso8601string, iso8601string.indexOf(UTC_MINUS), UTC_MINUS); } Date date = null; if(iso8601string.contains(":")) date = DATE_FORMAT_1.parse(iso8601string); else{ date = DATE_FORMAT_2.parse(iso8601string); } return date; } public static String toISO8601String(Date date){ return DATE_FORMAT_1.format(date); } private static String replaceColon(String sourceStr, int offsetIndex){ if(sourceStr.substring(offsetIndex).contains(":")) return sourceStr.substring(0, offsetIndex) + sourceStr.substring(offsetIndex).replace(":", ""); return sourceStr; } private static String appendZeros(String sourceStr, int offsetIndex, String offsetChar){ if((sourceStr.length()-1)-sourceStr.indexOf(offsetChar,offsetIndex)<=2) return sourceStr + "00"; return sourceStr; } 

}

Esto pareció funcionar mejor para mí:

 public static Date fromISO8601_( String string ) { try { return new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ssXXX").parse ( string ); } catch ( ParseException e ) { return Exceptions.handle (Date.class, "Not a valid ISO8601", e); } } 

Necesitaba convertir cadenas de fechas JavaScript a / fro a Java. Encontré que lo anterior funciona con la recomendación. Hubo algunos ejemplos que usaban SimpleDateFormat que estaban cerca pero que no parecían ser el subconjunto recomendado por:

http://www.w3.org/TR/NOTE-datetime

y soportado por PLIST y JavaScript Strings y eso es lo que necesitaba.

Esta parece ser la forma más común de cadena ISO8601 y un buen subconjunto.

Los ejemplos que dan son:

 1994-11-05T08:15:30-05:00 corresponds November 5, 1994, 8:15:30 am, US Eastern Standard Time. 1994-11-05T13:15:30Z corresponds to the same instant. 

También tengo una versión rápida:

 final static int SHORT_ISO_8601_TIME_LENGTH = "1994-11-05T08:15:30Z".length (); // 01234567890123456789012 final static int LONG_ISO_8601_TIME_LENGTH = "1994-11-05T08:15:30-05:00".length (); public static Date fromISO8601( String string ) { if (isISO8601 ( string )) { char [] charArray = Reflection.toCharArray ( string );//uses unsafe or string.toCharArray if unsafe is not available int year = CharScanner.parseIntFromTo ( charArray, 0, 4 ); int month = CharScanner.parseIntFromTo ( charArray, 5, 7 ); int day = CharScanner.parseIntFromTo ( charArray, 8, 10 ); int hour = CharScanner.parseIntFromTo ( charArray, 11, 13 ); int minute = CharScanner.parseIntFromTo ( charArray, 14, 16 ); int second = CharScanner.parseIntFromTo ( charArray, 17, 19 ); TimeZone tz ; if (charArray[19] == 'Z') { tz = TimeZone.getTimeZone ( "GMT" ); } else { StringBuilder builder = new StringBuilder ( 9 ); builder.append ( "GMT" ); builder.append( charArray, 19, LONG_ISO_8601_TIME_LENGTH - 19); String tzStr = builder.toString (); tz = TimeZone.getTimeZone ( tzStr ) ; } return toDate ( tz, year, month, day, hour, minute, second ); } else { return null; } } 

 public static int parseIntFromTo ( char[] digitChars, int offset, int to ) { int num = digitChars[ offset ] - '0'; if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); } } } } } } } } return num; } public static boolean isISO8601( String string ) { boolean valid = true; if (string.length () == SHORT_ISO_8601_TIME_LENGTH) { valid &= (string.charAt ( 19 ) == 'Z'); } else if (string.length () == LONG_ISO_8601_TIME_LENGTH) { valid &= (string.charAt ( 19 ) == '-' || string.charAt ( 19 ) == '+'); valid &= (string.charAt ( 22 ) == ':'); } else { return false; } // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 // "1 9 9 4 - 1 1 - 0 5 T 0 8 : 1 5 : 3 0 - 0 5 : 0 0 valid &= (string.charAt ( 4 ) == '-') && (string.charAt ( 7 ) == '-') && (string.charAt ( 10 ) == 'T') && (string.charAt ( 13 ) == ':') && (string.charAt ( 16 ) == ':'); return valid; } 

No lo he comparado, pero supongo que será bastante rápido. Parece funcionar. 🙂

 @Test public void testIsoShortDate() { String test = "1994-11-05T08:15:30Z"; Date date = Dates.fromISO8601 ( test ); Date date2 = Dates.fromISO8601_ ( test ); assertEquals(date2.toString (), date.toString ()); puts (date); } @Test public void testIsoLongDate() { String test = "1994-11-05T08:11:22-05:00"; Date date = Dates.fromISO8601 ( test ); Date date2 = Dates.fromISO8601_ ( test ); assertEquals(date2.toString (), date.toString ()); puts (date); } 

Creo que lo que mucha gente quiere hacer es analizar las cadenas de fechas de JSON. Existe la posibilidad de que, si accede a esta página, desee convertir una fecha JavaScript JSON en una fecha Java.

To show what a JSON date string looks like:

  var d=new Date(); var s = JSON.stringify(d); document.write(s); document.write("
"+d); "2013-12-14T01:55:33.412Z" Fri Dec 13 2013 17:55:33 GMT-0800 (PST)

The JSON date string is 2013-12-14T01:55:33.412Z.

Dates are not covered by JSON spec per say, but the above is a very specific ISO 8601 format, while ISO_8601 is much much bigger and that is a mere subset albeit a very important one.

See http://www.json.org See http://en.wikipedia.org/wiki/ISO_8601 See http://www.w3.org/TR/NOTE-datetime

As it happens I wrote a JSON parser and a PLIST parser both of which use ISO-8601 but not the same bits.

 /* var d=new Date(); var s = JSON.stringify(d); document.write(s); document.write("
"+d); "2013-12-14T01:55:33.412Z" Fri Dec 13 2013 17:55:33 GMT-0800 (PST) */ @Test public void jsonJavaScriptDate() { String test = "2013-12-14T01:55:33.412Z"; Date date = Dates.fromJsonDate ( test ); Date date2 = Dates.fromJsonDate_ ( test ); assertEquals(date2.toString (), "" + date); puts (date); }

I wrote two ways to do this for my project. One standard, one fast.

Again, JSON date string is a very specific implementation of ISO 8601….

(I posted the other one in the other answer which should work for PLIST dates, which are a different ISO 8601 format).

The JSON date is as follows:

 public static Date fromJsonDate_( String string ) { try { return new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ss.SSSXXX").parse ( string ); } catch ( ParseException e ) { return Exceptions.handle (Date.class, "Not a valid JSON date", e); } } 

PLIST files (ASCII non GNUNext) also uses ISO 8601 but no miliseconds so… not all ISO-8601 dates are the same. (At least I have not found one that uses milis yet and the parser I have seen skip the timezone altogether OMG).

Now for the fast version (you can find it in Boon).

 public static Date fromJsonDate( String string ) { return fromJsonDate ( Reflection.toCharArray ( string ), 0, string.length () ); } 

Note that Reflection.toCharArray uses unsafe if available but defaults to string.toCharArray if not.

(You can take it out of the example by replacing Reflection.toCharArray ( string ) with string.toCharArray()).

 public static Date fromJsonDate( char[] charArray, int from, int to ) { if (isJsonDate ( charArray, from, to )) { int year = CharScanner.parseIntFromTo ( charArray, from + 0, from + 4 ); int month = CharScanner.parseIntFromTo ( charArray, from +5, from +7 ); int day = CharScanner.parseIntFromTo ( charArray, from +8, from +10 ); int hour = CharScanner.parseIntFromTo ( charArray, from +11, from +13 ); int minute = CharScanner.parseIntFromTo ( charArray, from +14, from +16 ); int second = CharScanner.parseIntFromTo ( charArray, from +17, from +19 ); int miliseconds = CharScanner.parseIntFromTo ( charArray, from +20, from +23 ); TimeZone tz = TimeZone.getTimeZone ( "GMT" ); return toDate ( tz, year, month, day, hour, minute, second, miliseconds ); } else { return null; } } 

The isJsonDate is implemented as follows:

 public static boolean isJsonDate( char[] charArray, int start, int to ) { boolean valid = true; final int length = to -start; if (length != JSON_TIME_LENGTH) { return false; } valid &= (charArray [ start + 19 ] == '.'); if (!valid) { return false; } valid &= (charArray[ start +4 ] == '-') && (charArray[ start +7 ] == '-') && (charArray[ start +10 ] == 'T') && (charArray[ start +13 ] == ':') && (charArray[ start +16 ] == ':'); return valid; } 

Anyway… my guess is that quite a few people who come here.. might be looking for the JSON Date String and although it is an ISO-8601 date, it is a very specific one that needs a very specific parse.

 public static int parseIntFromTo ( char[] digitChars, int offset, int to ) { int num = digitChars[ offset ] - '0'; if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); if ( ++offset < to ) { num = ( num * 10 ) + ( digitChars[ offset ] - '0' ); } } } } } } } } return num; } 

See https://github.com/RichardHightower/boon Boon has a PLIST parser (ASCII) and a JSON parser.

The JSON parser is the fastest Java JSON parser that I know of.

Independently verified by the Gatling Performance dudes.

https://github.com/gatling/json-parsers-benchmark

 Benchmark Mode Thr Count Sec Mean Mean error Units BoonCharArrayBenchmark.roundRobin thrpt 16 10 1 724815,875 54339,825 ops/s JacksonObjectBenchmark.roundRobin thrpt 16 10 1 580014,875 145097,700 ops/s JsonSmartBytesBenchmark.roundRobin thrpt 16 10 1 575548,435 64202,618 ops/s JsonSmartStringBenchmark.roundRobin thrpt 16 10 1 541212,220 45144,815 ops/s GSONStringBenchmark.roundRobin thrpt 16 10 1 522947,175 65572,427 ops/s BoonDirectBytesBenchmark.roundRobin thrpt 16 10 1 521528,912 41366,197 ops/s JacksonASTBenchmark.roundRobin thrpt 16 10 1 512564,205 300704,545 ops/s GSONReaderBenchmark.roundRobin thrpt 16 10 1 446322,220 41327,496 ops/s JsonSmartStreamBenchmark.roundRobin thrpt 16 10 1 276399,298 130055,340 ops/s JsonSmartReaderBenchmark.roundRobin thrpt 16 10 1 86789,825 17690,031 ops/s 

It has the fastest JSON parser for streams, readers, bytes[], char[], CharSequence (StringBuilder, CharacterBuffer), and String.

See more benchmarks at:

https://github.com/RichardHightower/json-parsers-benchmark