Carácter de patrón ilegal 'T' al analizar una cadena de fecha a java.util.Date


182

Tengo una cadena de fecha y quiero analizarla a la fecha normal, use la API de fecha de Java, el siguiente es mi código:

public static void main(String[] args) {
    String date="2010-10-02T12:23:23Z";
    String pattern="yyyy-MM-ddThh:mm:ssZ";
    SimpleDateFormat sdf=new SimpleDateFormat(pattern);
    try {
        Date d=sdf.parse(date);
        System.out.println(d.getYear());
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Sin embargo, tengo una excepción: java.lang.IllegalArgumentException: Illegal pattern character 'T'

¿Entonces me pregunto si tengo que dividir la cadena y analizarla manualmente?

Por cierto, he intentado agregar un carácter de comilla simple a cada lado de la T:

String pattern="yyyy-MM-dd'T'hh:mm:ssZ";

Tampoco funciona.


1
¿Sabes qué significa esta "T"? Por lo que creo, toda la fecha junto con la 'T' debería convertirse a la fecha (sin omitirla entre 'Comillas simples', pero lo que nos falta es el patrón de análisis.
coding_idiot

9
El Trepresenta el tiempo y es parte de la ISO8601 estándar de tiempo internacional fecha de formateo.

1
Eso es extraño. Envolver las Tcomillas simples funcionó para mí (al menos en términos de resolución del error de análisis).
Agi Hammerthief

Respuestas:


204

Actualización para Java 8 y superior

Ahora puede simplemente hacer Instant.parse("2015-04-28T14:23:38.521Z")y obtener lo correcto ahora, especialmente porque debería usarlo en Instantlugar de lo roto java.util.Datecon las versiones más recientes de Java.

Debería usar en DateTimeFormatterlugar de SimpleDateFormattertambién.

Respuesta original

La explicación a continuación sigue siendo válida como lo que representa el formato. Pero fue escrito antes de que Java 8 fuera omnipresente, por lo que usa las clases antiguas que no deberías usar si estás usando Java 8 o superior.

Esto funciona con la entrada con el seguimiento Zcomo se demuestra:

En el patrón, Tse escapa con 'ambos lados.

El patrón para el Zfinal está realmente XXXdocumentado en JavaDoc SimpleDateFormat, simplemente no está muy claro cómo usarlo, ya que también Zes el marcador de la TimeZoneinformación anterior.

Q2597083.java

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

public class Q2597083
{
    /**
     * All Dates are normalized to UTC, it is up the client code to convert to the appropriate TimeZone.
     */
    public static final TimeZone UTC;

    /**
     * @see <a href="http://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations">Combined Date and Time Representations</a>
     */
    public static final String ISO_8601_24H_FULL_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";

    /**
     * 0001-01-01T00:00:00.000Z
     */
    public static final Date BEGINNING_OF_TIME;

    /**
     * 292278994-08-17T07:12:55.807Z
     */
    public static final Date END_OF_TIME;

    static
    {
        UTC = TimeZone.getTimeZone("UTC");
        TimeZone.setDefault(UTC);
        final Calendar c = new GregorianCalendar(UTC);
        c.set(1, 0, 1, 0, 0, 0);
        c.set(Calendar.MILLISECOND, 0);
        BEGINNING_OF_TIME = c.getTime();
        c.setTime(new Date(Long.MAX_VALUE));
        END_OF_TIME = c.getTime();
    }

    public static void main(String[] args) throws Exception
    {

        final SimpleDateFormat sdf = new SimpleDateFormat(ISO_8601_24H_FULL_FORMAT);
        sdf.setTimeZone(UTC);
        System.out.println("sdf.format(BEGINNING_OF_TIME) = " + sdf.format(BEGINNING_OF_TIME));
        System.out.println("sdf.format(END_OF_TIME) = " + sdf.format(END_OF_TIME));
        System.out.println("sdf.format(new Date()) = " + sdf.format(new Date()));
        System.out.println("sdf.parse(\"2015-04-28T14:23:38.521Z\") = " + sdf.parse("2015-04-28T14:23:38.521Z"));
        System.out.println("sdf.parse(\"0001-01-01T00:00:00.000Z\") = " + sdf.parse("0001-01-01T00:00:00.000Z"));
        System.out.println("sdf.parse(\"292278994-08-17T07:12:55.807Z\") = " + sdf.parse("292278994-08-17T07:12:55.807Z"));
    }
}

Produce el siguiente resultado:

sdf.format(BEGINNING_OF_TIME) = 0001-01-01T00:00:00.000Z
sdf.format(END_OF_TIME) = 292278994-08-17T07:12:55.807Z
sdf.format(new Date()) = 2015-04-28T14:38:25.956Z
sdf.parse("2015-04-28T14:23:38.521Z") = Tue Apr 28 14:23:38 UTC 2015
sdf.parse("0001-01-01T00:00:00.000Z") = Sat Jan 01 00:00:00 UTC 1
sdf.parse("292278994-08-17T07:12:55.807Z") = Sun Aug 17 07:12:55 UTC 292278994

26

tl; dr

Utilice la java.time.Instantclase para analizar texto en formato estándar ISO 8601, que representa un momento en UTC.

Instant.parse( "2010-10-02T12:23:23Z" )

ISO 8601

Ese formato está definido por el estándar ISO 8601 para formatos de cadena de fecha y hora.

Ambos:

… Usa los formatos ISO 8601 de forma predeterminada para analizar y generar cadenas.

En general, debe evitar el uso de las antiguas clases java.util.Date /.Calendar & java.text.SimpleDateFormat, ya que son notoriamente problemáticas, confusas e imperfectas. Si es necesario para interoperar, puede convertir de aquí para allá.

java.time

Integrado en Java 8 y posterior está el nuevo marco java.time . Inspirado por Joda-Time , definido por JSR 310 , y ampliado por el proyecto ThreeTen-Extra .

Instant instant = Instant.parse( "2010-10-02T12:23:23Z" );  // `Instant` is always in UTC.

Convierte a la vieja clase.

java.util.Date date = java.util.Date.from( instant );  // Pass an `Instant` to the `from` method.

Zona horaria

Si es necesario, puede asignar una zona horaria.

ZoneId zoneId = ZoneId.of( "America/Montreal" ); // Define a time zone rather than rely implicitly on JVM’s current default time zone.
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );  // Assign a time zone adjustment from UTC.

Convertir.

java.util.Date date = java.util.Date.from( zdt.toInstant() );  // Extract an `Instant` from the `ZonedDateTime` to pass to the `from` method.

Joda-Time

ACTUALIZACIÓN: El proyecto Joda-Time ahora está en modo de mantenimiento. El equipo aconseja la migración a las clases java.time .

Aquí hay un código de ejemplo en Joda-Time 2.8.

org.joda.time.DateTime dateTime_Utc = new DateTime( "2010-10-02T12:23:23Z" , DateTimeZone.UTC );  // Specifying a time zone to apply, rather than implicitly assigning the JVM’s current default.

Convierte a clase antigua. Tenga en cuenta que la zona horaria asignada se pierde en la conversión, ya que a juDate no se le puede asignar una zona horaria.

java.util.Date date = dateTime_Utc.toDate(); // The `toDate` method converts to old class.

Zona horaria

Si es necesario, puede asignar una zona horaria.

DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
DateTime dateTime_Montreal = dateTime_Utc.withZone ( zone );

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


Sobre 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 .

Puede intercambiar objetos java.time directamente con su base de datos. Utilice un controlador JDBC compatible con JDBC 4.2 o posterior. No hay necesidad de cadenas, no hay necesidad de java.sql.*clases.

¿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 anteriormente Android (<26), el ThreeTenABP proyecto 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 .


1
@AalapPatel Tenga en cuenta que el proyecto Joda-Time está ahora en modo de mantenimiento. El equipo aconseja la migración a las clases java.time. Ambos esfuerzos están dirigidos por el mismo hombre, Stephen Colebourne.
Basil Bourque

Gracias, lo uso para obtener milisegundos de la hora UTC devuelta del servidor y / o la hora local y funciona bien para mí en este caso ...
Aalap Patel

Instant.parse necesita un nivel mínimo de API 26 en Android
Naveed Ahmad

1
@NaveedAhmad Vea las viñetas en la parte inferior de la Respuesta sobre los proyectos ThreeTen-Backport y ThreeTenABP .
Basil Bourque

0

Hay dos respuestas anteriores hasta ahora y ambas son largas (y tl; dr demasiado cortas en mi humilde opinión), así que escribo un resumen de mi experiencia comenzando a usar la nueva biblioteca java.time (aplicable como se señala en otras respuestas a la versión de Java 8+). ISO 8601 establece la forma estándar de escribir fechas: YYYY-MM-DDpor lo tanto, el formato de fecha y hora es solo el siguiente (podría ser 0, 3, 6 o 9 dígitos por milisegundos) y no se necesita una cadena de formato:

import java.time.Instant;
public static void main(String[] args) {
    String date="2010-10-02T12:23:23Z";
    try {
        Instant myDate = Instant.parse(date);
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

No lo necesitaba, pero como obtener el año está en el código de la pregunta, entonces:
es más complicado, no se puede hacer Instantdirectamente, se puede hacer a través Calendarde preguntas Obtenga el valor entero del año actual en Java y Convertir Java. tiempo para el calendario, pero en mi humilde opinión ya que el formato es fijo, la subcadena es más fácil de usar:

myDate.toString().substring(0,4);
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.