Forçar fuso horário Java como GMT / UTC

95

Preciso forçar qualquer operação relacionada ao horário para GMT / UTC, independentemente do fuso horário definido na máquina. Alguma maneira conveniente de fazer isso no código?

Para esclarecer, estou usando o horário do servidor DB para todas as operações, mas sai formatado de acordo com o fuso horário local.

Obrigado!

SyBer
fonte
Na verdade, o problema dele é um subconjunto do meu, mas encontrei uma solução.
SyBer
Veja a pergunta duplicada e pesquise "Joda" e "DateTimeZone".
Basil Bourque

Respostas:

124

O OP respondeu a esta pergunta para alterar o fuso horário padrão para uma única instância de uma JVM em execução, defina a user.timezonepropriedade do sistema:

java -Duser.timezone=GMT ... <main-class>

Se você precisar definir fusos horários específicos ao recuperar objetos Date / Time / Timestamp de um banco de dados ResultSet, use a segunda forma dos getXXXmétodos que leva um Calendarobjeto:

Calendar tzCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
ResultSet rs = ...;
while (rs.next()) {
    Date dateValue = rs.getDate("DateColumn", tzCal);
    // Other fields and calculations
}

Ou definindo a data em uma PreparedStatement:

Calendar tzCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
PreparedStatement ps = conn.createPreparedStatement("update ...");
ps.setDate("DateColumn", dateValue, tzCal);
// Other assignments
ps.executeUpdate();

Isso garantirá que o valor armazenado no banco de dados seja consistente quando a coluna do banco de dados não mantém as informações de fuso horário.

As classes java.util.Datee java.sql.Datearmazenam a hora real (milissegundos) em UTC. Para formatar estes na saída para outro fuso horário, use SimpleDateFormat. Você também pode associar um fuso horário ao valor usando um objeto Calendar:

TimeZone tz = TimeZone.getTimeZone("<local-time-zone>");
//...
Date dateValue = rs.getDate("DateColumn");
Calendar calValue = Calendar.getInstance(tz);
calValue.setTime(dateValue);

Referência Útil

https://docs.oracle.com/javase/9/troubleshoot/time-zone-settings-jre.htm#JSTGD377

https://confluence.atlassian.com/kb/setting-the-timezone-for-the-java-environment-841187402.html

Kevin brock
fonte
Uma coisa a se observar sobre a configuração do fuso horário para toda a JVM é que isso afeta tudo, incluindo coisas como o registro. Apenas algo para se ter em mente se esse for o efeito desejado.
Hermann Hans
Sim, tudo bem. Apenas um ponto a ser mencionado, que eu defino o user.timezone diretamente no código, em vez de por meio do parâmetro -D.
SyBer
6
@SyBer: Isso funciona, mas você deve certificar-se de definir isso antes que qualquer método chame o método TimeZone.getDefault (). Caso contrário, você terá alguma confusão, pois o padrão é armazenado em cache após a primeira execução. Isso também significa que pode ser um problema se você fizer isso dentro de uma API / biblioteca (oculta da visualização simples) - certifique-se de documentar bastante o que está fazendo.
Kevin Brock,
23

Além disso, se você pode definir o fuso horário JVM desta forma

System.setProperty("user.timezone", "EST");

ou -Duser.timezone=GMTnos argumentos da JVM.

MG Developer
fonte
System.setProperty("user.timezone" ...)não funciona.
wilmol
9

Você pode alterar o fuso horário usando TimeZone.setDefault () :

TimeZone.setDefault(TimeZone.getTimeZone("GMT"))
snorbi
fonte
Sem ofensa, mas modificar temporariamente um estado estático global parece uma ideia muito ruim ...
G. Demecki
Você poderia, mas não deveria. Este é um conselho extraordinariamente ruim. Todo o JVM tem seu fuso horário alterado.
scot
1
Às vezes, é exatamente o que você deseja alcançar. Por exemplo. Tenho visto microsserviços baseados em Java rodando sempre com UTC para evitar problemas de fuso horário. Nesses sistemas, o backend do banco de dados também funciona com UTC, portanto, é natural "forçar" a JVM para UTC também. O que é importante: você tem que saber o que está fazendo e quais são as consequências ...
snorbi
absolutamente não. Se você quiser que todo o seu sistema esteja em UTC (uma ideia muito boa), defina o fuso horário do sistema como UTC e deixe o fuso horário Java sozinho.
scot
3
Exceto se você tiver vários JVMs com requisitos diferentes. Podemos discutir para sempre, mas cada caso é único: às vezes você precisa definir o fuso horário de todo o sistema, às vezes você define apenas um JVM. Vamos ficar felizes que os sistemas de hoje são tão flexíveis que podemos fazer ambos 😉
snorbi
8

para mim, basta rápido SimpleDateFormat,

  private static final SimpleDateFormat GMT = new SimpleDateFormat("yyyy-MM-dd");
  private static final SimpleDateFormat SYD = new SimpleDateFormat("yyyy-MM-dd");
  static {
      GMT.setTimeZone(TimeZone.getTimeZone("GMT"));
    SYD.setTimeZone(TimeZone.getTimeZone("Australia/Sydney"));
  }

em seguida, formate a data com um fuso horário diferente.

lwpro2
fonte
6

Gostaria de recuperar o tempo do banco de dados em uma forma bruta (timestamp longo ou data de java) e, em seguida, usar SimpleDateFormat para formatá-lo ou Calendar para manipulá-lo. Em ambos os casos, você deve definir o fuso horário dos objetos antes de usá-lo.

Veja SimpleDateFormat.setTimeZone(..)e Calendar.setTimeZone(..)para detalhes

Eyal Schneider
fonte
6

tl; dr

String sql = "SELECT CURRENT_TIMESTAMP ;

OffsetDateTime odt = myResultSet.getObject( 1 , OffsetDateTime.class ) ;

Evite depender do sistema operacional host ou JVM para o fuso horário padrão

Eu recomendo que você escreva todo o seu código para declarar explicitamente o fuso horário desejado / esperado. Você não precisa depender do fuso horário padrão atual da JVM. E esteja ciente de que o fuso horário padrão atual da JVM pode mudar a qualquer momento durante o tempo de execução, com qualquer código em qualquer thread de qualquer chamada de aplicativo TimeZone.setDefault. Tal chamada afeta todos os aplicativos dentro dessa JVM imediatamente.

java.time

Algumas das outras respostas estavam corretas, mas agora estão fora de moda. As terríveis classes de data e hora empacotadas com as primeiras versões do Java eram falhas, escritas por pessoas que não entendiam as complexidades e sutilezas do tratamento de data e hora.

As classes legadas de data e hora foram substituídas pelas classes java.time definidas no JSR 310.

Para representar um fuso horário, use ZoneId. Para representar um deslocamento do UTC, use ZoneOffset. Um deslocamento é apenas algumas horas-minutos-segundos à frente ou atrás do meridiano principal. Um fuso horário é muito mais. Um fuso horário é uma história das alterações passadas, presentes e futuras do deslocamento usado pelas pessoas de uma determinada região.

Preciso forçar qualquer operação relacionada ao horário para GMT / UTC

Para um deslocamento de zero horas-minutos-segundos, use a constante ZoneOffset.UTC.

Instant

Para capturar o momento atual em UTC, use um Instant. Esta classe representa um momento em UTC, sempre em UTC por definição.

Instant instant = Instant.now() ;  // Capture current moment in UTC.

ZonedDateTime

Para ver esse mesmo momento através do relógio de parede usado pelas pessoas de uma determinada região, ajuste o fuso horário. Mesmo momento, mesmo ponto na linha do tempo, horário diferente do relógio de parede.

ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

Base de dados

Você menciona um banco de dados.

Para recuperar um momento do banco de dados, sua coluna deve ser de um tipo de dados semelhante ao padrão SQL TIMESTAMP WITH TIME ZONE. Recupere um objeto em vez de uma string. No JDBC 4.2 e posterior, podemos trocar objetos java.time com o banco de dados. O OffsetDateTimeé exigido pelo JDBC, enquanto Instant& ZonedDateTimesão opcionais.

OffsetDateTime odt = myResultSet.getObject(  , OffsetDateTime.class ) ;

Na maioria dos bancos de dados e drivers, acho que você obterá o momento conforme visto no UTC. Mas se não, você pode ajustar de duas maneiras:

  • Extraia um Instant: odt.toInstant()
  • Ajuste do deslocamento fornecido para um deslocamento de zero: OffsetDateTime odtUtc = odt.withOffsetSameInstant (ZoneOffset.UTC); `.

Tabela de tipos de data e hora em Java (legado e moderno) e em SQL padrão


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.

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

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

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 cordas, não há necessidade de java.sql.*classes.

Onde obter as classes java.time?

Basil Bourque
fonte
1

crie um par cliente / servidor, para que após a execução, cliente servidor envie a hora e data corretas. Então, o cliente pergunta ao servidor pm GMT e o servidor envia de volta a resposta certa.

Bertan
fonte
1

Use a propriedade do sistema:

-Duser.timezone=UTC
Dawid Stępień
fonte
5
Por que usar isso? Por favor, adicione alguma explicação à sua resposta para que outras pessoas possam aprender com ela
Nico Haase,
1

Execute esta linha no MySQL para redefinir seu fuso horário:

SET @@global.time_zone = '+00:00';
Saeed
fonte
0

Uau. Eu sei que este é um segmento antigo, mas tudo que posso dizer é não chame TimeZone.setDefault () em nenhum código de nível de usuário. Isso sempre define o fuso horário para toda a JVM e quase sempre é uma péssima ideia. Aprenda a usar a biblioteca joda.time ou a nova classe DateTime em Java 8, que é muito semelhante à biblioteca joda.time.

escocês
fonte
-6

Se você gostaria de obter a hora GMT apenas com intiger: var currentTime = new Date (); var currentYear = '2010' var currentMonth = 10; var currentDay = '30 'var currentHours = '20' var currentMinutes = '20 'var currentSeconds = '00' var currentMilliseconds = '00 '

currentTime.setFullYear(currentYear);
currentTime.setMonth((currentMonth-1)); //0is January
currentTime.setDate(currentDay);  
currentTime.setHours(currentHours);
currentTime.setMinutes(currentMinutes);
currentTime.setSeconds(currentSeconds);
currentTime.setMilliseconds(currentMilliseconds);

var currentTimezone = currentTime.getTimezoneOffset();
currentTimezone = (currentTimezone/60) * -1;
var gmt ="";
if (currentTimezone !== 0) {
  gmt += currentTimezone > 0 ? ' +' : ' ';
  gmt += currentTimezone;
}
alert(gmt)
Incmos
fonte
5
que se parece com JavaScript (! = Java) para mim.
Phil