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
Sie verwenden problematische alte Datums- / Uhrzeitklassen , die jetzt älter sind und durch die moderne java.time ersetzt werden Klassen ersetzt werden.
Anscheinend speichern Sie einen Moment in Ihrer Datenbank in einer Spalte eines ganzzahligen Typs. Das ist bedauerlich. Sie sollten stattdessen eine Spalte eines Typs wie den SQL-Standard verwenden TIMESTAMP WITH TIME ZONE
. Aber für diese Antwort werden wir mit dem arbeiten, was wir haben.
Wenn Ihre Datensätze Momente mit einer Auflösung von Millisekunden darstellen und Sie alle Datensätze für einen ganzen Tag benötigen, benötigen wir einen Zeitbereich. Wir müssen einen Start- und einen Stoppmoment haben. Ihre Abfrage hat nur ein einziges Datum-Uhrzeit-Kriterium, bei dem sie ein Paar haben sollte.
Die LocalDate
Klasse repräsentiert einen Nur-Datum-Wert ohne Tageszeit und ohne Zeitzone.
Eine Zeitzone ist entscheidend für die Bestimmung eines Datums. Für jeden Moment variiert das Datum weltweit je nach Zone. Zum Beispiel ist ein paar Minuten nach Mitternacht in Paris Frankreich ein neuer Tag, während es in Montréal Québec noch „gestern“ ist .
Wenn keine Zeitzone angegeben ist, wendet die JVM implizit ihre aktuelle Standardzeitzone an. Diese Standardeinstellung kann sich jederzeit ändern, sodass Ihre Ergebnisse variieren können. Geben Sie Ihre gewünschte / erwartete Zeitzone besser explizit als Argument an.
Geben Sie einen richtigen Zeitzonennamen im Format continent/region
, wie zum Beispiel America/Montreal
, Africa/Casablanca
oder Pacific/Auckland
. Verwenden Sie niemals die Abkürzung für 3-4 Buchstaben, wie EST
oder, IST
da es sich nicht um echte Zeitzonen handelt, die nicht standardisiert und nicht einmal eindeutig (!) Sind.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Wenn Sie die aktuelle Standardzeitzone der JVM verwenden möchten, fragen Sie danach und übergeben Sie sie als Argument. Wenn nicht angegeben, wird der aktuelle Standard der JVM implizit angewendet. Es ist besser, explizit zu sein, da die Standardeinstellung jederzeit zur Laufzeit durch einen beliebigen Code in einem beliebigen Thread einer App innerhalb der JVM geändert werden kann .
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Oder geben Sie ein Datum an. Sie können den Monat durch eine Zahl mit der vernünftigen Nummer 1-12 für Januar bis Dezember einstellen.
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.
Oder verwenden Sie besser die Month
vordefinierten Enum-Objekte, eines für jeden Monat des Jahres. Tipp: Verwenden Sie diese Month
Objekte in Ihrer gesamten Codebasis und nicht nur als Ganzzahl, um Ihren Code selbstdokumentierender zu gestalten, gültige Werte sicherzustellen und Typensicherheit zu gewährleisten .
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
Mit einer LocalDate
Hand müssen wir das als nächstes in ein Paar Momente verwandeln, den Start und Stopp des Tages. Gehen Sie nicht davon aus, dass der Tag um 00:00:00 Uhr beginnt. Aufgrund von Anomalien wie der Sommerzeit (DST) kann der Tag zu einer anderen Zeit wie 01:00:00 Uhr beginnen. Lassen Sie also java.time den ersten Moment des Tages bestimmen. Wir geben ein ZoneId
Argument ab, LocalDate::atStartOfDay
um solche Anomalien nachzuschlagen. Das Ergebnis ist a ZonedDateTime
.
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;
Im Allgemeinen ist der Half-Open-Ansatz der beste Ansatz zur Definition einer Zeitspanne, bei dem der Anfang inklusive und das Ende exklusiv ist . Ein Tag beginnt also mit seinem ersten Moment und läuft bis zum ersten Moment des nächsten Tages, schließt ihn jedoch nicht ein.
ZonedDateTime zdtStop = ld.plusDays( 1 ).atStartOfDay( z ) ; // Determine the following date, and ask for the first moment of that day.
Unsere Abfrage für einen ganzen Tag kann keinen SQL-Befehl verwenden BETWEEN
. Dieser Befehl ist Fully-Closed ( []
) (Anfang und Ende sind inklusive), wo wir Half-Open ( [)
) wollen. Wir verwenden zwei Kriterien >=
und<
.
Ihre Spalte ist schlecht benannt. Vermeiden Sie die tausend Wörter, die von verschiedenen Datenbanken reserviert wurden. Verwenden wir placed
in diesem Beispiel.
Ihr Code sollte ?
Platzhalter verwendet haben , um unsere Momente anzugeben.
String sql = "SELECT * FROM orders WHERE placed >= ? AND placed < ? ; " ;
Wir haben jedoch ZonedDateTime
Objekte in der Hand, während Ihre Datenbank anscheinend Ganzzahlen speichert, wie oben beschrieben. Wenn Sie Ihre Spalte richtig definiert hätten , könnten wir die ZonedDateTime
Objekte einfach mit jedem JDBC-Treiber übergeben, der JDBC 4.2 oder höher unterstützt.
Stattdessen müssen wir in ganzen Sekunden eine Zählung aus der Epoche erhalten. Ich gehe davon aus, dass Ihr Epochenreferenzdatum der erste Moment von 1970 in UTC ist. Achten Sie auf mögliche Datenverluste, da die ZonedDateTime
Klasse eine Auflösung von Nanosekunden erreichen kann. Jede gebrochene Sekunde wird in den folgenden Zeilen abgeschnitten.
long start = zdtStart().toEpochSecond() ; // Transform to a count of whole seconds since 1970-01-01T00:00:00Z.
long stop = zdtStop().toEpochSecond() ;
Jetzt können wir diese Ganzzahlen an unseren oben definierten SQL-Code übergeben.
PreparedStatement ps = con.prepareStatement( sql );
ps.setObject( 1 , start ) ;
ps.setObject( 2 , stop ) ;
ResultSet rs = ps.executeQuery();
Wenn Sie Ihre ganzzahligen Werte von abrufen ResultSet
, können Sie sie in Instant
Objekte (immer in UTC) oder in ZonedDateTime
Objekte (mit einer zugewiesenen Zeitzone) umwandeln .
Instant instant = rs.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Über java.time
Das java.time- Framework ist in Java 8 und höher integriert. Diese Klassen verdrängen die lästigen alten Legacy - Datum-Zeit - Klassen wie java.util.Date
, Calendar
, & SimpleDateFormat
.
Das Joda-Time- Projekt, das sich jetzt im Wartungsmodus befindet , empfiehlt die Migration auf java.time Klassen .
Weitere Informationen finden Sie im Oracle-Lernprogramm . Suchen Sie im Stapelüberlauf nach vielen Beispielen und Erklärungen. Die Spezifikation ist JSR 310 .
Woher bekomme ich die java.time-Klassen?
Das ThreeTen-Extra- Projekt erweitert java.time um zusätzliche Klassen. Dieses Projekt ist ein Testfeld für mögliche zukünftige Ergänzungen von java.time. Sie können einige nützliche Klassen hier wie finden Interval
, YearWeek
, YearQuarter
, und mehr .