ทำไม“ 2016-02-16” จึงไม่เท่ากับ“ 2016-02-16 00:00”


97

new Date(t)ฉันพยายามที่จะผ่านสตริงวันที่ทั้งสอง

ฉันคาดว่าทั้งสองสายจะแสดงเวลาเดียวกันถ้าฉันเว้นเวลามันควรจะเป็นเที่ยงคืนของวันนั้นหรือ

แต่ในขณะที่

new Date("2016-02-16 00:00")

ส่งคืน 2016-02-16 เที่ยงคืนตามเวลาท้องถิ่นตามที่คาดไว้

new Date("2016-02-16")

ส่งคืนวันที่ 2016-02-16 เที่ยงคืน UTC ซึ่งผิดหรืออย่างน้อยก็ไม่ใช่สิ่งที่ฉันคาดหวังจากสิ่งที่สตริงอื่นแยกวิเคราะห์

ฉันเข้าใจดีว่าพวกเขาทั้งสองจะมีพฤติกรรมเหมือนกันไม่ว่าจะเป็นการคืนเวลาเป็นเวลาท้องถิ่นหรือตามเวลา UTC แต่ดูเหมือนว่าจะไม่สอดคล้องกันมากว่าทำไมพวกเขาถึงกลับต่างกันเช่นนี้

วิธีแก้ปัญหาเมื่อใดก็ตามที่ฉันพบวันที่ที่ไม่มีการประทับเวลาที่ตรงกันฉันสามารถต่อท้าย "00:00" เพื่อให้ได้พฤติกรรมที่สอดคล้องกัน แต่ดูเหมือนว่าจะค่อนข้างเปราะบาง

ฉันได้รับค่านี้จากองค์ประกอบ INPUT ประเภท 'datetime-local' ดังนั้นดูเหมือนว่าจะไม่สอดคล้องกันโดยเฉพาะอย่างยิ่งที่ฉันต้องแก้ไขค่าที่ส่งคืนโดยองค์ประกอบของหน้า

ฉันทำอะไรผิดหรือฉันควรทำอะไรที่แตกต่างออกไป?


2
2016-02-16 00:00- ดูไม่เหมือนกับเวลาที่ถูกต้องเลย ecma-international.org/ecma-262/6.0/…แต่ถึงแม้คุณจะวางไว้ที่Tนั่นมันก็มีพฤติกรรมที่แตกต่างออกไปแน่นอน
zerkms

ปัจจุบันคุณได้รับวัตถุ Date จากองค์ประกอบอินพุตตามค่าของมันอย่างไร
BoltClock

4
ตามมาตรฐาน - "ถ้าไม่มีฟิลด์ HH, mm หรือ ss" 00 "ถูกใช้เป็นค่าและค่าของฟิลด์ sss ที่ไม่มีอยู่คือ" 000 "หากไม่มีการชดเชยเขตเวลาวันที่ - เวลา ถูกตีความว่าเป็นเวลาท้องถิ่น " --- ควรปฏิบัติเช่นเดียวกัน
zerkms

@BoltClock อืมดูเหมือนว่าจะใช้ฟิลด์ค่าขององค์ประกอบและ (ตามที่ zerkms สังเกตเห็น) ลบ T ด้วยเหตุผลบางประการ (ฉันคิดว่าเป็นเพราะค่าจะแสดงต่อผู้ใช้ในบริบทที่ "T" อาจทำให้สับสน)
Michael

1
@ ไมเคิล: เยี่ยมมาก นิสัยใจคอของเบราว์เซอร์เช่นนี้เป็นที่ชื่นชอบ (หรืออาจเป็นมุมมองของข้อกำหนด DOM ไม่รู้ฉันไม่ได้ดูจริงๆ)
BoltClock

คำตอบ:


101

นี่คือสิ่งที่ข้อกำหนด ES5.1บอกให้ทำ:

ค่าของการชดเชยเขตเวลาที่ไม่มีคือ“ Z”

นอกจากนี้ยังกล่าวว่า:

ฟังก์ชันแรกจะพยายามแยกวิเคราะห์รูปแบบของ String ตามกฎที่เรียกใน Date Time String Format (15.9.1.15) หากสตริงไม่สอดคล้องกับรูปแบบดังกล่าวฟังก์ชันอาจถอยกลับไปใช้การวิเคราะห์พฤติกรรมเฉพาะการใช้งานหรือรูปแบบวันที่เฉพาะการนำไปใช้งาน

เนื่องจากรูปแบบต้องใช้Tตัวคั่นระหว่างวันที่และเวลาเวลาที่ถูกต้องจึงไปที่ UTC:

> new Date("2016-02-16T00:00:00")
Tue Feb 16 2016 01:00:00 GMT+0100 (CET)
> new Date("2016-02-16")
Tue Feb 16 2016 01:00:00 GMT+0100 (CET)

... ขณะอยู่ใน node.js เวลาที่ไม่ถูกต้อง (ไม่มีตัวคั่น T) ดูเหมือนว่าจะไปที่ localtime เฉพาะของการใช้งาน:

> new Date("2016-02-16 00:00:00")
Tue Feb 16 2016 00:00:00 GMT+0100 (CET)

โปรดทราบว่า ES6 เปลี่ยนแปลงสิ่งนี้ในส่วนเดียวกันของเอกสารที่เปลี่ยนเป็น:

หากไม่มีการชดเชยเขตเวลาระบบจะตีความวันที่ - เวลาเป็นเวลาท้องถิ่น

ความสุขของการเปลี่ยนแปลงที่จะหมด

แก้ไข

ตามTC39ข้อกำหนดนี้หมายถึงการตีความเป็นสตริงวันที่และเวลาโดยไม่มีเขตเวลา (เช่น "2016-02-16T00: 00: 00") จะถือว่าเป็นแบบโลคัล (ตาม ISO 8601) แต่วันที่เท่านั้นสตริง (เช่น "2016-02-16") เป็น UTC (ซึ่งไม่สอดคล้องกับ ISO 8601)


20
ความสุขของการทำลายการเปลี่ยนแปลงแน่นอน ในร่างมาตรฐาน ES7 ฉบับใหม่ส่วนหนึ่งของการเปลี่ยนแปลงจาก UTC เป็นเวลาท้องถิ่นถูกเปลี่ยนกลับมาตรฐานใหม่ระบุว่าวันที่ควรตีความเป็น UTC หากไม่มีการระบุเขตเวลาในขณะที่วันที่ - เวลาควรตีความเป็นเวลาท้องถิ่นหากไม่มีเขตเวลา ระบุไว้
hichris123

3
ทั้งคู่ควรเป็นเวลาท้องถิ่นโดยไม่คำนึงถึงรูปแบบวันที่เท่านั้นหรือวันที่ - เวลา นั่นคือวิธีการทำงานของ ISO8601 เป็นเรื่องน่าขันที่รูปแบบวันที่เท่านั้นถูกตีความว่าเป็นเวลาเที่ยงคืน UTC เนื่องจากวันที่ UTC เป็นอมตะไม่ได้มีเหตุผลในเชิงแนวคิด มีการถกเถียงกันอย่างดุเดือดเกี่ยวกับเรื่องนี้ที่ ECMA tc39 ฉันต่อสู้เพื่อเวลาท้องถิ่นและแพ้ github.com/tc39/ecma262/issues/87
Matt Johnson-Pint

10

ตามข้อกำหนด :

ฟังก์ชันแรกจะพยายามแยกวิเคราะห์รูปแบบของ String ตามกฎที่เรียกใน Date Time String Format (15.9.1.15) หากสตริงไม่สอดคล้องกับรูปแบบดังกล่าวฟังก์ชันอาจถอยกลับไปใช้การวิเคราะห์พฤติกรรมเฉพาะการใช้งานหรือรูปแบบวันที่เฉพาะการนำไปใช้งาน

และรูปแบบสตริงวันเวลายอมรับ2016-02-16เป็นวันที่ที่ถูกต้อง

รูปแบบนี้รวมถึงรูปแบบวันที่เท่านั้น:

ปปปปปป
ปปปป
ปปปป-MM-DD

[... ] ถ้าฟิลด์ HH, mm หรือ ss ไม่มี "00" จะถูกใช้เป็นค่าและค่าของฟิลด์ sss ที่ไม่มีคือ "000" ค่าของการชดเชยเขตเวลาที่ไม่มีคือ“ Z”

ด้วยประการฉะนี้ 2016-02-16แปล2016-02-16T00:00:00.000Zว่า

วันที่อื่น2016-02-16 00:00ไม่เป็นไปตามรูปแบบดังนั้นการแยกวิเคราะห์จึงเป็นการใช้งานเฉพาะ เห็นได้ชัดว่าวันที่ดังกล่าวถือว่ามีเขตเวลาท้องถิ่นและวันที่ในตัวอย่างของคุณจะส่งกลับค่าที่แตกต่างกันขึ้นอยู่กับเขตเวลา:

/* tz = +05:00 */ new Date("2016-02-16 00:00").toISOString() // 2016-02-15T19:00:00.000Z
/* tz = -08:00 */ new Date("2016-02-16 00:00").toISOString() // 2016-02-16T08:00:00.000Z

สรุป:

  • สำหรับรูปแบบวันที่ที่สอดคล้องกันจะมีการกำหนดลักษณะการทำงานไว้อย่างดี - ในกรณีที่ไม่มีการชดเชยเขตเวลาสตริงวันที่จะถือว่าเป็น UTC (ES5) หรือภายใน (ES6)
  • สำหรับรูปแบบวันที่ที่ไม่เป็นไปตามข้อกำหนดลักษณะการทำงานจะเป็นการใช้งานเฉพาะในกรณีที่ไม่มีเขตเวลาชดเชยตามปกติพฤติกรรมคือการถือว่าวันที่เป็นแบบท้องถิ่น
  • ตามความเป็นจริงการใช้งานสามารถเลือกที่จะส่งคืนNaNแทนที่จะพยายามแยกวิเคราะห์วันที่ที่ไม่เป็นไปตาม เพียงทดสอบโค้ดของคุณใน Internet Explorer 11;)

7

คุณอาจพบความแตกต่างระหว่างการใช้งาน ES5, ES6 และผลลัพธ์ที่คุณคาดหวัง ต่อDate.parseที่ MDN "โดยเฉพาะอย่างยิ่งในการใช้งาน ECMAScript ที่แตกต่างกันซึ่งสตริงเช่น" 2015-10-12 12:00:00 "อาจถูกแยกวิเคราะห์เป็น NaN, UTC หรือเขตเวลาท้องถิ่น" มีความสำคัญ

การทดสอบเพิ่มเติมใน Firefox 44 และ IE 11 พบว่าทั้งคู่ส่งคืนวัตถุวันที่สำหรับ new Date("2016-02-16 00:00")ที่ซึ่งอ็อบเจ็กต์ส่งคืน NaN เมื่อพยายามรับค่าคอมโพเนนต์วันที่และค่า toString คือ "Invalid Date" (ไม่ใช่ "NaN") ดังนั้นการต่อท้าย "00:00 เพื่อให้ได้พฤติกรรมที่สอดคล้องกัน" สามารถทำลายเบราว์เซอร์ต่างๆได้อย่างง่ายดาย

ดังที่ระบุไว้ในคำตอบอื่น ๆnew Date("2016-02-16")จะใช้การชดเชยเขตเวลาเป็นศูนย์โดยค่าเริ่มต้นโดยจะสร้างเวลาเที่ยงคืน UTC แทนท้องถิ่น


ดูเหมือนว่า OP จะได้ผลลัพธ์ที่แตกต่างกันในการใช้งานเดียวกัน
Salman A

6

ตามDateParser::Parse()ซอร์สโค้ด V8 สำหรับ Chrome

ES5 ISO 8601 วันที่:

[('-'|'+')yy]yyyy[-MM[-DD]][THH:mm[:ss[.sss]][Z|(+|-)hh:mm]]

ตัวเลขที่ไม่ได้ลงนามตามด้วย ":" คือค่าเวลาและจะถูกเพิ่มเข้าไปใน TimeComposer

เขตเวลาเริ่มต้นเป็น Z หากไม่มี

> new Date("2016-02-16 00:00")
  Tue Feb 16 2016 00:00:00 GMT+0800 (China Standard Time)

สตริงที่ตรงกับทั้งสองรูปแบบ (เช่น1970-01-01) จะถูกแยกวิเคราะห์เป็นสตริงวันที่ - เวลา ES5 ซึ่งหมายความว่าค่าเริ่มต้นจะเป็นUTC time-zon e หลีกเลี่ยงไม่ได้หากปฏิบัติตามข้อกำหนด ES5

> new Date("2016-02-16")
Tue Feb 16 2016 08:00:00 GMT+0800 (China Standard Time)

3

ส่งคืนวันที่ 2016-02-16 เที่ยงคืน UTC ซึ่งผิดหรืออย่างน้อยก็ไม่ใช่สิ่งที่ฉันคาดหวังจากสิ่งที่สตริงอื่นแยกวิเคราะห์

จะเพิ่มการชดเชยเขตเวลาให้กับ 00:00

new Date("2016-02-16") เอาต์พุต Tue Feb 16 2016 05:30:00 GMT+0530 (India Standard Time)

เขตเวลาของฉันเป็น IST โดยมีค่าออฟเซ็ต (เป็นนาที) +330ดังนั้นจึงเพิ่ม 330 นาทีถึง 00:00 น.

ตามecma-262 ส่วน 20.3.3.2 Date.parse (สตริง)

หาก ToString ส่งผลให้การดำเนินการเสร็จสิ้นอย่างกะทันหันระบบจะส่งคืนระเบียนความสมบูรณ์ทันที มิฉะนั้นการแยกวิเคราะห์จะตีความสตริงผลลัพธ์เป็นวันที่และเวลา จะส่งคืนตัวเลขค่าเวลา UTC ที่สอดคล้องกับวันที่และเวลา String อาจตีความเป็นเวลาท้องถิ่นเวลา UTC หรือเวลาในเขตเวลาอื่น ๆ ขึ้นอยู่กับเนื้อหาของ String

เมื่อคุณอย่างชัดเจนตั้งเวลาหน่วยnew Date("2016-02-16 00:00")มัน wll ใช้ชุดว่าhoursและminutes,

มิฉะนั้นตามที่ระบุไว้ที่นี่ใน 2 0.3.1.16

หากไม่มีการชดเชยเขตเวลาระบบจะตีความวันที่ - เวลาเป็นเวลาท้องถิ่น


ใช่ แต่ทำไมถึงทำเช่นนี้ในกรณีเดียว แต่ไม่ใช่อีกกรณีหนึ่ง?
Michael

@ Michael ใช่ตรวจสอบecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf section 20.3.3.2
gurvinder372

เหตุใดผลลัพธ์จึงแตกต่างกัน มาตรฐานอ้างว่าต้องเหมือนกัน
zerkms

@zerkms Standard บอกว่ามันจะทำอะไรเมื่อคุณผ่านหน่วยเวลามันไม่ได้บอกว่ามันจะทำอะไรเมื่อคุณไม่ทำ มันบอกชัดเจนThe String may be interpreted as a local time, a UTC time, or a time in some other time zone, depending on the contents of the String.ตรงไหนที่อ้างว่าต้องเหมือนกัน?
gurvinder372

@ gurvinder372 ฉันได้ระบุคำพูดไว้ในความคิดเห็นของคำถาม: "ถ้าฟิลด์ HH, mm หรือ ss ไม่มี" 00 "จะใช้เป็นค่าและค่าของฟิลด์ sss ที่ไม่มีคือ" 000 "หากเขตเวลาชดเชย ไม่อยู่วันที่ - เวลาจะถูกตีความเป็นเวลาท้องถิ่น "
zerkms
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.