Data de análise SimpleDateFormat com 'Z' literal [duplicado
-
24-09-2019 - |
Pergunta
Esta pergunta já tem uma resposta aqui:
Estou tentando analisar uma data que se parece com o seguinte:
2010-04-05T17:16:00Z
Esta é uma data válida por http://www.ietf.org/rfc/rfc3339.txt. O 'Z' literal "implica que o UTC é o ponto de referência preferido para o tempo especificado".
Se eu tentar analisá -lo usando o SimpleDateFormat e esse padrão:
yyyy-MM-dd'T'HH:mm:ss
Será analisado como seg 05 de abril 17:16:00 EDT 2010
SimpleDateFormat é incapaz de analisar a string com estes padrões:
yyyy-MM-dd'T'HH:mm:ssz
yyyy-MM-dd'T'HH:mm:ssZ
Posso definir explicitamente o fuso horário para usar no SimpleDateFormat para obter a saída esperada, mas não acho que isso seja necessário. Há algo que estou perdendo? Existe um analisador alternativo de data?
Solução
No padrão, a inclusão de um componente de data de data 'Z' indica que o formato do fuso horário precisa estar em conformidade com o Fuso horário geral "padrão", cujos exemplos são Pacific Standard Time; PST; GMT-08:00
.
A 'z' indica que o fuso horário está em conformidade com o RFC 822 fuso horário padrão, por exemplo -0800
.
Eu acho que você precisa de um datatypeConverter ...
@Test
public void testTimezoneIsGreenwichMeanTime() throws ParseException {
final Calendar calendar = javax.xml.bind.DatatypeConverter.parseDateTime("2010-04-05T17:16:00Z");
TestCase.assertEquals("gotten timezone", "GMT+00:00", calendar.getTimeZone().getID());
}
Outras dicas
O Java não analisa as datas ISO corretamente.
Semelhante à resposta de McKenzie.
Basta consertar o Z
antes de analisar.
Código
String string = "2013-03-05T18:05:05.000Z";
String defaultTimezone = TimeZone.getDefault().getID();
Date date = (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")).parse(string.replaceAll("Z$", "+0000"));
System.out.println("string: " + string);
System.out.println("defaultTimezone: " + defaultTimezone);
System.out.println("date: " + (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")).format(date));
Resultado
string: 2013-03-05T18:05:05.000Z
defaultTimezone: America/New_York
date: 2013-03-05T13:05:05.000-0500
A data em que você está analisando está no formato ISO8601.
Em Java 7, o padrão de ler e aplicar o sufixo do fuso horário deve ler yyyy-MM-dd'T'HH:mm:ssX
tl; dr
Instant.parse ( "2010-04-05T17:16:00Z" )
ISO 8601 padrão
Sua string está em conformidade com o ISO 8601 padrão (do qual o mencionado RFC 3339 é um perfil).
Evite Judate
As classes Java.util.Date e .Calendar, empacotadas com Java, são notoriamente problemáticas. Evite-os.
Em vez disso, use o Joda-Time Biblioteca ou o novo pacote Java.Time em Java 8. Ambos usam a ISO 8601 como padrões para analisar e gerar representações de string de valores de data-hora.
Java.Time
o Java.Time Framework incorporado ao Java 8 e depois suplanta o problemático antigo java.util.date/.Calendar Classes. As novas classes são inspiradas pelo sucesso Joda-Time Framework, destinado a seu sucessor, semelhante em conceito, mas re-arquitetado. Definido por JSR 310. Estendido pelo Threeten-Extra projeto. Veja o Tutorial.
o Instant
A classe em Java.Time representa um momento na linha do tempo em UTC fuso horário.
o Z
no final da sua sequência de entrada significa Zulu
que significa UTC
. Essa string pode ser analisada diretamente pelo Instant
classe, sem necessidade de especificar um formatador.
String input = "2010-04-05T17:16:00Z";
Instant instant = Instant.parse ( input );
Despejar para consolar.
System.out.println ( "instant: " + instant );
Instante: 2010-04-05T17: 16: 00Z
A partir daí, você pode aplicar um fuso horário (ZoneId
) para ajustar isso Instant
dentro de ZonedDateTime
. Pesquise pilha de transbordamento para discussão e exemplos.
Se você deve usar um java.util.Date
Objeto, você pode converter chamando os novos métodos de conversão adicionados às classes antigas, como o método estático java.util.Date.from( Instant )
.
java.util.Date date = java.util.Date.from( instant );
Joda-Time
Exemplo no Joda-Time 2.5.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" ):
DateTime dateTime = new DateTime( "2010-04-05T17:16:00Z", timeZone );
Converter para UTC.
DateTime dateTimeUtc = dateTime.withZone( DateTimeZone.UTC );
Converta em um java.util.Date, se necessário.
java.util.Date date = dateTime.toDate();
De acordo com a última linha no Padrões de data e hora Tabela do Java 7 API
X Fuso horário ISO 8601 fuso horário -08; -0800; -08: 00
Para o fuso horário da ISO 8601, você deve usar:
- X para (-08 ou z),
- Xx para (-0800 ou z),
- Xxx para (-08: 00 ou z);
Então, para analisar o seu "2010-04-05T17: 16: 00Z", você pode usar qualquer um "yyyy-mm-dd't'hh: mm: ssx" ou "yyyy-mm-dd't'hh: mm: ssxx" ou "yyyy-mm-dd't'hh: mm: ssxxx" .
System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX").parse("2010-04-05T17:16:00Z"));
System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXX").parse("2010-04-05T17:16:00Z"));
System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").parse("2010-04-05T17:16:00Z"));
Imprimirá corretamente 'Seg 05 13:16:00 EDT 2010'
O 'X' só funciona se não estiverem segundos parciais: ou seja, padrão simplificado de formação de
"Aaaaaa-mm-dd't'hh: mm: ssx"
Irá analisar corretamente
"2008-01-31T00: 00: 00Z"
mas
"Aaaaaa-mm-dd't'hh: mm: ss.sx"
Não vai analisar
"2008-01-31T00: 00: 00.000Z"
Triste, mas é verdade, um encontro com segundos parciais não parece ser uma data de ISO válida: http://en.wikipedia.org/wiki/iso_8601
O fuso horário deve ser algo como "GMT+00: 00" ou 0000 para ser analisado corretamente pelo SimpleDateFormat - você pode substituir Z por esta construção.
O projeto RestLet inclui uma classe InternetDateFormat que pode analisar as datas RFC 3339.
RESTLETLET DO INTERNETDATEFORMAT
No entanto, você pode apenas substituir o 'Z' à direita pelo "UTC" antes de analisá -lo.
Eu forneço outra resposta que encontrei por api-client-library
pelo Google
try {
DateTime dateTime = DateTime.parseRfc3339(date);
dateTime = new DateTime(new Date(dateTime.getValue()), TimeZone.getDefault());
long timestamp = dateTime.getValue(); // get date in timestamp
int timeZone = dateTime.getTimeZoneShift(); // get timezone offset
} catch (NumberFormatException e) {
e.printStackTrace();
}
Guia de instalação,
https://developers.google.com/api-client-library/java/google-api-java-client/setup#download
Aqui está referência da API,
https://developers.google.com/api-client-library/java/google-http-java-client/reference/1.20.0/com/google/api/client/util/datetime
Código -fonte de DateTime
Classe,
https://github.com/google/google-http-java-client/blob/master/google-http-client/src/main/java/com/google/api/client/util/datetime.java
DateTime
testes de unidade,
https://github.com/google/google-http-java-client/blob/master/google-http-client/src/test/java/com/google/api/client/util/datetetest.java#l121
Com relação ao JSR-310, outro projeto de interesse pode ser Threetenbp.
O JSR-310 fornece uma nova biblioteca de data e hora para Java SE 8. Este projeto é o backport para Java SE 6 e 7.
Caso você esteja trabalhando em um Android Projeto que você pode querer verificar o Biblioteca Threetenabp.
compile "com.jakewharton.threetenabp:threetenabp:${version}"
O JSR-310 foi incluído no Java 8 como o pacote Java.Time.*. É um substituto completo para a data doente e as APIs do calendário em Java e Android. O JSR-310 foi apoiado para o Java 6 por seu criador, Stephen Colebourne, da qual esta biblioteca está adaptada.
Sob Java 8, use o DateTimeFormatter predefinido.iso_date_time
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
ZonedDateTime result = ZonedDateTime.parse("2010-04-05T17:16:00Z", formatter);
Eu acho que é a maneira mais fácil
Desde que Java 8 apenas use ZonedDateTime.parse("2010-04-05T17:16:00Z")