TransWikia.com

Converter data com timezone

Stack Overflow em Português Asked by DudisRoyer on December 11, 2021

new Date() em JavaScript me retorna esse formato:

Tue Apr 01 2014 13:43:13 GMT-0300 (BRT)

Eu preciso converter isso para um java.util.Date. Para isso estou tentando utilizar SimpleDateFormat mas não achei um pattern que desse certo, sempre me retorna:

java.text.ParseException: Unparseable date: "Tue Apr 01 2014 13:43:13 GMT-0300"

4 Answers

Complementando, existem alguns detalhes que não foram cobertos nas outras respostas.

Por exemplo, o dia da semana e mês estão em inglês ("Tue Apr"), e por padrão SimpleDateFormat usa o idioma correspondente ao locale default da JVM, então não é garantido que os códigos sugeridos na outra resposta funcionem sempre. Por exemplo, na minha JVM o locale default é pt_BR (Português do Brasil), então o código sugerido dá erro, pois ele tenta fazer o parsing dos nomes em português.

Para que ele sempre considere os nomes em inglês, independente da configuração da JVM, basta informar o Locale correto no construtor de SimpleDateFormat:

String text = "Tue Apr 01 2014 13:43:13 GMT-0300 (BRT)";
String format = "EEE MMM dd yyyy HH:mm:ss 'GMT'Z (zzz)";
// usando Locale.US (Inglês Americano)
SimpleDateFormat dateFormat = new SimpleDateFormat(format, Locale.US);
System.out.println(dateFormat.parse(text));

Usei Locale.US (Inglês Americano), assim ele considera os nomes em inglês, independente do locale que estiver configurado na JVM.

Outro ponto que discordo da outra resposta é sobre "você já tem a identificação do TimeZone correto, que no caso é o BRT". Na verdade essas abreviações como "BRT" não são padronizadas, e muitas são ambíguas: há várias delas que representam mais de um timezone - veja esta lista, temos o exemplo de "CST" que é usada nos Estados Unidos, Cuba e China, ou ainda "IST", que é usada na Irlanda, Índia e Israel. Ou seja, nem sempre (na verdade quase nunca) será possível determinar o timezone a partir da abreviação.

Mesmo no caso de "BRT", há mais de um timezone que usa esta sigla, como America/Sao_Paulo e America/Recife (a diferença entre eles é que em muitos anos, um adotava o horário de verão, e o outro não). Tudo bem que durante o horário de verão a sigla muda para "BRST", mas como já disse, as abreviações são ambíguas e não-padronizadas, e de qualquer forma, existem muitos outros timezones no Brasil, como America/Belem, America/Rio_Branco, entre outros, que apesar de usarem a mesma sigla, possuem diferentes históricos com relação a quando cada um adotava horário de verão ou não. Ou seja, ter a abreviação não garante "a identificação do timezone correto" (ou você aceita o default que SimpleDateFormat usa, ou escolhe um arbitrariamente, como veremos a seguir).

E para entender melhor o que é um timezone e de onde vem esses nomes como America/Sao_Paulo e America/Recife, leia aqui.


java.time

A partir do Java 8 (lançado em 2014), é possível usar a API java.time. A diferença que esta API é mais flexível, e nela é possível configurar qual timezone você quer usar para a abreviação (no caso de SimpleDateFormat, ele usará um valor default e não é possível mudá-lo). Esta flexibilidade tem um preço, que é ter que dizer explicitamente o que queremos:

String text = "Tue Apr 01 2014 13:43:13 GMT-0300 (BRT)";

// timezones usados para "desambiguar" as abreviações
Set<ZoneId> preferredZones = new HashSet<ZoneId>();
preferredZones.add(ZoneId.of("America/Sao_Paulo"));

DateTimeFormatter parser = new DateTimeFormatterBuilder()
    // data, hora e offset
    .appendPattern("EEE MMM dd uuuu HH:mm:ss 'GMT'Z (")
    // usar timezone arbitrário para abreviação
    .appendZoneText(TextStyle.SHORT, preferredZones)
    // usar locale inglês para mês e dia da semana
    .appendLiteral(')').toFormatter(Locale.US);
// faz o parsing da String
ZonedDateTime data = ZonedDateTime.parse(text, parser);
// se precisar de um java.util.Date
Date date = Date.from(data.toInstant());

Uma das características desta API é que existem várias classes diferentes para representar datas e horas, e você pode escolher a mais adequada para cada situação. Como neste caso eu tenho a data, hora e timezone, a melhor opção é um ZonedDateTime.

E se precisar especificamente de um java.util.Date, o exemplo acima mostra como fazer a conversão.

Apesar de parecer mais trabalhoso, esta API corrige muitos dos problemas e falhas de design de Date e Calendar, então na minha opinião é um trabalho que no fim vale a pena.

Answered by hkotsubo on December 11, 2021

Estabeleça um padrão de data em String que o Javascript deva passar. Depois disso, deixe por conta do Java. Um jeito um pouco mais alternativo (só um pouco) de se fazer isso seria usando o método setTimeZone() do DateFormat da seguinte maneira:

DateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm");
df.setTimeZone(TimeZone.getTimeZone("GMT"));
String strData = "30/05/2014 21:45";

try {
    Date data = df.parse(strData);
    System.out.println(data);
}
catch (ParseException e) {
    e.printStackTrace();
}

A saída seria:

Fri May 30 18:45:00 BRT 2014

Mas você pode se perguntar, quais os TimeZones disponíveis para a utilização no método setTimeZone(). Você pode listar todos os TimeZones da seguinte forma:

for (String tz : TimeZone.getAvailableIDs()) {
    System.out.println(tz);
}

Não vou postar aqui a saída, porque é um pouco extensa. Mas tenho certeza que o timezone que você procura estará na lista.

EDIT: Complementando a resposta a respeito do Javascript. Você pode formatar o object Date, através de vários métodos como getMonth(), getDate(), getMinutes()... Dessa forma você formata a data como quiser para enviar ao Java. Veja a referência completa do objeto Date no Javascript.

Answered by humungs on December 11, 2021

Como você já tem a identificação do TimeZone correto, que no caso é o BRT você poderia ignorar a parte que tem o GMT-0300, porque colocar o GMT no formato pode ser problemático, a não ser que ele seja fixo. Então proponho 2 soluções.

1) Removendo o GMT-0300

public static void main(String[] args) throws ParseException {
    String text = stripGMT("Tue Apr 01 2014 13:43:13 GMT-0300 (BRT)");
    String format = "EEE MMM dd yyyy HH:mm:ss  (zzz)";
    DateFormat dateFormat = new SimpleDateFormat(format);
    System.out.println(dateFormat.parse(text));
}

private static String stripGMT(String text) {
    return text.replaceAll("GMT-\d{4}", "");
}

2) Mantendo o GMT como um argumento fixo

public static void main(String[] args) throws ParseException {
    String text = "Tue Apr 01 2014 13:43:13 GMT-0300 (BRT)";
    String format = "EEE MMM dd yyyy HH:mm:ss 'GMT'Z (zzz)";
    DateFormat dateFormat = new SimpleDateFormat(format);
    System.out.println(dateFormat.parse(text));
}

Answered by Rodrigo Sasaki on December 11, 2021

Como no SO em inglês

A melhor formar de converter é usando o time em milisegundos, UTC. O object Javascript Date e
java.util.Date suportam conversão utilizando milisegundos.

Answered by Leonardo Otto on December 11, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP