Estou tentando definir uma data e hora agnóstica do servidor em meu banco de dados e acredito que a melhor prática para fazer isso é definir uma data e hora UTC. Meu servidor db é Cassandra e o driver db para Java entende apenas o tipo de data.
Portanto, supondo que em meu código estou usando o novo Java 8 ZonedDateTime para obter o UTC now ( ZonedDateTime.now(ZoneOffset.UTC)
), como posso converter essa instância de ZonedDateTime para a classe Date "legada"?
java
java-8
java.util.date
datetime-conversion
zoneddatetime
Milen Kovachev
fonte
fonte
java.util.Date
ejava.sql.Date
.Respostas:
Você pode converter ZonedDateTime em um instante, que pode ser usado diretamente com Date.
fonte
Date
é um número de milissegundos desde a época - portanto, está relacionado ao UTC. Se você imprimi- lo, o fuso horário padrão será usado, mas a classe Date não tem conhecimento do fuso horário do usuário ... Veja por exemplo a seção "mapeamento" em docs.oracle.com/javase/tutorial/datetime/iso/legacy .html E LocalDateTime é explicitamente sem referência a um fuso horário - o que pode ser visto como confuso ...ZonedDateTime
. Ajava.time.Instant
classe é uma substituição direta dejava.util.Date
, ambas representando um momento no UTC, emboraInstant
use uma resolução mais precisa de nanossegundos em vez de milissegundos.Date.from( Instant.now() )
deveria ter sido sua solução. Ou por falar nisso, apenasnew Date()
que tenha o mesmo efeito, captando o momento atual em UTC.tl; dr
Embora não houvesse nenhum ponto naquele código acima. Ambos
java.util.Date
eInstant
representam um momento na UTC, sempre na UTC. O código acima tem o mesmo efeito que:Nenhum benefício aqui para usar
ZonedDateTime
. Se você já tiver umZonedDateTime
, ajuste para UTC extraindo umInstant
.Outra resposta correta
A resposta de ssoltanid aborda corretamente sua questão específica, como converter um objeto java.time (
ZonedDateTime
) da nova escola em um objeto da velha escolajava.util.Date
. Extraia oInstant
de ZonedDateTime e passe parajava.util.Date.from()
.Perda de dados
Observe que você sofrerá perda de dados , pois
Instant
rastreia nanossegundos desde a época, enquantojava.util.Date
rastreia milissegundos desde a época.Sua pergunta e comentários levantam outras questões.
Manter servidores em UTC
Seus servidores devem ter seu sistema operacional host definido para UTC como uma prática recomendada em geral. A JVM escolhe essa configuração do sistema operacional host como seu fuso horário padrão, nas implementações Java que conheço.
Especifique o fuso horário
Mas você nunca deve confiar no fuso horário padrão atual da JVM. Em vez de escolher a configuração do host, um sinalizador passado ao iniciar uma JVM pode definir outro fuso horário. Pior ainda: qualquer código em qualquer thread de qualquer aplicativo a qualquer momento pode fazer uma chamada para
java.util.TimeZone::setDefault
para alterar esse padrão em tempo de execução!Timestamp
Tipo CassandraQualquer banco de dados e driver decente deve lidar automaticamente com o ajuste de uma data e hora passada para UTC para armazenamento. Eu não uso o Cassandra, mas parece ter algum suporte rudimentar para data e hora. A documentação diz que seu
Timestamp
tipo é uma contagem de milissegundos da mesma época (primeiro momento de 1970 em UTC).ISO 8601
Além disso, o Cassandra aceita entradas de string nos formatos padrão ISO 8601 . Felizmente, java.time usa formatos ISO 8601 como padrão para analisar / gerar strings. A implementação da
Instant
classetoString
servirá perfeitamente.Precisão: Milissegundo vs Nanosegordo
Mas primeiro precisamos reduzir a precisão de nanossegundos de ZonedDateTime para milissegundos. Uma maneira é criar um novo Instant usando milissegundos. Felizmente, java.time tem alguns métodos úteis para conversão de e para milissegundos.
Código de exemplo
Aqui está um exemplo de código no Java 8 Update 60.
Ou de acordo com este documento de driver Cassandra Java , você pode passar uma
java.util.Date
instância (não deve ser confundida comjava.sqlDate
). Portanto, você pode fazer um juDate com issoinstantTruncatedToMilliseconds
no código acima.Se fizer isso com freqüência, você pode fazer um one-liner.
Mas seria mais legal criar um pequeno método utilitário.
Observe a diferença em todo esse código em relação à pergunta. O código da pergunta estava tentando ajustar o fuso horário da instância ZonedDateTime para UTC. Mas isso não é necessário. Conceitualmente:
Nós apenas extraímos a parte Instant, que já está em UTC (basicamente em UTC, leia a documentação da classe para detalhes precisos).
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
, eSimpleDateFormat
.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 Oracle . E pesquise no 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 cordas, não há necessidade de
java.sql.*
classes.Onde obter as classes java.time?
O projeto ThreeTen-Extra estende java.time com classes adicionais. Este projeto é um campo de provas para possíveis adições futuras ao java.time. Você pode encontrar algumas classes úteis aqui, como
Interval
,YearWeek
,YearQuarter
, e mais .fonte
Aqui está um exemplo de conversão da hora atual do sistema em UTC. Envolve a formatação de ZonedDateTime como String e, em seguida, o objeto String será analisado em um objeto de data usando java.text DateFormat.
fonte
Se você estiver usando o backport ThreeTen para Android e não puder usar o mais recente
Date.from(Instant instant)
(que requer no mínimo API 26), você pode usar:ou:
Leia também o conselho na resposta de Basil Bourque
fonte
DateTimeUtils
classe com os métodos de conversão, então eu usaria Date date = DateTimeUtils.toDate (zdt.toInstant ()); `. Não é tão baixo.Você pode fazer isso usando as classes java.time integradas ao Java 8 e posterior.
fonte
Instant
s rastreia segundos e nanossegundos.Date
s trilha milissegundos. Essa conversão de um para o outro está correta.Eu uso isso.
Porque
Date.from(java.time.ZonedDateTime.ofInstant(now, zoneId).toInstant());
não dá certo !!! Se vc executar seu aplicativo em seu computador, não é problema. Mas se você executar em qualquer região do AWS ou Docker ou GCP, isso vai gerar problema. Porque o computador não é o seu fuso horário na nuvem. Você deve definir seu fuso horário corretamente no Código. Por exemplo, Ásia / Taipei. Em seguida, ele irá corrigir no AWS ou Docker ou GCP.fonte
ans=Mon Jun 18 01:07:56 CEST 2018
, o que está incorreto ewrongAns=Sun Jun 17 17:07:56 CEST 2018
, em seguida , o que está correto.A resposta aceita não funcionou para mim. A data retornada é sempre a data local e não a data do fuso horário original. Eu moro na UTC + 2.
Eu vim com duas maneiras alternativas de obter a data correta de um ZonedDateTime.
Digamos que você tenha este ZonedDateTime para o Havaí
ou para UTC como solicitado originalmente
Alternativa 1
Podemos usar java.sql.Timestamp. É simples, mas provavelmente também afetará sua integridade de programação
Alternativa 2
Criamos a data de millis (respondido aqui anteriormente). Observe que o ZoneOffset local é obrigatório.
fonte
Para um aplicativo docker como o beehuang comentou, você deve definir seu fuso horário.
Como alternativa, você pode usar withZoneSameLocal . Por exemplo:
01-07-2014T00: 00 + 02: 00 [GMT + 02: 00] é convertido por
para Ter 01 de julho 00:00:00 CEST 2014 e até
a Seg 30 de junho às 22:00:00 UTC de 2014
fonte
Se você está interessado apenas no agora, basta usar:
fonte