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
คุณกำลังใช้คลาสวันที่และเวลาเก่าที่มีปัญหาซึ่งตอนนี้เป็นแบบดั้งเดิมแทนที่ด้วยคลาสjava.time ที่ทันสมัย
เห็นได้ชัดว่าคุณกำลังจัดเก็บช่วงเวลาหนึ่งในฐานข้อมูลของคุณในคอลัมน์ประเภทจำนวนเต็ม นั่นเป็นเรื่องโชคร้าย คุณควรใช้คอลัมน์ประเภทเช่น SQL-standard 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
วัตถุทั่ว codebase ของคุณแทนที่จะเป็นจำนวนเต็มเพียงที่จะทำให้รหัสของคุณเองมากขึ้นการจัดเก็บเอกสารให้ตรวจสอบค่าที่ถูกต้องและให้ประเภทความปลอดภัย
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
เมื่อLocalDate
อยู่ในมือเราต้องเปลี่ยนสิ่งนั้นให้เป็นช่วงเวลาหนึ่งจุดเริ่มต้นและจุดหยุดของวัน อย่าถือว่าวันเริ่มเวลา 00: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
ได้ คำสั่งนั้นคือ Fully-Closed ( []
) (รวมทั้งจุดเริ่มต้นและจุดสิ้นสุด) โดยที่เราต้องการ Half-Open ( [)
) เราใช้คู่ของเกณฑ์>=
และ<
.
คอลัมน์ของคุณตั้งชื่อไม่ดี หลีกเลี่ยงพันคำที่สงวนไว้โดยฐานข้อมูลต่างๆ มาใช้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
ต้องการเรียนรู้เพิ่มเติมโปรดดูที่ออราเคิลกวดวิชา และค้นหา Stack Overflow สำหรับตัวอย่างและคำอธิบายมากมาย สเปกJSR 310
จะหาคลาส java.time ได้ที่ไหน?
- Java SE 8 , Java SE 9และใหม่กว่า
- ในตัว.
- ส่วนหนึ่งของ Java API มาตรฐานพร้อมการใช้งานแบบรวม
- Java 9 เพิ่มคุณสมบัติและการแก้ไขเล็กน้อย
- Java SE 6และ Java SE 7
- มากของการทำงาน java.time จะกลับรังเพลิง Java 6 และ 7 ในThreeTen-ย้ายกลับ
- Android
- การติดตั้งบันเดิล Android เวอร์ชันที่ใหม่กว่าของคลาส java.time
- สำหรับก่อนหน้านี้ของ Android ที่ThreeTenABPโครงการปรับThreeTen-ย้ายกลับ (ดังกล่าวข้างต้น) ดูวิธีใช้ ThreeTenABP … .
โครงการThreeTen-Extraขยาย java.time ด้วยคลาสเพิ่มเติม โปรเจ็กต์นี้เป็นพื้นที่พิสูจน์สำหรับการเพิ่ม java.time ในอนาคต คุณอาจพบว่าการเรียนที่มีประโยชน์บางอย่างที่นี่เช่นInterval
, YearWeek
, YearQuarter
และอื่น ๆ อีกมากมาย