tl; dr
LocalDate.now( ZoneId.of( "Africa/Tunis" ) )
.atStartOfDay( ZoneId.of( "Africa/Tunis" ) )
.toEpochSecond()
…
LocalDate.now( ZoneId.of( "Africa/Tunis" ) )
.plusDays( 1 )
.atStartOfDay( ZoneId.of( "Africa/Tunis" ) )
.toEpochSecond()
…
"SELECT * FROM orders WHERE placed >= ? AND placed < ? ; "
…
myPreparedStatement.setObject( 1 , start )
myPreparedStatement.setObject( 2 , stop )
java.time
Você está usando classes antigas e problemáticas que agora são legadas, suplantadas pelas classes java.time modernas .
Aparentemente, você está armazenando um momento em seu banco de dados em uma coluna de algum tipo inteiro. Isso é uma pena. Em vez disso, você deve usar uma coluna de um tipo como o padrão SQL TIMESTAMP WITH TIME ZONE
. Mas, para esta Resposta, vamos trabalhar com o que temos.
Se seus registros representam momentos com uma resolução de milissegundos e você deseja todos os registros de um dia inteiro, então precisamos de um intervalo de tempo. Devemos ter um momento de início e um momento de parada. Sua consulta tem apenas um único critério de data e hora em que deveria haver um par.
A LocalDate
classe representa um valor apenas de data sem hora do dia e sem fuso horário.
Um fuso horário é crucial para determinar uma data. Para qualquer momento, a data varia ao redor do globo por zona. Por exemplo, alguns minutos depois da meia-noite em Paris, a França é um novo dia e ainda “ontem” em Montreal Québec .
Se nenhum fuso horário for especificado, a JVM aplicará implicitamente seu fuso horário padrão atual. Esse padrão pode mudar a qualquer momento, então seus resultados podem variar. Melhor especificar o fuso horário desejado / esperado explicitamente como um argumento.
Especifique um nome próprio fuso horário no formato continent/region
, como America/Montreal
, Africa/Casablanca
, ou Pacific/Auckland
. Nunca utilize o 3-4 letras, como EST
ou IST
como eles são não zonas verdadeiros tempo, não padronizados, e nem mesmo única (!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Se você quiser usar o fuso horário padrão atual da JVM, pergunte por ele e passe como um argumento. Se omitido, o padrão atual da JVM é aplicado implicitamente. Melhor ser explícito, pois o padrão pode ser alterado a qualquer momento durante o tempo de execução por qualquer código em qualquer encadeamento de qualquer aplicativo dentro da JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Ou especifique uma data. Você pode definir o mês por um número, com a numeração normal de 1 a 12 para janeiro a dezembro.
LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ; // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.
Ou, melhor, use os Month
objetos enum predefinidos, um para cada mês do ano. Dica: Use esses Month
objetos em toda a sua base de código em vez de um mero número inteiro para tornar seu código mais autodocumentado, garantir valores válidos e fornecer segurança de tipo .
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
Com um LocalDate
em mãos, precisamos transformá-lo em um par de momentos, o início e o fim do dia. Não presuma que o dia começa às 00:00:00 hora do dia. Devido a anomalias como horário de verão (DST), o dia pode começar em outro horário, como 01:00:00. Portanto, deixe java.time determinar o primeiro momento do dia. Passamos um ZoneId
argumento LocalDate::atStartOfDay
para procurar essas anomalias. O resultado é um ZonedDateTime
.
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;
Geralmente, a melhor abordagem para definir um intervalo de tempo é a abordagem Half-Open, onde o início é inclusivo, enquanto o final é exclusivo . Portanto, um dia começa com seu primeiro momento e vai até, mas não incluindo, o primeiro momento do dia seguinte.
ZonedDateTime zdtStop = ld.plusDays( 1 ).atStartOfDay( z ) ; // Determine the following date, and ask for the first moment of that day.
Nossa consulta de um dia inteiro não pode usar o comando SQL BETWEEN
. Esse comando é Fully-Closed ( []
) (tanto o início quanto o fim são inclusivos) onde quisermos Half-Open ( [)
). Usamos um par de critérios >=
e <
.
Sua coluna está mal nomeada. Evite qualquer uma das mil palavras reservadas por vários bancos de dados. Vamos usar placed
neste exemplo.
Seu código deve ter usado ?
espaços reservados para especificar nossos momentos.
String sql = "SELECT * FROM orders WHERE placed >= ? AND placed < ? ; " ;
Mas temos ZonedDateTime
objetos em mãos, enquanto seu banco de dados aparentemente está armazenando inteiros, conforme discutido acima. Se você tivesse definido sua coluna corretamente, poderíamos simplesmente passar os ZonedDateTime
objetos com qualquer driver JDBC com suporte a JDBC 4.2 ou posterior.
Mas, em vez disso, precisamos obter uma contagem da época em segundos inteiros. Assumirei que sua data de referência de época é o primeiro momento de 1970 em UTC. Cuidado com a possível perda de dados, pois a ZonedDateTime
classe é capaz de resolução de nanossegundos. Qualquer segundo fracionário será truncado nas linhas a seguir.
long start = zdtStart().toEpochSecond() ; // Transform to a count of whole seconds since 1970-01-01T00:00:00Z.
long stop = zdtStop().toEpochSecond() ;
Agora estamos prontos para passar esses inteiros para nosso código SQL definido acima.
PreparedStatement ps = con.prepareStatement( sql );
ps.setObject( 1 , start ) ;
ps.setObject( 2 , stop ) ;
ResultSet rs = ps.executeQuery();
Ao recuperar seus valores inteiros do ResultSet
, você pode transformar em Instant
objetos (sempre em UTC) ou em ZonedDateTime
objetos (com um fuso horário atribuído).
Instant instant = rs.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
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 Oracle . E pesquise no Stack Overflow para muitos exemplos e explicações. A especificação é JSR 310 .
Onde obter as classes java.time?
- Java SE 8 , Java SE 9 e posterior
- Construídas em.
- Parte da API Java padrão com uma implementação agrupada.
- Java 9 adiciona alguns recursos e correções menores.
- Java SE 6 e Java SE 7
- Grande parte da funcionalidade java.time é portada para Java 6 e 7 no ThreeTen-Backport .
- Android
- Versões posteriores de implementações de pacotes Android das classes java.time.
- Para anteriormente Android, o ThreeTenABP projeto adapta ThreeTen-Backport (mencionados acima). Consulte Como usar o ThreeTenABP… .
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 .