Como definir o fuso horário de um java.util.Date?

225

Analisei um java.util.Datede um Stringmas ele está definindo o fuso horário local como o fuso horário do dateobjeto.

O fuso horário não está especificado no Stringqual Dateé analisado. Eu quero definir um fuso horário específico do dateobjeto.

Como eu posso fazer isso?

Yatendra Goel
fonte
8
Embora não seja realmente uma resposta para sua pergunta, usei o Joda Time depois de vê-lo mencionado aqui algumas vezes. Parece-me mais racional do que as APIs padrão e pode fazer esse tipo de coisa com bastante facilidade.
Clstrfsck 23/05
2
Atualmente, use as classes java.time em vez do Joda-Time. O projeto Joda-Time agora está no modo de manutenção , com a equipe aconselhando a migração para as classes java.time . Consulte o Tutorial da Oracle .
Basil Bourque

Respostas:

315

Use DateFormat. Por exemplo,

SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = isoFormat.parse("2010-05-23T09:01:02");
ZZ Coder
fonte
6
se a data for criada a partir da classe Calendário, você poderá definir o fuso horário para o Calendário.
Lwpro2
19
@ lwpro2 essa declaração é enganosa; Você pode definir o fuso horário para um objeto Calendário, mas obter um objeto Date usando o método getTime () retornará um objeto Date com o fuso horário do computador host.
BrDaHa
Estou tendo problemas para analisar 20/02/15 14:44. Alguém pode ajudar
MS
@BrDaHa está correto. Você precisará TimeZone.setDefault()antes de ligar getTime()para que o novo objeto de data fique no fuso horário desejado. No JDK 1.8, as Calendar.getTime()chamadas return new Date(getTimeInMillis());.
Jpllosa # 9/17
182

Esteja ciente de que os java.util.Dateobjetos não contêm nenhuma informação de fuso horário por si mesmos - você não pode definir o fuso horário em um Dateobjeto. A única coisa que um Dateobjeto contém é um número de milissegundos desde a "época" - 1 de janeiro de 1970, 00:00:00 UTC.

Como mostra o ZZ Coder, você define o fuso horário no DateFormatobjeto, para informar em que fuso horário deseja exibir a data e a hora.

Jesper
fonte
77
Depois de pesquisar no Google, experimentar e explorar, percebi que essa é uma adição precisa e útil à resposta - e vale a pena destacar: a data contém apenas o valor de milissegundos . Se você olhar para a fonte, existe praticamente um longcampo chamado fastTime. Date.toString()na verdade, usa a Calendarpara interpretar esse milissegundo. Portanto, imprimir um Datefaz parecer um fuso horário (padrão), levando a perguntas compreensíveis sobre como definir esse fuso horário.
David Carboni
3
há informações de fuso horário dentro dos objetos Date. Mas pode ser verdade, você não pode mudar isso.
Lwpro2
3
@ Iwpro2, Jesper afirma (e eu concordo) que o objeto Date não armazena um fuso horário. Se você está reivindicando que o fuso horário está armazenado no java.util.Date, forneça uma referência.
21413 Jim
6
@ Jim, este é o código fonte do java.util.Date. Ele contém o fastTime como época unix e o cdate BaseCalendar.Date que é usado em favor do fastTime, se ele estiver definido. Esse contém informações de fuso horário. Entendo que uma instância de Data possa conter informações de fuso horário, mas talvez não.
EIS
2
@ Jim Eu não disse que você poderia configurá-lo, eu estava dizendo que ele contém essas informações. Também não vejo como defini-lo como usuário. Acho que o OP está correto, pois parece ser sempre o fuso horário padrão do usuário / sistema.
EIS
95

tl; dr

… Analisado… de um String… o fuso horário não está especificado… Quero definir um fuso horário específico

LocalDateTime.parse( "2018-01-23T01:23:45.123456789" )  // Parse string, lacking an offset-from-UTC and lacking a time zone, as a `LocalDateTime`.
    .atZone( ZoneId.of( "Africa/Tunis" ) )              // Assign the time zone for which you are certain this date-time was intended. Instantiates a `ZonedDateTime` object.

Sem fuso horário no juDate

Como as outras respostas corretas declararam, um java.util.Date não tem fuso horário . Representa UTC / GMT (sem deslocamento de fuso horário). Muito confuso porque seu toStringmétodo aplica o fuso horário padrão da JVM ao gerar uma representação de String.

Evite juDate

Por esse e muitos outros motivos, você deve evitar o uso de java.util.Date e .Calendar e java.text.SimpleDateFormat interno. Eles são notoriamente problemáticos.

Em vez disso, use o pacote java.time incluído no Java 8 .

java.time

As classes java.time podem representar um momento na linha do tempo de três maneiras:

  • UTC ( Instant)
  • Com um deslocamento ( OffsetDateTimecom ZoneOffset)
  • Com um fuso horário ( ZonedDateTimecom ZoneId)

Instant

Em java.time , o componente básico é Instantum momento na linha do tempo no UTC. Use Instantobjetos para grande parte da sua lógica de negócios.

Instant instant = Instant.now();

OffsetDateTime

Aplique um deslocamento do UTC para ajustar o horário do relógio de parede de alguma localidade .

Aplique a ZoneOffsetpara obter uma OffsetDateTime.

ZoneOffset zoneOffset = ZoneOffset.of( "-04:00" );
OffsetDateTime odt = OffsetDateTime.ofInstant( instant , zoneOffset );

ZonedDateTime

Melhor é aplicar um fuso horário , um deslocamento mais as regras para lidar com anomalias, como horário de verão (DST) .

Aplique a ZoneIda Instantpara obter a ZonedDateTime. Sempre especifique um nome de fuso horário adequado . Nunca use abreviações de 3 a 4 como ESTou ISTque não sejam únicas nem padronizadas.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

LocalDateTime

Se a sequência de entrada não possuir nenhum indicador de deslocamento ou zona, analise como a LocalDateTime.

Se você tiver certeza do fuso horário pretendido, atribua a ZoneIdpara produzir a ZonedDateTime. Veja o exemplo de código acima na seção tl; dr na parte superior.

Strings formatadas

Chame o toStringmétodo em qualquer uma dessas três classes para gerar uma String representando o valor de data e hora no formato padrão ISO 8601 . A ZonedDateTimeclasse estende o formato padrão acrescentando o nome do fuso horário entre colchetes.

String outputInstant = instant.toString(); // Ex: 2011-12-03T10:15:30Z
String outputOdt = odt.toString(); // Ex: 2007-12-03T10:15:30+01:00
String outputZdt = zdt.toString(); // Ex: 2007-12-03T10:15:30+01:00[Europe/Paris]

Para outros formatos, use a DateTimeFormatterclasse Geralmente, é melhor deixar essa classe gerar formatos localizados usando a linguagem humana esperada do usuário e as normas culturais. Ou você pode especificar um formato específico.


Tabela de todos os 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?

O projeto ThreeTen-Extra estende o 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 .


Joda-Time

Enquanto o Joda-Time ainda é mantido ativamente, seus fabricantes nos disseram para migrar para o java.time o mais rápido possível. Deixo esta seção intacta como referência, mas sugiro usar a java.timeseção acima.

No Joda-Time , um objeto de data e hora ( DateTime) realmente conhece seu fuso horário atribuído. Isso significa um deslocamento do UTC e as regras e o histórico do horário de verão (DST) do fuso horário e outras anomalias desse tipo.

String input = "2014-01-02T03:04:05";
DateTimeZone timeZone = DateTimeZone.forID( "Asia/Kolkata" );
DateTime dateTimeIndia = new DateTime( input, timeZone );
DateTime dateTimeUtcGmt = dateTimeIndia.withZone( DateTimeZone.UTC );

Chame o toStringmétodo para gerar uma String no formato ISO 8601 .

String output = dateTimeIndia.toString();

O Joda-Time também oferece recursos avançados para gerar todos os tipos de outros formatos de String.

Se necessário, você pode converter de Joda-Time DateTime para um java.util.Date.

Java.util.Date date = dateTimeIndia.toDate();

Pesquise StackOverflow por "joda date" para encontrar muitos mais exemplos, alguns bastante detalhados.


Na verdade não é um fuso horário incorporado em um java.util.Date, utilizados para algumas funções internas (ver comentários sobre esta resposta). Mas esse fuso horário interno não é exposto como uma propriedade e não pode ser definido. Esse fuso horário interno não é o usado pelo toStringmétodo na geração de uma representação de sequência do valor de data e hora; em vez disso, o fuso horário padrão atual da JVM é aplicado imediatamente. Então, como abreviação, costumamos dizer "juDate não tem fuso horário". Confuso? Sim. Mais uma razão para evitar essas velhas classes cansadas.

Basil Bourque
fonte
3
"Nenhum fuso horário no juDate" está errado. Há uma informação de fuso horário no juDate armazenada em sua BaseCalendar.Date cdatepropriedade, se configurada. Dê uma olhada no código fonte aqui . Você não pode definir o fuso horário de um objeto juDate, exceto alterando o fuso horário padrão da JVM chamando TimeZone.setDefault(TimeZone.getTimeZone("NEW_TIME_ZONE"));. Assim, há um deslocamento de fuso horário e você pode obter o deslocamento chamando o método juDate.getTimezoneOffset deprecated ()
Thai Bui
3
@blquythai Correto, você fez sua lição de casa. Como eu, tendo visto esse código fonte antes. Não é um fuso horário enterrado lá. Mas, para todos os fins práticos, esse fuso horário é ignorado. Um java.util.Date funciona sem nenhum fuso horário, com efeito no UTC, ignorando esse fuso horário oculto. Exceto pelo toStringmétodo que aplica o fuso horário padrão atual da JVM; novamente ignorando o fuso horário oculto. Portanto, por questões de brevidade, dizemos que java.util.Date não tem fuso horário. Como Art , é uma mentira que diz a verdade.
Basil Bourque
@blquythai Quanto à chamada TimeZone.setDefault, você não está definindo o fuso horário do objeto java.util.Date - o objeto Date ainda ignora o fuso horário oculto , atuando efetivamente no UTC. Você afetaria o toStringmétodo da data . Definir o padrão altera o fuso horário padrão da JVM, que geralmente é definido como o fuso horário do sistema operacional host. Essa chamada não é recomendada , pois afeta todo o código em todos os encadeamentos de todos os aplicativos em execução nessa JVM e o faz on-the-fly enquanto eles estão em execução. Por ser rude e perigoso, essa ligação deve ser considerada apenas como último recurso.
Basil Bourque
5
Esse fuso horário é usado muitas vezes (usado em equals, hashcode, getTime..) Se você der uma olhada no equalsmétodo, ele chama getTime()que as chamadas getTimeImpl(), que as chamadas normalize()se a cdatepropriedade não é normalizada. No normalize()método, a última condição if recalcula os milissegundos desde 1/1/70 com base em suas informações de fuso horário armazenadas se o fuso horário de cdatefor diferente do fuso horário do ambiente atual da JVM em que está sendo executado. (Dê uma olhada sun.util.calendar.AbstractCalendar getCalendarDate(long millis, CalendarDate date))
Thai Bui
2
@MichalM Por seu conselho, há algum tempo atrás eu adicionei um posfácio sobre a zona incorporada.
Basil Bourque
75

Você também pode definir o fuso horário no nível da JVM

Date date1 = new Date();
System.out.println(date1);

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
// or pass in a command line arg: -Duser.timezone="UTC"

Date date2 = new Date();
System.out.println(date2);

resultado:

Thu Sep 05 10:11:12 EDT 2013
Thu Sep 05 14:11:12 UTC 2013
starmer
fonte
Isso me ajudou. configurações de fuso horário em SDF não fazer qualquer diff
Vishnu Viswanath
Eu acho que é mais confiável. (+1)
foobar
23
Cuidado: A chamada TimeZone.setDefaulté bastante drástica, pois afeta toda a JVM, afeta todos os outros objetos e encadeamentos. Veja esta resposta para obter detalhes, incluindo ainda mais complicações, se você estiver executando com um SecurityManager. Adicionando ainda mais complicação: Esse comportamento foi alterado em várias versões do Java, conforme discutido nesta pergunta .
Basil Bourque
Isso define um fuso horário comum em todos os threads gerados após esta declaração, certo?
Jaydev
Esta é uma resposta melhor para a pergunta do que a aceita.
Francogrex
10

Se você precisar trabalhar apenas com classes JDK padrão, poderá usar isto:

/**
 * Converts the given <code>date</code> from the <code>fromTimeZone</code> to the
 * <code>toTimeZone</code>.  Since java.util.Date has does not really store time zome
 * information, this actually converts the date to the date that it would be in the
 * other time zone.
 * @param date
 * @param fromTimeZone
 * @param toTimeZone
 * @return
 */
public static Date convertTimeZone(Date date, TimeZone fromTimeZone, TimeZone toTimeZone)
{
    long fromTimeZoneOffset = getTimeZoneUTCAndDSTOffset(date, fromTimeZone);
    long toTimeZoneOffset = getTimeZoneUTCAndDSTOffset(date, toTimeZone);

    return new Date(date.getTime() + (toTimeZoneOffset - fromTimeZoneOffset));
}

/**
 * Calculates the offset of the <code>timeZone</code> from UTC, factoring in any
 * additional offset due to the time zone being in daylight savings time as of
 * the given <code>date</code>.
 * @param date
 * @param timeZone
 * @return
 */
private static long getTimeZoneUTCAndDSTOffset(Date date, TimeZone timeZone)
{
    long timeZoneDSTOffset = 0;
    if(timeZone.inDaylightTime(date))
    {
        timeZoneDSTOffset = timeZone.getDSTSavings();
    }

    return timeZone.getRawOffset() + timeZoneDSTOffset;
}

O crédito vai para este post .

diadyne
fonte
1
O deslocamento dos fusos horários nem sempre é constante. De fato, eles podem mudar devido a razões geopolíticas. TimeZone.getDSTSavings () não leva isso em consideração e sempre retorna o deslocamento atual . Isso significa que você pode obter uma conversão incorreta caso esteja lidando com uma data histórica com uma fromTimeZone / toTimeZone que alterou as compensações desde essa data.
andrerobot
6

java.util.Calendaré a maneira usual de lidar com fusos horários usando apenas classes JDK. O Apache Commons possui outras alternativas / utilitários que podem ser úteis. A nota de Edit Spong me lembrou que eu ouvi coisas realmente boas sobre Joda-Time (embora eu não as tenha usado).

TJ Crowder
fonte
+1 para o tempo de Joda. Embora ele não forneça nenhuma funcionalidade adicional que você não possa obter da API Java padrão (que eu encontrei em qualquer caso - prazer em ser mostrado de outra forma), o Joda Time facilita algumas tarefas.
19373 Joshua Hutchison
2
@JoshuaHutchison Joda-Time tem toneladas de funcionalidades adicionais. Exemplo: Representação de períodos de tempo com as classes Period, Duration, e Interval. Essas extensões incluem métodos de comparação, tais como contains, abuts, overlap, e gap. E PeriodFormatterBuilderpode criar frases descritivas como "15 anos e 8 meses".
Basil Bourque
1

Converta a Data em String e faça-o com SimpleDateFormat.

    SimpleDateFormat readFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    readFormat.setTimeZone(TimeZone.getTimeZone("GMT" + timezoneOffset));
    String dateStr = readFormat.format(date);
    SimpleDateFormat writeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    Date date = writeFormat.parse(dateStr);
avisador
fonte
1
As respostas no estouro de pilha devem ter alguma discussão ou explicação. Este site pretende ser mais do que uma biblioteca de trechos de código.
Basil Bourque
obrigado @Basil Bourque pelo seu comentário. Eu edito a resposta
avisper 18/01/19
0

Se alguém precisar disso, se você precisar converter um XMLGregorianCalendarfuso horário para o seu fuso horário atual a partir do UTC, basta definir o fuso horário e 0, em seguida, ligar toGregorianCalendar()- ele permanecerá no mesmo fuso horário, mas Datesabe como convertê-lo para seu, para que você possa obter os dados a partir daí.

XMLGregorianCalendar xmlStartTime = DatatypeFactory.newInstance()
    .newXMLGregorianCalendar(
        ((GregorianCalendar)GregorianCalendar.getInstance());
xmlStartTime.setTimezone(0);
GregorianCalendar startCalendar = xmlStartTime.toGregorianCalendar();
Date startDate = startCalendar.getTime();
XMLGregorianCalendar xmlStartTime = DatatypeFactory.newInstance()
    .newXMLGregorianCalendar(startCalendar);
xmlStartTime.setHour(startDate.getHours());
xmlStartTime.setDay(startDate.getDate());
xmlStartTime.setMinute(startDate.getMinutes());
xmlStartTime.setMonth(startDate.getMonth()+1);
xmlStartTime.setTimezone(-startDate.getTimezoneOffset());
xmlStartTime.setSecond(startDate.getSeconds());
xmlStartTime.setYear(startDate.getYear() + 1900);
System.out.println(xmlStartTime.toString());

Resultado:

2015-08-26T12:02:27.183Z
2015-08-26T14:02:27.183+02:00
EpicPandaForce
fonte
0

Este código foi útil em um aplicativo em que estou trabalhando:

    Instant date = null;
    Date sdf = null;
    String formatTemplate = "EEE MMM dd yyyy HH:mm:ss";
    try {
        SimpleDateFormat isoFormat = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss");
        isoFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.of("US/Pacific")));
        sdf = isoFormat.parse(timeAtWhichToMakeAvailable);
        date = sdf.toInstant();

    } catch (Exception e) {
        System.out.println("did not parse: " + timeAtWhichToMakeAvailable);
    }

    LOGGER.info("timeAtWhichToMakeAvailable: " + timeAtWhichToMakeAvailable);
    LOGGER.info("sdf: " + sdf);
    LOGGER.info("parsed to: " + date);
VikR
fonte