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
Używasz kłopotliwych starych klas daty i czasu, które są teraz starsze, wyparte przez nowoczesne klasy java.time .
Najwyraźniej przechowujesz moment w swojej bazie danych w kolumnie pewnego typu całkowitego. To jest niefortunne. Zamiast tego powinieneś używać kolumny typu, takiego jak standard SQL TIMESTAMP WITH TIME ZONE
. Ale w przypadku tej odpowiedzi będziemy pracować z tym, co mamy.
Jeśli Twoje rekordy przedstawiają momenty z rozdzielczością milisekund i chcesz, aby wszystkie rekordy obejmowały cały dzień, potrzebujemy przedziału czasu. Musimy mieć moment rozpoczęcia i moment zakończenia. Twoje zapytanie ma tylko jedno kryterium daty i czasu, w którym powinno mieć parę.
LocalDate
Klasa reprezentuje wartość data-tylko bez time-of-day i bez strefy czasowej.
Strefa czasowa ma kluczowe znaczenie przy ustalaniu daty. W dowolnym momencie data różni się na całym świecie według strefy. Na przykład, kilka minut po północy w Paryżu Francja jest nowym dniem, podczas gdy w Montrealu Quebec wciąż trwa „wczoraj” .
Jeśli nie określono strefy czasowej, maszyna JVM niejawnie stosuje swoją bieżącą domyślną strefę czasową. To ustawienie domyślne może się zmienić w dowolnym momencie, więc wyniki mogą się różnić. Lepiej jest jawnie określić żądaną / oczekiwaną strefę czasową jako argument.
Określ prawidłową nazwę strefy czasowej w formacie continent/region
, takie jak America/Montreal
, Africa/Casablanca
lub Pacific/Auckland
. Nigdy nie używaj 3-4-literowego skrótu, takiego jak EST
lub, IST
ponieważ nie są to prawdziwe strefy czasowe, nie znormalizowane, a nawet nie unikalne (!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Jeśli chcesz użyć bieżącej domyślnej strefy czasowej maszyny JVM, poproś o to i podaj jako argument. W przypadku pominięcia bieżąca wartość domyślna maszyny JVM jest stosowana niejawnie. Lepiej, aby było to jawne, ponieważ wartość domyślna może zostać zmieniona w dowolnym momencie podczas działania przez dowolny kod w dowolnym wątku dowolnej aplikacji w JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Lub podaj datę. Możesz ustawić miesiąc za pomocą liczby, z rozsądną numeracją 1-12 dla stycznia-grudnia.
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.
Lub, lepiej, użyj Month
wstępnie zdefiniowanych obiektów wyliczeniowych, po jednym dla każdego miesiąca w roku. Porada: Użyj tych Month
obiektów w całej bazie kodu, a nie zwykłej liczby całkowitej, aby kod był bardziej samodokumentujący, zapewniał prawidłowe wartości i zapewniał bezpieczeństwo typów .
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
Mając LocalDate
w ręku, musimy następnie przekształcić to w parę chwil, początek i koniec dnia. Nie zakładaj, że dzień zaczyna się o godzinie 00:00:00. Z powodu anomalii, takich jak czas letni (DST), dzień może rozpocząć się o innej godzinie, na przykład o godzinie 01:00:00. Niech java.time określi pierwszą porę dnia. Przekazujemy ZoneId
argument LocalDate::atStartOfDay
do wyszukania takich anomalii. Wynik to ZonedDateTime
.
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;
Ogólnie najlepszym podejściem do definiowania przedziału czasu jest podejście Half-Open, w którym początek jest włączający, a koniec wyłączny . Tak więc dzień zaczyna się od swojej pierwszej chwili i kończy się, ale nie wliczając, pierwszej chwili następnego dnia.
ZonedDateTime zdtStop = ld.plusDays( 1 ).atStartOfDay( z ) ; // Determine the following date, and ask for the first moment of that day.
Nasze zapytanie przez cały dzień nie może korzystać z polecenia SQL BETWEEN
. To polecenie to Fully-Closed ( []
) (zarówno początek, jak i koniec są włącznie), gdzie tak, jak chcemy, Half-Open ( [)
). Używamy pary kryteriów >=
i <
.
Twoja kolumna ma złą nazwę. Unikaj tysiąca słów zarezerwowanych przez różne bazy danych. Użyjmy placed
w tym przykładzie.
Twój kod powinien zawierać ?
symbole zastępcze, w których można określić nasze momenty.
String sql = "SELECT * FROM orders WHERE placed >= ? AND placed < ? ; " ;
Ale mamy ZonedDateTime
obiekty w ręku, podczas gdy twoja baza danych najwyraźniej przechowuje liczby całkowite, jak omówiono powyżej. Jeśli nie zdefiniowano swoją kolumnę właściwie moglibyśmy po prostu przekazać ZonedDateTime
obiektów z dowolnego sterownika JDBC nośnej JDBC 4.2 lub nowszej.
Ale zamiast tego potrzebujemy odliczać od epoki w pełnych sekundach. Zakładam, że datą odniesienia dla twojej epoki jest pierwsza chwila roku 1970 UTC. Uważaj na możliwą utratę danych, ponieważ ZonedDateTime
klasa ma rozdzielczość nanosekundową. Każda ułamkowa część sekundy zostanie obcięta w następnych wierszach.
long start = zdtStart().toEpochSecond() ; // Transform to a count of whole seconds since 1970-01-01T00:00:00Z.
long stop = zdtStop().toEpochSecond() ;
Teraz jesteśmy gotowi do przekazania tych liczb całkowitych do naszego kodu SQL zdefiniowanego powyżej.
PreparedStatement ps = con.prepareStatement( sql );
ps.setObject( 1 , start ) ;
ps.setObject( 2 , stop ) ;
ResultSet rs = ps.executeQuery();
Gdy pobierasz wartości całkowite z ResultSet
, możesz przekształcić je w Instant
obiekty (zawsze w UTC) lub w ZonedDateTime
obiekty (z przypisaną strefą czasową).
Instant instant = rs.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Informacje o java.time
Struktura java.time jest wbudowana w Javę 8 i nowsze. Klasy te kłopotliwe zastąpić stary starszych klas Date-Time, takich jak java.util.Date
, Calendar
, i SimpleDateFormat
.
Projekt Joda-Time , obecnie w trybie konserwacji , zaleca migrację do klas java.time .
Aby dowiedzieć się więcej, zobacz samouczek Oracle . I przeszukaj Stack Overflow, aby znaleźć wiele przykładów i wyjaśnień. Specyfikacja to JSR 310 .
Skąd wziąć klasy java.time?
- Java SE 8 , Java SE 9 i nowsze
- Wbudowany.
- Część standardowego interfejsu API języka Java z dołączoną implementacją.
- Java 9 dodaje kilka drobnych funkcji i poprawek.
- Java SE 6 i Java SE 7
- Wiele funkcji java.time jest przenoszonych z powrotem do Java 6 i 7 w ThreeTen-Backport .
- Android
- Późniejsze wersje implementacji klas java.time w pakietach Androida.
- Na wcześniejszym Androidzie ThreeTenABP projekt dostosowuje ThreeTen-backportu (wymienione powyżej). Zobacz Jak używać ThreeTenABP… .
Projekt ThreeTen-Extra rozszerza java.time o dodatkowe klasy. Ten projekt jest poligonem doświadczalnym dla ewentualnych przyszłych dodatków do java.time. Można znaleźć kilka przydatnych klas tutaj takie jak Interval
, YearWeek
, YearQuarter
, i więcej .