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
Bạn đang sử dụng các lớp date-time cũ rắc rối mà bây giờ là kế thừa, được thay thế bởi các lớp java.time hiện đại .
Rõ ràng bạn đang lưu trữ một khoảnh khắc trong cơ sở dữ liệu của mình trong một cột của một số kiểu số nguyên. Đó là điều không may. Thay vào đó, bạn nên sử dụng một cột có kiểu như SQL-standard TIMESTAMP WITH TIME ZONE
. Nhưng, đối với Câu trả lời này, chúng tôi sẽ làm việc với những gì chúng tôi có.
Nếu bản ghi của bạn đại diện cho những khoảnh khắc với độ phân giải mili giây và bạn muốn tất cả bản ghi cho cả ngày, thì chúng tôi cần một khoảng thời gian. Chúng ta phải có thời điểm bắt đầu và thời điểm dừng lại. Truy vấn của bạn chỉ có một tiêu chí ngày-giờ duy nhất mà nó đáng lẽ phải có một cặp.
Các LocalDate
lớp đại diện cho một giá trị ngày tháng chỉ mà không cần thời gian của ngày và không có múi giờ.
Múi giờ rất quan trọng trong việc xác định ngày. Đối với bất kỳ thời điểm nhất định nào, ngày thay đổi trên toàn cầu theo khu vực. Ví dụ: một vài phút sau nửa đêm ở Paris Pháp là một ngày mới trong khi vẫn là “ngày hôm qua” ở Montréal Québec .
Nếu không có múi giờ nào được chỉ định, JVM sẽ áp dụng hoàn toàn múi giờ mặc định hiện tại của nó. Mặc định đó có thể thay đổi bất kỳ lúc nào, vì vậy kết quả của bạn có thể thay đổi. Tốt hơn là chỉ định múi giờ mong muốn / dự kiến của bạn một cách rõ ràng làm đối số.
Chỉ định một tên múi giờ thích hợp trong các định dạng của continent/region
, chẳng hạn như America/Montreal
, Africa/Casablanca
hoặc Pacific/Auckland
. Không bao giờ sử dụng từ viết tắt 3-4 chữ cái, chẳng hạn như EST
hoặc IST
vì chúng không phải là múi giờ thực, không được tiêu chuẩn hóa và thậm chí không phải là duy nhất (!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Nếu bạn muốn sử dụng múi giờ mặc định hiện tại của JVM, hãy yêu cầu múi giờ đó và chuyển làm đối số. Nếu bị bỏ qua, mặc định hiện tại của JVM được áp dụng ngầm. Tốt hơn là rõ ràng, vì mặc định có thể được thay đổi bất kỳ lúc nào trong thời gian chạy bằng bất kỳ mã nào trong bất kỳ chuỗi nào của bất kỳ ứng dụng nào trong JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Hoặc chỉ định một ngày. Bạn có thể đặt tháng bằng một số, với việc đánh số lành mạnh từ 1-12 cho tháng 1 đến tháng 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.
Hoặc, tốt hơn, sử dụng các Month
đối tượng enum được xác định trước, một đối tượng cho mỗi tháng trong năm. Mẹo: Sử dụng các Month
đối tượng này trong toàn bộ cơ sở mã của bạn thay vì một số nguyên đơn thuần để làm cho mã của bạn tự ghi lại nhiều hơn, đảm bảo các giá trị hợp lệ và cung cấp an toàn kiểu .
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
Có được LocalDate
trong tay, tiếp theo, chúng ta cần biến nó thành một cặp thời điểm, điểm bắt đầu và điểm dừng trong ngày. Đừng cho rằng ngày bắt đầu lúc 00:00:00 trong ngày. Do những điều bất thường như Giờ tiết kiệm ánh sáng ban ngày (DST), ngày có thể bắt đầu vào một thời điểm khác, chẳng hạn như 01:00:00. Vì vậy, hãy để java.time xác định thời điểm đầu tiên trong ngày. Chúng tôi chuyển một ZoneId
đối số LocalDate::atStartOfDay
để tìm kiếm bất kỳ sự bất thường nào như vậy. Kết quả là a ZonedDateTime
.
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;
Nói chung, cách tiếp cận tốt nhất để xác định khoảng thời gian là cách tiếp cận Nửa mở trong đó phần đầu là bao gồm trong khi phần kết là độc quyền . Vì vậy, một ngày bắt đầu với khoảnh khắc đầu tiên của nó và kéo dài đến, nhưng không bao gồm, khoảnh khắc đầu tiên của ngày hôm sau.
ZonedDateTime zdtStop = ld.plusDays( 1 ).atStartOfDay( z ) ; // Determine the following date, and ask for the first moment of that day.
Truy vấn của chúng tôi trong cả ngày không thể sử dụng lệnh SQL BETWEEN
. Lệnh đó là Full-Closed ( []
) (cả phần đầu và phần cuối đều bao gồm) ở nơi chúng ta muốn Half-Open ( [)
). Chúng tôi sử dụng một cặp tiêu chí >=
và <
.
Cột của bạn được đặt tên kém. Tránh bất kỳ từ nào trong số hàng nghìn từ được đặt trước bởi các cơ sở dữ liệu khác nhau. Hãy sử dụng placed
trong ví dụ này.
Mã của bạn nên đã sử dụng ?
trình giữ chỗ để chỉ định các khoảnh khắc của chúng ta.
String sql = "SELECT * FROM orders WHERE placed >= ? AND placed < ? ; " ;
Nhưng chúng tôi có ZonedDateTime
các đối tượng trong tay, trong khi cơ sở dữ liệu của bạn dường như đang lưu trữ các số nguyên như đã thảo luận ở trên. Nếu bạn đã xác định đúng cột của mình, chúng tôi có thể chỉ cần chuyển các ZonedDateTime
đối tượng bằng bất kỳ trình điều khiển JDBC nào hỗ trợ JDBC 4.2 trở lên.
Nhưng thay vào đó, chúng ta cần đếm từ kỷ nguyên trong cả giây. Tôi sẽ giả sử ngày tham chiếu kỷ nguyên của bạn là thời điểm đầu tiên của năm 1970 theo giờ UTC. Cẩn thận với khả năng mất dữ liệu, vì ZonedDateTime
lớp này có khả năng phân giải nano giây. Bất kỳ giây phân số nào sẽ được cắt ngắn trong các dòng sau.
long start = zdtStart().toEpochSecond() ; // Transform to a count of whole seconds since 1970-01-01T00:00:00Z.
long stop = zdtStop().toEpochSecond() ;
Bây giờ chúng ta đã sẵn sàng chuyển các số nguyên đó sang mã SQL của chúng ta được định nghĩa ở trên.
PreparedStatement ps = con.prepareStatement( sql );
ps.setObject( 1 , start ) ;
ps.setObject( 2 , stop ) ;
ResultSet rs = ps.executeQuery();
Khi bạn truy xuất các giá trị số nguyên của mình từ ResultSet
, bạn có thể chuyển đổi thành Instant
các đối tượng (luôn ở UTC) hoặc thành ZonedDateTime
các đối tượng (với múi giờ được chỉ định).
Instant instant = rs.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Giới thiệu về java.time
Các java.time khung được xây dựng vào Java 8 và sau đó. Những lớp học thay thế cái cũ phiền hà di sản lớp học ngày thời gian như java.util.Date
, Calendar
, & SimpleDateFormat
.
Các Joda thời gian dự án, bây giờ trong chế độ bảo trì , khuyên chuyển đổi sang các java.time lớp.
Để tìm hiểu thêm, hãy xem Hướng dẫn Oracle . Và tìm kiếm Stack Overflow để có nhiều ví dụ và giải thích. Đặc điểm kỹ thuật là JSR 310 .
Lấy các lớp java.time ở đâu?
Các ThreeTen-Extra dự án mở rộng java.time với các lớp bổ sung. Dự án này là cơ sở chứng minh cho những bổ sung có thể có trong tương lai cho java.time. Bạn có thể tìm thấy một số các lớp học hữu ích ở đây chẳng hạn như Interval
, YearWeek
, YearQuarter
, và nhiều hơn nữa .