тл; д-р
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
Ви користуєтеся клопітними старими класами дати, які тепер застаріли, замінені сучасним java.time класи класами .
Мабуть, ви зберігаєте момент у своїй базі даних у стовпці якогось цілого типу. Це прикро. Натомість слід використовувати стовпець такого типу, як стандарт SQL TIMESTAMP WITH TIME ZONE
. Але для цього відповіді ми будемо працювати з тим, що маємо.
Якщо ваші записи представляють моменти з роздільною здатністю мілісекунд, і ви хочете, щоб усі записи були за цілий день, тоді нам потрібен часовий діапазон. У нас повинен бути момент початку та зупинки. У вашому запиті є лише один критерій дати та часу, де він мав би мати пару.
LocalDate
Клас являє собою дату тільки значення без часу доби і без часового поясу.
Часовий пояс має вирішальне значення при визначенні дати. Для будь-якого моменту дата змінюється по всьому світу за зоною. Наприклад, через кілька хвилин після півночі у Парижі Франція - це новий день, а ще «вчора» в Монреалі Квебек .
Якщо часовий пояс не вказаний, JVM неявно застосовує поточний часовий пояс за замовчуванням. Цей дефолт може змінитися в будь-який момент, тому результати можуть відрізнятися. Краще вказати бажаний / очікуваний часовий пояс як аргумент.
Вкажіть правильний час ім'я зони в форматі continent/region
, наприклад America/Montreal
, Africa/Casablanca
або Pacific/Auckland
. Ніколи не використовуйте абревіатуру з 3–4 літер, наприклад, EST
або IST
як вони не є справжніми часовими поясами, не стандартизовані та навіть не унікальні (!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Якщо ви хочете використовувати поточний часовий пояс JVM за замовчуванням, запитайте його та передайте як аргумент. Якщо пропущено, поточний дефолт JVM застосовується неявно. Краще бути явним, оскільки за замовчуванням може бути змінено будь-який момент під час виконання будь-яким кодом у будь-якому потоці будь-якої програми в рамках JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Або вкажіть дату. Ви можете встановити місяць за числом, причому для січня-грудня число "розумне" буде 1-12.
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.
Або, краще, використовуйте Month
заздалегідь визначені об’єкти enum, по одному на кожен місяць року. Порада: Використовуйте ці Month
об'єкти у всій своїй кодовій базі, а не просто ціле число, щоб зробити свій код більш самодокументованим, забезпечити дійсні значення та забезпечити безпеку типу .
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
З LocalDate
рукою нам далі потрібно перетворити це на пару моментів, початку та зупинки дня. Не припускайте, що день починається о 00:00 години часу. Через аномалії, такі як літній час (DST), день може розпочатися в інший час, наприклад, 01:00:00. Тож нехай java.time визначає перший момент дня. Ми передаємо ZoneId
аргумент, LocalDate::atStartOfDay
щоб шукати будь-які подібні аномалії. Результат - а ZonedDateTime
.
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;
Як правило, найкращим підходом до визначення проміжку часу є підхід "Напіврозкритий", де початок є інклюзивним, а закінчення є виключним . Отже день починається з першого моменту і закінчується, але не включаючи першого моменту наступного дня.
ZonedDateTime zdtStop = ld.plusDays( 1 ).atStartOfDay( z ) ; // Determine the following date, and ask for the first moment of that day.
Наш запит протягом цілого дня не може використовувати команду SQL BETWEEN
. Ця команда є повністю заповненою ( []
) (починаючи з кінця і закінчуючи), де ми хочемо напіввідкрити ( [)
). Ми використовуємо пару критеріїв >=
і<
.
Ваш стовпець погано названий. Уникайте будь-якого з тисяч слів, зарезервованих різними базами даних. Давайте скористаємося placed
в цьому прикладі.
У вашому коді повинні бути використані ?
заповнювачі, в яких можна вказати наші моменти.
String sql = "SELECT * FROM orders WHERE placed >= ? AND placed < ? ; " ;
Але у нас є ZonedDateTime
об’єкти в руці, тоді як ваша база даних, очевидно, зберігає цілі числа, як обговорювалося вище. Якщо ви вже визначили свій стовпець правильно , ми могли б просто передатиZonedDateTime
об'єкти з будь-яким драйвером JDBC з підтримкою JDBC 4.2 або новішої версії.
Але замість цього нам потрібно отримати цілі секунди відлік епохи. Я припускаю, що ваша дата відліку епохи - це перший момент 1970 року в UTC. Остерігайтеся можливих втрат даних, оскільки ZonedDateTime
клас здатний до наносекундної роздільної здатності. Будь-яка дробова секунда буде усічена в наступних рядках.
long start = zdtStart().toEpochSecond() ; // Transform to a count of whole seconds since 1970-01-01T00:00:00Z.
long stop = zdtStop().toEpochSecond() ;
Тепер ми готові передати ці цілі числа до нашого SQL-коду, визначеного вище.
PreparedStatement ps = con.prepareStatement( sql );
ps.setObject( 1 , start ) ;
ps.setObject( 2 , stop ) ;
ResultSet rs = ps.executeQuery();
Коли ви отримуєте цілі цілі значення з ResultSet
, ви можете перетворитись на Instant
об'єкти (завжди в UTC) або в ZonedDateTime
об'єкти (з призначеним часовим поясом).
Instant instant = rs.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Про java.time
Java.time каркас вбудований в Java 8 і пізніших версій. Ці класи витісняти неприємні старі застарілі класи дати і часу , такі як java.util.Date
, Calendar
, іSimpleDateFormat
.
Проект Joda-Time , який зараз знаходиться в режимі обслуговування , радить перейти на java.time класів .
Щоб дізнатися більше, дивіться навчальний посібник Oracle . І шукайте переповнення стека за багатьма прикладами та поясненнями. Специфікація - JSR 310 .
Де отримати класи java.time?
Проект ThreeTen-Extra розширює java.time додатковими класами. Цей проект є передумовою для можливих майбутніх доповнень до java.time. Ви можете знайти деякі корисні класи тут , такі як Interval
, YearWeek
, YearQuarter
, і більш .