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
Está utilizando clases de fecha y hora antiguas problemáticas que ahora son heredadas, suplantadas por el moderno java.time clases .
Aparentemente, está almacenando un momento en su base de datos en una columna de algún tipo entero. Eso es desafortunado. En su lugar, debería utilizar una columna de un tipo como el estándar SQL TIMESTAMP WITH TIME ZONE
. Pero, para esta Respuesta, trabajaremos con lo que tenemos.
Si sus registros representan momentos con una resolución de milisegundos y desea todos los registros de un día completo, entonces necesitamos un intervalo de tiempo. Debemos tener un momento de inicio y un momento de parada. Su consulta tiene un solo criterio de fecha y hora donde debería haber tenido un par.
los LocalDate
clase representa un valor de solo fecha sin hora del día y sin zona horaria.
Una zona horaria es crucial para determinar una fecha. Para un momento dado, la fecha varía en todo el mundo según la zona. Por ejemplo, unos minutos después de la medianoche en París, Francia es un nuevo día, mientras que todavía es “ayer” en Montreal, Québec .
Si no se especifica ninguna zona horaria, la JVM aplica implícitamente su zona horaria predeterminada actual. Ese valor predeterminado puede cambiar en cualquier momento, por lo que sus resultados pueden variar. Es mejor especificar la zona horaria deseada / esperada explícitamente como argumento.
Especificar un nombre de zona horaria correcta en el formato de continent/region
, por ejemplo America/Montreal
, Africa/Casablanca
o Pacific/Auckland
. Nunca use el 3-4 abreviatura de letras tales como EST
o IST
, ya que son no verdaderas zonas de tiempo, no estandarizados, y ni siquiera único (!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Si desea utilizar la zona horaria predeterminada actual de la JVM, solicítela y páselo como argumento. Si se omite, el valor predeterminado actual de la JVM se aplica implícitamente. Es mejor ser explícito, ya que el valor predeterminado puede cambiarse en cualquier momento durante el tiempo de ejecución mediante cualquier código en cualquier hilo de cualquier aplicación dentro de la JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
O especifique una fecha. Puede establecer el mes con un número, con una numeración sensata del 1 al 12 para enero-diciembre.
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.
O, mejor, use los Month
objetos enum predefinidos, uno para cada mes del año. Consejo: utilice estos Month
objetos en todo su código base en lugar de un simple número entero para hacer que su código sea más autodocumentado, garantizar valores válidos y proporcionar seguridad de tipos .
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
Con un LocalDate
en la mano, lo siguiente que tenemos que hacer es transformar eso en un par de momentos, el comienzo y el final del día. No asuma que el día comienza a las 00:00:00 hora del día. Debido a anomalías como el horario de verano (DST), el día puede comenzar a otra hora, como la 01:00:00. Así que deja que java.time determine el primer momento del día. Pasamos un ZoneId
argumento a LocalDate::atStartOfDay
para buscar tales anomalías. El resultado es a ZonedDateTime
.
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;
Generalmente, el mejor enfoque para definir un lapso de tiempo es el enfoque semiabierto, donde el principio es inclusivo mientras que el final es exclusivo . Entonces, un día comienza con su primer momento y se extiende hasta, pero sin incluir, el primer momento del día siguiente.
ZonedDateTime zdtStop = ld.plusDays( 1 ).atStartOfDay( z ) ; // Determine the following date, and ask for the first moment of that day.
Nuestra consulta de un día entero no puede hacer uso del comando SQL BETWEEN
. Ese comando es Completamente cerrado ( []
) (tanto el principio como el final son inclusivos) donde queremos Half-Open ( [)
). Usamos un par de criterios >=
y<
.
Tu columna está mal nombrada. Evite cualquiera de las mil palabras reservadas por diversas bases de datos. Usemos placed
en este ejemplo.
Su código debería haber utilizado ?
marcadores de posición para especificar nuestros momentos.
String sql = "SELECT * FROM orders WHERE placed >= ? AND placed < ? ; " ;
Pero tenemos ZonedDateTime
objetos en la mano, mientras que su base de datos aparentemente almacena enteros como se discutió anteriormente. Si hubiera definido su columna correctamente podríamos simplemente pasar los ZonedDateTime
objetos con cualquier controlador JDBC de soporte de JDBC 4.2 o posterior.
Pero, en cambio, necesitamos obtener un recuento desde la época en segundos completos. Asumiré que la fecha de referencia de su época es el primer momento de 1970 en UTC. Tenga cuidado con la posible pérdida de datos, ya que la ZonedDateTime
clase tiene una resolución de nanosegundos. Cualquier fracción de segundo se truncará en las siguientes líneas.
long start = zdtStart().toEpochSecond() ; // Transform to a count of whole seconds since 1970-01-01T00:00:00Z.
long stop = zdtStop().toEpochSecond() ;
Ahora estamos listos para pasar esos números enteros a nuestro código SQL definido anteriormente.
PreparedStatement ps = con.prepareStatement( sql );
ps.setObject( 1 , start ) ;
ps.setObject( 2 , stop ) ;
ResultSet rs = ps.executeQuery();
Cuando recupera sus valores enteros del ResultSet
, puede transformarlos en Instant
objetos (siempre en UTC) o en ZonedDateTime
objetos (con una zona horaria asignada).
Instant instant = rs.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Acerca de java.time
El marco java.time está integrado en Java 8 y versiones posteriores. Estas clases suplantar la vieja problemáticos heredados clases de fecha y hora como java.util.Date
, Calendar
, y SimpleDateFormat
.
El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a java.time clases .
Para obtener más información, consulte el tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .
¿Dónde obtener las clases java.time?
- Java SE 8 , Java SE 9 y posterior
- Incorporado.
- Parte de la API estándar de Java con una implementación integrada.
- Java 9 agrega algunas funciones y correcciones menores.
- Java SE 6 y Java SE 7
- Gran parte de la funcionalidad de java.time está adaptada a Java 6 y 7 en ThreeTen-Backport .
- Androide
- Versiones posteriores de implementaciones de paquetes de Android de las clases java.time.
- Para principios de Android, el ThreeTenABP proyecto se adapta ThreeTen-Backport (mencionado anteriormente). Consulte Cómo utilizar ThreeTenABP… .
El proyecto ThreeTen-Extra extiende java.time con clases adicionales. Este proyecto es un campo de pruebas para posibles adiciones futuras a java.time. Usted puede encontrar algunas clases útiles aquí, como Interval
, YearWeek
, YearQuarter
, y más .