การแยกสตริงที่มีวันที่และเวลาเป็นช่วงเวลาใดเวลาหนึ่ง (Java เรียกว่า " Instant
") ค่อนข้างซับซ้อน Java ได้แก้ปัญหานี้ในการทำซ้ำหลาย ๆ ล่าสุดjava.time
และjava.time.chrono
ครอบคลุมเกือบทุกความต้องการ (ยกเว้นการขยายเวลา) :))
อย่างไรก็ตามความซับซ้อนนั้นทำให้เกิดความสับสนมากมาย
กุญแจสำคัญในการทำความเข้าใจการแยกวันที่คือ:
ทำไม Java มีหลายวิธีในการวิเคราะห์วันที่
- มีหลายระบบในการวัดเวลา ยกตัวอย่างเช่นปฏิทินประวัติศาสตร์ของญี่ปุ่นได้รับมาจากช่วงเวลาของรัชสมัยของจักรพรรดิหรือราชวงศ์ จากนั้นก็มีเช่นการประทับเวลา UNIX โชคดีที่โลกทั้งโลก (ธุรกิจ) สามารถใช้งานได้เหมือนกัน
- ในอดีตระบบถูกเปลี่ยนจาก / เป็นด้วยเหตุผลต่างๆเหตุผลต่างๆเช่นจากปฏิทิน Julian ถึงปฏิทิน Gregorian ในปี 1582 ดังนั้นวันที่ 'ตะวันตก' ก่อนหน้านั้นจะต้องได้รับการปฏิบัติแตกต่างกัน
- และแน่นอนการเปลี่ยนแปลงไม่ได้เกิดขึ้นพร้อมกัน เนื่องจากปฏิทินดังกล่าวมาจากสำนักงานใหญ่ของศาสนาและส่วนอื่น ๆ ของยุโรปที่เชื่อในการควบคุมอาหารอื่น ๆ เช่นเยอรมนีไม่ได้เปลี่ยนจนกระทั่งปี 1700
... และทำไมจึงเป็นLocalDateTime
เช่นZonedDateTime
นั้น ซับซ้อนมาก
มีโซนเวลา เขตเวลานั้นโดยทั่วไปจะเป็น "แถบ" * [1]ของพื้นผิวโลกซึ่งเจ้าหน้าที่ปฏิบัติตามกฎเดียวกันว่าจะมีการชดเชยเวลาเมื่อใด ซึ่งรวมถึงกฎเวลาฤดูร้อน
เขตเวลาเปลี่ยนแปลงไปตามกาลเวลาสำหรับพื้นที่ต่างๆโดยส่วนใหญ่ขึ้นอยู่กับว่าใครจะเป็นใคร และกฎของเขตเวลาหนึ่งก็เปลี่ยนไปตามกาลเวลาเช่นกัน
มีการชดเชยเวลา ไม่เหมือนกับเขตเวลาเนื่องจากเขตเวลาอาจเป็นเช่น "ปราก" แต่มีการชดเชยเวลาฤดูร้อนและชดเชยเวลาฤดูหนาว
หากคุณได้รับการประทับเวลาที่มีเขตเวลาการชดเชยอาจแตกต่างกันไปขึ้นอยู่กับส่วนของปีที่อยู่ในช่วงเวลากระโดดชั่วโมงการประทับเวลาอาจหมายถึง 2 ครั้งที่แตกต่างกันดังนั้นจึงไม่มีข้อมูลเพิ่มเติม แปลง
หมายเหตุ: ตามการประทับเวลาฉันหมายถึง "สตริงที่มีวันที่และ / หรือเวลาเลือกที่จะมีเขตเวลาและ / หรือเวลาชดเชย"
หลายเขตเวลาอาจใช้เวลาเดียวกันร่วมกันในบางช่วงเวลา ตัวอย่างเช่นเขตเวลา GMT / UTC เหมือนกับเขตเวลา "ลอนดอน" เมื่อการชดเชยเวลาฤดูร้อนไม่มีผล
หากต้องการทำให้ซับซ้อนขึ้นเล็กน้อย (แต่นั่นไม่สำคัญสำหรับกรณีการใช้งานของคุณ):
- นักวิทยาศาสตร์สังเกตการเปลี่ยนแปลงของโลกซึ่งเปลี่ยนแปลงตลอดเวลา ขึ้นอยู่กับว่าพวกเขาเพิ่มวินาทีในตอนท้ายของแต่ละปี (ดังนั้น
2040-12-31 24:00:00
อาจเป็นวันที่และเวลาที่ถูกต้อง) สิ่งนี้ต้องการการปรับปรุงข้อมูลเมตาเป็นประจำที่ระบบใช้เพื่อให้การแปลงวันที่ถูกต้อง เช่นบน Linux คุณจะได้รับการอัปเดตเป็นประจำสำหรับแพ็คเกจ Java รวมถึงข้อมูลใหม่เหล่านี้
การปรับปรุงไม่ได้เก็บพฤติกรรมก่อนหน้านี้ไว้เสมอสำหรับทั้งการบันทึกเวลาในอดีตและในอนาคต ดังนั้นอาจเกิดขึ้นได้เมื่อวิเคราะห์การประทับเวลาสองครั้งรอบ ๆ การเปลี่ยนแปลงของเขตเวลาเมื่อเปรียบเทียบกับการเปลี่ยนแปลงเหล่านั้นอาจให้ผลลัพธ์ที่แตกต่างกันเมื่อทำงานกับซอฟต์แวร์รุ่นต่าง ๆ ซึ่งยังใช้กับการเปรียบเทียบระหว่างเขตเวลาที่ได้รับผลกระทบกับเขตเวลาอื่น
ควรเหตุนี้ข้อผิดพลาดในซอฟต์แวร์ของคุณให้พิจารณาใช้การประทับเวลาบางส่วนที่ไม่ได้มีกฎระเบียบที่ซับซ้อนดังกล่าวเช่นUNIX ประทับเวลา
เนื่องจาก 7 สำหรับวันที่ในอนาคตเราไม่สามารถแปลงวันที่อย่างแน่นอนด้วยความแน่นอน ตัวอย่างเช่นการวิเคราะห์คำในปัจจุบัน8524-02-17 12:00:00
อาจไม่กี่วินาทีจากการวิเคราะห์คำในอนาคต
API ของ JDK สำหรับสิ่งนี้ได้รับการพัฒนาตามความต้องการร่วมสมัย
- Java รุ่นแรกนั้น
java.util.Date
มีวิธีการที่ไร้เดียงสาเพียงเล็กน้อยโดยสมมติว่ามีเพียงปีเดือนวันและเวลา เรื่องนี้ไม่เร็วพอ
- นอกจากนี้ความต้องการของฐานข้อมูลก็มีความแตกต่างกันดังนั้นจึงมีการเริ่มใช้งานเร็วมาก
java.sql.Date
ด้วยข้อ จำกัด ของตัวเอง
- เนื่องจากไม่ครอบคลุมทั้งปฏิทินและเขตเวลาที่แตกต่างกัน
Calendar
จึงมีการแนะนำ API
- สิ่งนี้ยังไม่ครอบคลุมความซับซ้อนของเขตเวลา และถึงกระนั้นการผสมผสานของ API ข้างต้นก็เป็นความเจ็บปวดที่จะทำงานด้วย ดังนั้นเมื่อนักพัฒนา Java เริ่มทำงานกับเว็บแอพพลิเคชั่นทั่วโลกห้องสมุดที่ตั้งเป้าหมายการใช้งานส่วนใหญ่เช่น JodaTime จะได้รับความนิยมอย่างรวดเร็ว JodaTime เป็นมาตรฐานจริงประมาณสิบปี
- แต่ JDK ไม่ได้รวมกับ JodaTime ดังนั้นการทำงานกับมันจึงค่อนข้างยุ่งยาก ดังนั้นหลังจากการพูดคุยกันนานมากเกี่ยวกับวิธีการเข้าถึงเรื่องนี้JSR-310จึงถูกสร้างขึ้นตาม JodaTime เป็นหลัก
วิธีจัดการกับมันในภาษาจาวา java.time
กำหนดประเภทที่จะแยกวิเคราะห์เวลา
เมื่อคุณใช้สตริงการประทับเวลาคุณจำเป็นต้องทราบข้อมูลที่มีอยู่ นี่คือจุดสำคัญ หากคุณไม่ได้รับสิทธินี้คุณจะพบกับข้อยกเว้นที่เป็นความลับเช่น "ไม่สามารถสร้างได้ทันที" หรือ "โซนออฟเซ็ตหายไป" หรือ "รหัสโซนที่ไม่รู้จัก" เป็นต้น
มันมีวันที่และเวลาหรือไม่?
มันมีเวลาชดเชยหรือไม่?
การชดเชยเวลาเป็น+hh:mm
ส่วนหนึ่ง บางครั้ง+00:00
อาจถูกแทนที่ด้วยZ
'เวลาซูลู' UTC
ตามเวลาสากลเชิงพิกัดหรือGMT
เวลามาตรฐานกรีนิช สิ่งเหล่านี้ยังตั้งค่าเขตเวลา สำหรับการประทับเวลาเหล่านี้คุณใช้
OffsetDateTime
มีเขตเวลาหรือไม่? สำหรับการประทับเวลาเหล่านี้คุณใช้
โซนจะถูกระบุด้วยZonedDateTime
- ชื่อ ("ปราก", "เวลามาตรฐานแปซิฟิก", "PST") หรือ
- "เขต ID" ( "อเมริกา / Los_Angeles", "ยุโรป / ลอนดอน") แสดงโดยjava.time.ZoneId
รายการของเขตเวลาจะถูกรวบรวมโดย"ฐานข้อมูล TZ"ซึ่งได้รับการสนับสนุนโดย ICAAN
ตามZoneId
javadoc ของ id โซนยังสามารถระบุเป็นZ
และชดเชย ฉันไม่แน่ใจว่าแผนที่นี้เป็นโซนจริงได้อย่างไร หากการประทับเวลาซึ่งมีเพียง TZ เท่านั้นที่ตกอยู่ในการเปลี่ยนแปลงออฟเซ็ตของการกระโดดข้ามเวลาแสดงว่ามันไม่ชัดเจนและการตีความนั้นเป็นเรื่องของResolverStyle
ดูด้านล่าง
ถ้ามันไม่มีทั้งบริบทที่หายไปจะถือว่าหรือละเลย และผู้บริโภคจะต้องตัดสินใจ ดังนั้นจึงต้องมีการแยกวิเคราะห์LocalDateTime
และแปลงเป็นOffsetDateTime
โดยการเพิ่มข้อมูลที่ขาดหายไป:
- คุณสามารถสันนิษฐานได้ว่ามันเป็นเวลา UTC เพิ่ม UTC ออฟเซ็ต 0 ชั่วโมง
- คุณสามารถสันนิษฐานได้ว่ามันเป็นช่วงเวลาของสถานที่ที่เกิดการแปลงเกิดขึ้น แปลงโดยเพิ่มเขตเวลาของระบบ
- คุณสามารถละเลยและใช้มันได้ตามที่เป็นอยู่ สิ่งนี้มีประโยชน์เช่นการเปรียบเทียบหรือย่อส่วนสองครั้ง (ดู
Duration
) หรือเมื่อคุณไม่รู้และไม่สำคัญ (เช่นตารางรถบัสท้องถิ่น)
ข้อมูลเวลาบางส่วน
- จากสิ่งที่ประทับเวลามีคุณสามารถใช้
LocalDate
, LocalTime
, OffsetTime
, MonthDay
, Year
หรือYearMonth
ออกจากมัน
หากคุณมีข้อมูลที่ครบถ้วน, java.time.Instant
คุณจะได้รับ นี้จะใช้ภายในการแปลงระหว่างและOffsetDateTime
ZonedDateTime
คิดวิธีการแยกมัน
มีเอกสารมากมายDateTimeFormatter
ที่สามารถแยกสตริงเวลาประทับและรูปแบบเป็นสตริงได้
ก่อนสร้างDateTimeFormatter
sควรจะครอบคลุมเพิ่มเติมน้อยลงทุกรูปแบบการประทับเวลามาตรฐาน ยกตัวอย่างเช่นสามารถแยกISO_INSTANT
2011-12-03T10:15:30.123457Z
หากคุณมีรูปแบบพิเศษบางอย่างคุณสามารถสร้าง DateTimeFormatter ของคุณเอง (ซึ่งเป็นตัวแยกวิเคราะห์)
private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
.toFormatter();
ผมขอแนะนำให้ไปดูที่รหัสที่มาของและได้รับแรงบันดาลใจในการสร้างโดยใช้DateTimeFormatter
DateTimeFormatterBuilder
ในขณะที่คุณอยู่ที่นั่นให้ดูที่ResolverStyle
การควบคุมว่าตัวแยกวิเคราะห์เป็น LENIENT, SMART หรือ STRICT สำหรับรูปแบบและข้อมูลที่คลุมเครือ
TemporalAccessor
TemporalAccessor
ตอนนี้ความผิดพลาดที่พบบ่อยคือเข้าไปในความซับซ้อนของ SimpleDateFormatter.parse(String)
นี้มาจากวิธีการที่นักพัฒนาที่ถูกนำมาใช้ในการทำงานกับ ขวาช่วยให้คุณDateTimeFormatter.parse("...")
TemporalAccessor
// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");
แต่พร้อมกับความรู้จากส่วนก่อนหน้าคุณสามารถแยกประเภทที่คุณต้องการ:
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z", TIMESTAMP_PARSER);
คุณไม่จำเป็นต้องจริงไปDateTimeFormatter
อย่างใดอย่างหนึ่ง ประเภทที่คุณต้องการแยกวิเคราะห์มีparse(String)
วิธีการ
OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z");
เกี่ยวกับTemporalAccessor
คุณสามารถใช้งานได้หากคุณมีความคิดที่คลุมเครือเกี่ยวกับข้อมูลที่มีอยู่ในสตริงและต้องการตัดสินใจใช้งานจริง
ฉันหวังว่าฉันจะเข้าใจถึงความรู้สึกของคุณ :)
หมายเหตุ: มีการย้ายกลับของเป็นjava.time
: ชวา 6 และ 7 ThreeTen-ย้ายกลับ สำหรับ Android มันมีThreeTenABP
[1]ไม่ใช่แค่ว่าพวกเขาไม่ใช่ลายทาง แต่ยังมีความสุดขั้วอีกด้วย ตัวอย่างเช่นบางเกาะแปซิฟิกที่อยู่ใกล้เคียงมีเขตเวลา +14: 00 และ -11: 00 หมายความว่าขณะที่อยู่บนเกาะหนึ่งมี 1 พฤษภาคม 15:00 บนเกาะอื่นไม่ไกลมันยังคงเป็น 30 เมษายน 12 น. (หากฉันนับถูกต้อง :)
ZonedDateTime
LocalDateTime
ชื่อนี้ใช้ง่ายLocal
หมายใด ๆท้องที่ทั่วไปมากกว่าโซนเวลาที่เฉพาะเจาะจง ดังนั้นLocalDateTime
วัตถุจะไม่ผูกติดกับเส้นเวลา เพื่อให้มีความหมายในการรับช่วงเวลาที่ระบุในเส้นเวลาคุณต้องใช้เขตเวลา