Caractere de padrão ilegal 'T' ao analisar uma sequência de datas para java.util.Date

182

Eu tenho uma string de data e quero analisá-la para a data normal, use a API java Date, o seguinte é o meu 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();
    }
}

No entanto, eu tenho uma exceção: java.lang.IllegalArgumentException: Illegal pattern character 'T'

Então, eu me pergunto se tenho que dividir a string e analisá-la manualmente?

BTW, tentei adicionar um caractere de aspas simples em ambos os lados do T:

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

Também não funciona.

hguser
fonte
1
você sabe o que significa esse "T"? Tanto quanto eu acho que a data inteira juntamente com o 'T' deve ser convertido para a data (não ignorando-lo através de 'Aspas simples', mas a coisa que está faltando é o padrão de análise.
coding_idiot
9
Ele Trepresenta a hora e faz parte do padrão ISO8601 para formatação de data e hora internacional.
1
Isso é estranho. O agrupamento de Taspas simples funcionou para mim (pelo menos em termos de solução do erro de análise).
Agi Hammerthief

Respostas:

204

Atualização para Java 8 e superior

Agora você pode simplesmente fazer Instant.parse("2015-04-28T14:23:38.521Z")e obter a coisa correta agora, especialmente porque você deve estar usando em Instantvez de estar quebrado java.util.Datecom as versões mais recentes do Java.

Você deve usar em DateTimeFormattervez de SimpleDateFormattertambém.

Resposta original:

A explicação abaixo ainda é válida como o que o formato representa. Mas ele foi escrito antes que o Java 8 fosse onipresente, portanto, ele usa as classes antigas que você não deveria usar se estivesse usando o Java 8 ou superior.

Isso funciona com a entrada com o final, Zconforme demonstrado:

No padrão, o Tescape é 'de ambos os lados.

O padrão para o Zfinal é realmente XXXo documentado no JavaDoc SimpleDateFormat, apenas não está muito claro como usá-lo, pois também Zé o marcador para as TimeZoneinformações antigas .

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

Produz a seguinte saída:

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

fonte
26

tl; dr

Use a java.time.Instantclasse para analisar o texto no formato ISO 8601 padrão, representando um momento no UTC.

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

ISO 8601

Esse formato é definido pelo padrão ISO 8601 para os formatos de string de data e hora.

Ambos:

… Use os formatos ISO 8601 por padrão para analisar e gerar strings.

Geralmente, você deve evitar o uso das classes java.util.Date /.Calendar e java.text.SimpleDateFormat antigas , pois são notoriamente problemáticas, confusas e com falhas. Se necessário para interoperar, você pode converter para lá e para cá.

java.time

Construído no Java 8 e posterior, está a nova estrutura java.time . Inspirado no Joda-Time , definido pelo JSR 310 , e estendido pelo projeto ThreeTen-Extra .

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

Converta para a classe antiga.

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

Fuso horário

Se necessário, você pode atribuir um fuso horário.

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.

Converter.

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

Joda-Time

ATUALIZAÇÃO: O projeto Joda-Time agora está no modo de manutenção. A equipe aconselha a migração para as classes java.time .

Aqui está um exemplo de código no 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.

Converta para a classe antiga. Observe que o fuso horário atribuído é perdido na conversão, pois o juDate não pode ser atribuído a um fuso horário.

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

Fuso horário

Se necessário, você pode atribuir um fuso horário.

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

Tabela de tipos de data e hora em Java, modernos e herdados.


Sobre java.time

A estrutura java.time é integrada ao Java 8 e posterior. Essas classes suplantar os velhos problemáticos legados aulas de data e hora, como java.util.Date, Calendar, e SimpleDateFormat.

O projeto Joda-Time , agora em modo de manutenção , aconselha a migração para as classes java.time .

Para saber mais, consulte o Tutorial do Oracle . E procure Stack Overflow para muitos exemplos e explicações. A especificação é JSR 310 .

Você pode trocar objetos java.time diretamente com seu banco de dados. Use um driver JDBC compatível com JDBC 4.2 ou posterior. Não há necessidade de strings, não há necessidade de java.sql.*classes.

Onde obter as classes java.time?

  • Java SE 8 , Java SE 9 e posterior
    • Construídas em.
    • Parte da API Java padrão com uma implementação em pacote configurável.
    • O Java 9 adiciona alguns recursos e correções menores.
  • Java SE 6 e Java SE 7
    • Grande parte da funcionalidade java.time é portada para Java 6 e 7 no ThreeTen-Backport .
  • Android
    • Versões posteriores das implementações do pacote Android das classes java.time.
    • Para anteriormente Android (<26), o ThreeTenABP projeto adapta ThreeTen-Backport (mencionados acima). Consulte Como usar o ThreeTenABP… .

Tabela de qual biblioteca java.time a ser usada com qual versão do Java ou Android.

O projeto ThreeTen-Extra estende java.time com classes adicionais. Este projeto é um campo de prova para possíveis adições futuras ao java.time. Você pode encontrar algumas classes úteis aqui, como Interval, YearWeek, YearQuarter, e mais .

Basil Bourque
fonte
1
@AalapPatel Esteja ciente de que o projeto Joda-Time agora está no modo de manutenção. A equipe aconselha a migração para as classes java.time. Ambos os esforços são liderados pelo mesmo homem, Stephen Colebourne.
Basil Bourque
Obrigado, eu usá-lo para obter milissegundos de servidor retornou hora UTC e / ou hora local e funciona bem para mim neste caso ..
Aalap Patel
Instant.parse precisa min nível API 26 no android
Naveed Ahmad
1
@NaveedAhmad Veja os marcadores na parte inferior da resposta sobre os projetos ThreeTen-Backport e ThreeTenABP .
manjericão Bourque
0

Há duas respostas acima up-to-e agora eles são ambos muito tempo (e tl; dr IMHO muito curta), então eu escrever resumo da minha experiência de começar a usar nova java.time biblioteca (aplicável como observado em outras respostas a versão Java 8+). A ISO 8601 define a maneira padrão de escrever datas: YYYY-MM-DDportanto, o formato da data e hora é apenas o seguinte (pode ser de 0, 3, 6 ou 9 dígitos por milissegundos) e nenhuma sequência de formatação é necessária:

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

Eu não precisava disso, mas como obter ano está no código da pergunta, então:
é mais complicado, não pode ser feito Instantdiretamente, pode ser feito via Calendarmeio de perguntas Obtenha valor inteiro do ano atual em Java e Convertendo java. tempo para o calendário, mas IMHO como formato é substring fixo é mais simples de usar:

myDate.toString().substring(0,4);
Alexei Martianov
fonte