Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Manipulando o fuso horário no aplicativo da web


Leia a pergunta Práticas recomendadas de horário de verão e fuso horário. O seu é basicamente uma duplicata.

Servidores em UTC


Sim, geralmente os servidores devem ter seu sistema operacional definido como UTC como fuso horário ou, se não fornecido, use GMT ou o fuso horário de Reykjavík Islândia. Sua implementação Java provavelmente pega essa configuração como seu próprio fuso horário padrão atual.

Especificar fuso horário


Mas não dependa do fuso horário definido como UTC. Um administrador de sistema poderia alterá-lo. E qualquer código Java em qualquer thread de qualquer aplicativo em sua JVM pode alterar o fuso horário padrão atual da JVM em tempo de execução chamando TimeZone.setDefault . Então, crie o hábito de sempre especificar o fuso horário desejado/esperado passando o argumento opcional em seu código Java.

Considero uma falha de design que qualquer estrutura de data e hora torne o fuso horário opcional. Ser opcional cria uma quantidade infinita de confusão porque os programadores, como todo mundo, inconscientemente pensam em termos de seu próprio fuso horário pessoal, a menos que sejam solicitados. Portanto, com muita frequência, no trabalho de data e hora, nenhuma atenção é dada ao problema. Adicione o problema de que o padrão da JVM varia. A propósito, idem para Locale , mesmos problemas, devem sempre ser especificados explicitamente.

UTC


Sua lógica de negócios, armazenamento de dados e troca de dados quase sempre devem ser feitos em UTC. Quase todos os bancos de dados têm um recurso para ajustar qualquer entrada em UTC e armazenar em UTC.

Ao apresentar uma data e hora para um usuário, ajuste o fuso horário esperado. Ao serializar um valor de data e hora, use os formatos de string ISO 8601. Veja a resposta de VickyArora para Oracle especificamente (eu sou uma pessoa do Postgres). Certifique-se de ler o documento com atenção e praticar experimentando para entender completamente o comportamento do seu banco de dados. A especificação SQL não explica muito a esse respeito, e o comportamento varia muito.

java.sql


Lembre-se que ao usar Java e JDBC, você estará usando o java.sql.Timestamp e tipos de dados relacionados. Eles estão sempre em UTC, automaticamente. No futuro, espere ver os drivers JDBC atualizados para usar diretamente os novos tipos de dados definidos na estrutura java.time incorporada ao Java 8 e posterior.

java.time


As classes antigas estão ultrapassadas pelo java.time. Aprenda a usar java.time evitando o antigo java.util.Date/.Calendar e tornando sua vida de programação muito mais agradável.

Até que seu driver JDBC seja atualizado, você pode usar os métodos de conveniência de conversão integrados ao java.time. Veja os exemplos a seguir, onde Instant é um momento em UTC e ZonedDateTime é um Instant ajustado em um fuso horário.
Instant instant = myJavaSqlTimestamp.toInstant();
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

Para ir na outra direção.
java.sql.Timestamp myJavaSqlTimestamp = java.sql.Timestamp.from( zdt.toInstant() );

Se você precisar do fuso horário original, armazene-o


Se seus requisitos de negócios consideram o fuso horário dos dados de entrada original importante, a ser lembrado, armazene-o explicitamente como uma coluna separada em sua tabela de banco de dados. Você pode usar um deslocamento de UTC, mas isso não fornece informações completas. Um fuso horário é um deslocamento mais um conjunto de regras para o tratamento passado, presente e futuro de anomalias, como o horário de verão. Portanto, um nome de fuso horário adequado é mais apropriado, como America/Montreal .

Somente data é ambíguo


Você disse que coleta muitos valores somente de data, sem hora do dia e sem fuso horário. A classe para isso em java.time é LocalDate . Assim como com LocalTime e LocalDateTime , a parte "Local..." significa nenhuma localidade específica, portanto, nenhum fuso horário e, portanto, não um ponto na linha do tempo - não tem significado real.

Lembre-se de que um valor somente de data é ambíguo por definição. A qualquer momento, a data varia ao redor do mundo. Por exemplo, logo após a meia-noite em Paris, a França é um novo dia, mas em Montréal Québec a data ainda é “ontem”.

Normalmente, nos negócios, algum fuso horário está implícito, mesmo inconscientemente. A intuição inconsciente sobre pontos de dados tende a não funcionar bem a longo prazo, especialmente em software. Melhor deixar explícito qual fuso horário foi pretendido. Você pode armazenar a zona pretendida ao lado da data, como outra coluna na tabela do banco de dados, ou pode fazer um comentário em seu código de programação. Acredito que seria muito melhor e mais seguro armazenar um valor de data e hora. Então, como transformamos uma data somente em uma data e hora?

Muitas vezes um novo dia é o momento depois da meia-noite, o primeiro momento do dia. Você pode pensar que isso significa a hora do dia 00:00:00.0 Mas não sempre. O horário de verão (DST) e possivelmente outras anomalias podem empurrar o primeiro momento para um horário diferente do relógio de parede. Deixe java.time determinar a hora do dia correta para o primeiro momento passando pelo LocalDate classe e seu atStartOfDay método.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( zoneId );
ZonedDateTime todayStart = today.atStartOfDay( zoneId );

Em alguns contextos de negócios, um novo dia pode ser definido (ou assumido) como horário comercial. Por exemplo, digamos que uma editora em Nova York signifique 9h no horário local quando diz “o rascunho do livro deve ser entregue em 2 de janeiro”. Vamos obter essa hora do dia para essa data nesse fuso horário.
ZoneId zoneId = ZoneId.of( "America/New_York" );
ZonedDateTime zdt = ZonedDateTime.of( 2016 , 1 , 2 , 9 , 0 , 0 , 0 , zoneId );

O que isso significa para o autor que trabalha na Nova Zelândia? Ajuste o fuso horário específico dela para a apresentação chamando withZoneSameInstant .
ZoneId zoneId_Pacific_Auckland = ZoneId.of( "Pacific/Auckland" );
ZonedDateTime zdt_Pacific_Auckland = zdt.withZoneSameInstant( zoneId_Pacific_Auckland );

Banco de dados


Para armazenamento de banco de dados, transformamos em um Instant (um momento na linha do tempo em UTC) e passar como um java.sql.Timestamp como visto anteriormente acima.
java.sql.Timestamp ts = java.sql.Timestamp.from( zdt.toInstant() );

Quando recuperado do banco de dados, transforme novamente em uma data e hora de Nova York. Converter de java.sql.Timestamp para um Instant , em seguida, aplique um fuso horário ZoneId para obter um ZonedDateTime .
Instant instant = ts.toInstant();
ZoneId zoneId = ZoneId.of( "America/New_York" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

Se seu driver de banco de dados estiver em conformidade com JDBC 4.2 ou posterior, você poderá passar/buscar os tipos java.time diretamente em vez de converter de/para tipos java.sql. Experimente o PreparedStatement::setObject e ResultSet::getObject métodos.