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
Stai usando vecchie classi data-ora fastidiose che ora sono ereditate, soppiantate dal moderno java.time classi .
Apparentemente stai memorizzando un momento nel tuo database in una colonna di qualche tipo intero. Questo è sfortunato. Dovresti invece usare una colonna di un tipo come lo standard SQLTIMESTAMP WITH TIME ZONE
. Ma, per questa risposta, lavoreremo con ciò che abbiamo.
Se i tuoi record rappresentano momenti con una risoluzione di millisecondi e desideri tutti i record per un'intera giornata, è necessario un intervallo di tempo. Dobbiamo avere un momento di inizio e un momento di arresto. La tua query ha un solo criterio data-ora in cui avrebbe dovuto avere una coppia.
Il LocalDate
classe rappresenta un valore di sola data senza ora del giorno e senza fuso orario.
Un fuso orario è fondamentale per determinare una data. Per ogni dato momento, la data varia in tutto il mondo in base alla zona. Ad esempio, pochi minuti dopo la mezzanotte a Parigi, la Francia è un nuovo giorno mentre ancora "ieri" a Montréal Québec .
Se non viene specificato alcun fuso orario, la JVM applica implicitamente il proprio fuso orario predefinito corrente. L'impostazione predefinita può cambiare in qualsiasi momento, quindi i risultati potrebbero variare. È meglio specificare esplicitamente il fuso orario desiderato / previsto come argomento.
Specificare un nome proprio fuso orario nel formato continent/region
, come ad esempio America/Montreal
, Africa/Casablanca
o Pacific/Auckland
. Non usare mai il 3-4 lettera sigla come ad esempio EST
o IST
come sono non fusi orari veri e propri, non standardizzati, e nemmeno unico (!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Se desideri utilizzare l'attuale fuso orario predefinito della JVM, richiedilo e passa come argomento. Se omesso, il valore predefinito corrente della JVM viene applicato implicitamente. Meglio essere espliciti, poiché l'impostazione predefinita può essere modificata in qualsiasi momento durante il runtime da qualsiasi codice in qualsiasi thread di qualsiasi app all'interno della JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Oppure specifica una data. Puoi impostare il mese con un numero, con una numerazione sana da 1 a 12 per gennaio-dicembre.
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, meglio, usa gli Month
oggetti enum predefiniti, uno per ogni mese dell'anno. Suggerimento: utilizza questi Month
oggetti in tutta la base del codice anziché un semplice numero intero per rendere il codice più auto-documentante, garantire valori validi e fornire l'indipendenza dai tipi .
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
Con un LocalDate
in mano, dobbiamo poi trasformarlo in un paio di momenti, l'inizio e la fine della giornata. Non dare per scontato che la giornata inizi alle 00:00:00 ora del giorno. A causa di anomalie come l'ora legale (DST), il giorno può iniziare in un altro momento come l'01: 00:00. Quindi lascia che java.time determini il primo momento della giornata. Passiamo un ZoneId
argomento a LocalDate::atStartOfDay
per cercare tali anomalie. Il risultato è un file ZonedDateTime
.
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;
Generalmente l'approccio migliore per definire un arco di tempo è l'approccio Half-Open in cui l'inizio è inclusivo mentre il finale è esclusivo . Quindi un giorno inizia con il suo primo momento e arriva fino al primo momento del giorno successivo, ma non lo include.
ZonedDateTime zdtStop = ld.plusDays( 1 ).atStartOfDay( z ) ; // Determine the following date, and ask for the first moment of that day.
La nostra query per un'intera giornata non può utilizzare il comando SQL BETWEEN
. Quel comando è Fully-Closed ( []
) (sia l'inizio che la fine sono inclusi) dove vogliamo Half-Open ( [)
). Usiamo un paio di criteri >=
e <
.
La tua colonna ha un nome sbagliato. Evita le mille parole riservate dai vari database. Usiamo placed
in questo esempio.
Il tuo codice avrebbe dovuto utilizzare ?
segnaposto in cui specificare i nostri momenti.
String sql = "SELECT * FROM orders WHERE placed >= ? AND placed < ? ; " ;
Ma abbiamo ZonedDateTime
oggetti in mano, mentre il tuo database a quanto pare memorizza numeri interi come discusso sopra. Se avessi definito correttamente la tua colonna, potremmo semplicemente passare gli ZonedDateTime
oggetti con qualsiasi driver JDBC che supporti JDBC 4.2 o successivo.
Ma invece dobbiamo ottenere un conteggio dall'epoca in secondi interi. Presumo che la data di riferimento della tua epoca sia il primo momento del 1970 in UTC. Attenzione alla possibile perdita di dati, poiché la ZonedDateTime
classe è in grado di risoluzione in nanosecondi. Qualsiasi frazione di secondo verrà troncata nelle righe seguenti.
long start = zdtStart().toEpochSecond() ; // Transform to a count of whole seconds since 1970-01-01T00:00:00Z.
long stop = zdtStop().toEpochSecond() ;
Ora siamo pronti per passare questi numeri interi al nostro codice SQL definito sopra.
PreparedStatement ps = con.prepareStatement( sql );
ps.setObject( 1 , start ) ;
ps.setObject( 2 , stop ) ;
ResultSet rs = ps.executeQuery();
Quando recuperi i tuoi valori interi da ResultSet
, puoi trasformarli in Instant
oggetti (sempre in UTC) o in ZonedDateTime
oggetti (con un fuso orario assegnato).
Instant instant = rs.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Informazioni su java.time
Il framework java.time è integrato in Java 8 e versioni successive. Queste classi soppiantare la vecchia fastidiosi legacy classi data-time come java.util.Date
, Calendar
, e SimpleDateFormat
.
Il progetto Joda-Time , ora in modalità di manutenzione , consiglia la migrazione alle classi java.time .
Per saperne di più, vedere il tutorial Oracle . E cerca Stack Overflow per molti esempi e spiegazioni. La specifica è JSR 310 .
Dove ottenere le classi java.time?
- Java SE 8 , Java SE 9 e versioni successive
- Built-in.
- Parte dell'API Java standard con un'implementazione in bundle.
- Java 9 aggiunge alcune funzionalità e correzioni minori.
- Java SE 6 e Java SE 7
- Gran parte delle funzionalità java.time sono portate indietro a Java 6 e 7 in ThreeTen-Backport .
- androide
- Versioni successive delle implementazioni di bundle Android delle classi java.time.
- Per le versioni precedenti di Android, il progetto ThreeTenABP adatta ThreeTen-Backport (menzionato sopra). Vedi Come usare ThreeTenABP… .
Il progetto ThreeTen-Extra estende java.time con classi aggiuntive. Questo progetto è un banco di prova per possibili future aggiunte a java.time. Si possono trovare alcune classi utili, come per esempio Interval
, YearWeek
, YearQuarter
e altro .