ในช่วงที่ผ่านมาประสบการณ์การเขียนล่าม JS ฉันปล้ำมากมายกับการทำงานภายในของวันที่ ECMA / JS ดังนั้นฉันคิดว่าฉันจะโยนใน 2 เซ็นต์ของฉันที่นี่ หวังว่าการแบ่งปันสิ่งนี้จะช่วยให้ผู้อื่นมีข้อสงสัยเกี่ยวกับความแตกต่างระหว่างเบราว์เซอร์ในการจัดการวันที่
ด้านการป้อนข้อมูล
การใช้งานทั้งหมดเก็บค่าวันที่ของพวกเขาภายในเป็นตัวเลข 64 บิตที่แสดงจำนวนมิลลิวินาที (ms) ตั้งแต่ 1970-01-01 UTC (GMT เป็นสิ่งเดียวกันกับ UTC) วันที่นี้เป็นยุค ECMAScript ที่ใช้โดยภาษาอื่นเช่นระบบ Java และ POSIX เช่น UNIX วันที่เกิดขึ้นหลังจากยุคเป็นจำนวนบวกและวันที่ก่อนหน้านี้เป็นค่าลบ
รหัสต่อไปนี้ถูกตีความว่าเป็นวันที่เดียวกันในเบราว์เซอร์ปัจจุบันทั้งหมด แต่มีออฟเซ็ตเขตเวลาท้องถิ่น:
Date.parse('1/1/1970'); // 1 January, 1970
ในเขตเวลาของฉัน (EST ซึ่งคือ -05: 00) ผลลัพธ์คือ 18000000 เพราะนั่นคือจำนวน ms ใน 5 ชั่วโมง (เพียง 4 ชั่วโมงในช่วงเวลาออมแสงกลางวัน) ค่าจะแตกต่างกันในเขตเวลาที่ต่างกัน พฤติกรรมนี้มีการระบุไว้ใน ECMA-262 ดังนั้นเบราว์เซอร์ทั้งหมดจึงทำเช่นเดียวกัน
ในขณะที่มีความแปรปรวนบางอย่างในรูปแบบของสตริงอินพุตที่เบราว์เซอร์หลักจะแยกวิเคราะห์เป็นวันที่พวกเขาจะตีความพวกเขาเป็นหลักเช่นเดียวกับเขตเวลาและการปรับเวลาตามฤดูกาลเป็นกังวลแม้ว่าการแยกวิเคราะห์จะขึ้นอยู่กับการนำไปใช้
อย่างไรก็ตามรูปแบบ ISO 8601 นั้นแตกต่างกัน เป็นหนึ่งในสองรูปแบบที่ระบุไว้ใน ECMAScript 2015 (ed 6) โดยเฉพาะที่ต้องแยกวิเคราะห์ในลักษณะเดียวกันโดยการใช้งานทั้งหมด (อีกรูปแบบหนึ่งคือรูปแบบที่ระบุสำหรับDate.prototype.toString )
แต่สำหรับสตริงการจัดรูปแบบ ISO 8601 การใช้งานบางอย่างทำให้เกิดความผิดพลาด นี่คือผลลัพธ์การเปรียบเทียบของ Chrome และ Firefox เมื่อคำตอบนี้ถูกเขียนขึ้นสำหรับ 1/1/1970 (ยุค) ในเครื่องของฉันโดยใช้สตริงการจัดรูปแบบ ISO 8601 ที่ควรแยกวิเคราะห์เป็นค่าเดียวกันในทุกการใช้งาน:
Date.parse('1970-01-01T00:00:00Z'); // Chrome: 0 FF: 0
Date.parse('1970-01-01T00:00:00-0500'); // Chrome: 18000000 FF: 18000000
Date.parse('1970-01-01T00:00:00'); // Chrome: 0 FF: 18000000
- ในกรณีแรกตัวระบุ "Z" บ่งชี้ว่าอินพุตอยู่ในเวลา UTC ดังนั้นไม่ได้ถูกชดเชยจากยุคและผลลัพธ์คือ 0
- ในกรณีที่สองตัวระบุ "-0500" บ่งชี้ว่าอินพุตอยู่ใน GMT-05: 00 และเบราว์เซอร์ทั้งสองแปลความหมายอินพุตที่อยู่ในเขตเวลา -05: 00 นั่นหมายความว่าค่า UTC ถูกชดเชยจากยุคซึ่งหมายถึงการเพิ่ม 18000000ms ให้กับค่าเวลาภายในของวันที่
- เคสที่สามซึ่งไม่มีตัวระบุควรถูกใช้เป็นโลคัลสำหรับระบบโฮสต์ FF ปฏิบัติต่อข้อมูลเข้าเป็นเวลาท้องถิ่นอย่างถูกต้องขณะที่ Chrome ถือว่าเป็นเวลา UTC ดังนั้นจะต้องสร้างค่าเวลาต่างกัน สำหรับฉันนี่สร้างความแตกต่าง 5 ชั่วโมงในค่าที่เก็บไว้ซึ่งเป็นปัญหา ระบบอื่นที่มีออฟเซ็ตต่างกันจะได้ผลลัพธ์ที่แตกต่างกัน
ความแตกต่างนี้ได้รับการแก้ไขตั้งแต่ปี 2020 แต่ข้อผิดพลาดอื่น ๆ อยู่ระหว่างเบราว์เซอร์เมื่อแยกวิเคราะห์สตริงการจัดรูปแบบ ISO 8601
แต่มันแย่ลงเรื่อย ๆ ความแปลกประหลาดของ ECMA-262 คือรูปแบบ ISO 8601 date – only (YYYY-MM-DD) จะต้องมีการแยกวิเคราะห์เป็น UTC ในขณะที่ ISO 8601 ต้องการให้แยกวิเคราะห์เป็นท้องถิ่น นี่คือเอาต์พุตจาก FF ที่มีรูปแบบวันที่ ISO แบบยาวและแบบสั้นที่ไม่มีตัวระบุเขตเวลา
Date.parse('1970-01-01T00:00:00'); // 18000000
Date.parse('1970-01-01'); // 0
ดังนั้นอันแรกจะถูกแจงเป็นท้องถิ่นเพราะเป็น ISO 8601 วันที่และเวลาโดยไม่มีเขตเวลาและอันที่สองจะถูกแจงเป็น UTC เพราะเป็นวันที่ ISO 8601 เท่านั้น
ดังนั้นในการตอบคำถามเดิมโดยตรง"YYYY-MM-DD"
ECMA-262 จึงจำเป็นต้องตีความเป็น UTC ในขณะที่อีกคำถามหนึ่งแปลเป็นท้องถิ่น นั่นเป็นเหตุผล:
สิ่งนี้ไม่ให้ผลลัพธ์ที่เทียบเท่า:
console.log(new Date(Date.parse("Jul 8, 2005")).toString()); // Local
console.log(new Date(Date.parse("2005-07-08")).toString()); // UTC
สิ่งนี้:
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08T00:00:00")).toString());
บรรทัดล่างคือสิ่งนี้สำหรับการแยกสตริงวันที่ สตริง ISO 8601 เท่านั้นที่คุณสามารถแยกวิเคราะห์ข้ามเบราว์เซอร์ได้อย่างปลอดภัยคือรูปแบบยาวที่มีออฟเซ็ต (± HH: mm หรือ "Z") หากคุณทำเช่นนั้นคุณสามารถย้อนกลับไปมาระหว่างเวลาท้องถิ่นและ UTC อย่างปลอดภัย
สิ่งนี้ใช้ได้กับเบราว์เซอร์ (หลังจาก IE9):
console.log(new Date(Date.parse("2005-07-08T00:00:00Z")).toString());
เบราว์เซอร์ปัจจุบันส่วนใหญ่ใช้รูปแบบการป้อนข้อมูลอื่นอย่างเท่าเทียมกันรวมถึง '1/1/1970' (M / D / YYYY) ที่ใช้บ่อยและ '1/1/1970 00:00:00 AM' (M / D / YYYY hh : mm: ss ap) รูปแบบ รูปแบบต่อไปนี้ทั้งหมด (ยกเว้นที่ผ่านมา) ถือเป็นอินพุตเวลาท้องถิ่นในเบราว์เซอร์ทั้งหมด ผลลัพธ์ของรหัสนี้จะเหมือนกันในทุกเบราว์เซอร์ในเขตเวลาของฉัน อันสุดท้ายถือเป็น -05: 00 โดยไม่คำนึงถึงเขตเวลาโฮสต์เพราะการตั้งค่าออฟเซ็ตในการประทับเวลา:
console.log(Date.parse("1/1/1970"));
console.log(Date.parse("1/1/1970 12:00:00 AM"));
console.log(Date.parse("Thu Jan 01 1970"));
console.log(Date.parse("Thu Jan 01 1970 00:00:00"));
console.log(Date.parse("Thu Jan 01 1970 00:00:00 GMT-0500"));
อย่างไรก็ตามเนื่องจากการแยกวิเคราะห์แม้แต่รูปแบบที่ระบุใน ECMA-262 ไม่สอดคล้องกันดังนั้นจึงไม่แนะนำให้ใช้ตัวแยกวิเคราะห์ในตัวและแยกวิเคราะห์สตริงด้วยตนเองเสมอโดยใช้ไลบรารีและจัดรูปแบบให้กับตัวแยกวิเคราะห์
เช่นใน moment.js คุณอาจจะเขียน:
let m = moment('1/1/1970', 'M/D/YYYY');
ด้านเอาท์พุท
ในด้านผลลัพธ์เบราว์เซอร์ทั้งหมดแปลโซนเวลาด้วยวิธีเดียวกัน แต่จัดการกับรูปแบบสตริงต่างกัน นี่คือtoString
ฟังก์ชั่นและสิ่งที่พวกเขาส่งออก สังเกตtoUTCString
และtoISOString
ฟังก์ชั่นเอาต์พุต 5:00 AM บนเครื่องของฉัน นอกจากนี้ชื่อเขตเวลาอาจเป็นตัวย่อและอาจแตกต่างกันในการใช้งานที่แตกต่างกัน
แปลงจาก UTC เป็นเวลาท้องถิ่นก่อนทำการพิมพ์
- toString
- toDateString
- toTimeString
- toLocaleString
- toLocaleDateString
- toLocaleTimeString
พิมพ์เวลา UTC ที่เก็บไว้โดยตรง
- toUTCString
- toISOString
ใน Chrome
toString Thu Jan 01 1970 00:00:00 GMT-05:00 (Eastern Standard Time)
toDateString Thu Jan 01 1970
toTimeString 00:00:00 GMT-05:00 (Eastern Standard Time)
toLocaleString 1/1/1970 12:00:00 AM
toLocaleDateString 1/1/1970
toLocaleTimeString 00:00:00 AM
toUTCString Thu, 01 Jan 1970 05:00:00 GMT
toISOString 1970-01-01T05:00:00.000Z
ใน Firefox
toString Thu Jan 01 1970 00:00:00 GMT-05:00 (Eastern Standard Time)
toDateString Thu Jan 01 1970
toTimeString 00:00:00 GMT-0500 (Eastern Standard Time)
toLocaleString Thursday, January 01, 1970 12:00:00 AM
toLocaleDateString Thursday, January 01, 1970
toLocaleTimeString 12:00:00 AM
toUTCString Thu, 01 Jan 1970 05:00:00 GMT
toISOString 1970-01-01T05:00:00.000Z
ปกติฉันจะไม่ใช้รูปแบบ ISO สำหรับการป้อนสตริง เวลาเดียวที่ใช้รูปแบบนั้นมีประโยชน์กับฉันคือเมื่อวันที่ต้องถูกจัดเรียงเป็นสตริง รูปแบบ ISO สามารถจัดเรียงตามที่เป็นในขณะที่คนอื่นไม่ได้ หากคุณต้องมีความเข้ากันได้ข้ามเบราว์เซอร์ให้ระบุเขตเวลาหรือใช้รูปแบบสตริงที่เข้ากันได้
รหัสnew Date('12/4/2013').toString()
ผ่านการแปลงภายในแบบต่อไปนี้:
"12/4/2013" -> toUCT -> [storage] -> toLocal -> print "12/4/2013"
ฉันหวังว่าคำตอบนี้มีประโยชน์