ทำไม Date.parse ให้ผลลัพธ์ที่ไม่ถูกต้อง


346

กรณีที่หนึ่ง:

new Date(Date.parse("Jul 8, 2005"));

เอาท์พุท:

ศุกร์ 8 กรกฎาคม 2005 00:00:00 GMT-0700 (PST)

กรณีที่สอง:

new Date(Date.parse("2005-07-08"));

เอาท์พุท:

พฤ. 07 ก.ค. 2005 17:00:00 GMT-0700 (PST)


ทำไมการแยกวิเคราะห์ที่สองจึงไม่ถูกต้อง


30
การแยกวิเคราะห์ครั้งที่สองนั้นไม่ถูกต้องต่อหนึ่งครั้งมันเป็นเพียงการแยกวิเคราะห์ครั้งแรกในเวลาท้องถิ่นและที่สองใน UTC โปรดทราบว่า "Thu Jul 07 2005 17:00:00 GMT-0700 (PST)" เหมือนกับ "2005-07-08 00:00"
jches


18

1
ในกรณีที่ทุกคนมาที่นี่เพื่อหาสาเหตุว่าทำไมวันที่ถึงส่งคืนNaNใน Firefox ฉันค้นพบว่าเบราว์เซอร์อื่น ๆ ส่วนใหญ่ (และ Node.js) จะแยกวิเคราะห์วันที่โดยไม่มีวันเช่น "เมษายน 2014" เป็น 1 เมษายน 2014 แต่ Firefox ส่งคืน NaN คุณต้องผ่านวันที่ที่เหมาะสม
Jazzy

หากต้องการเพิ่มความคิดเห็นของ Jason ด้านบน: หากคุณได้รับ NaN ใน Firefox ปัญหาอื่นอาจเป็นได้ว่า Firefox และ Safari ไม่ชอบวันที่ใส่ยัติภังค์ เฉพาะ Chrome เท่านั้น ใช้เครื่องหมายทับแทน
Bangkok

คำตอบ:


451

จนกว่าสเป็ครุ่นที่ 5 จะออกมาDate.parseวิธีการนี้ขึ้นอยู่กับการนำไปใช้อย่างสมบูรณ์( new Date(string)เทียบเท่ากับDate.parse(string)ยกเว้นในภายหลังที่ส่งคืนตัวเลขมากกว่า a Date) ในข้อกำหนดรุ่นที่ 5 มีการเพิ่มข้อกำหนดเพื่อรองรับISO-8601 ที่เรียบง่าย(และไม่ถูกต้องเล็กน้อย) (ดูที่สตริงวันที่และเวลาที่ถูกต้องใน JavaScript คืออะไร ) แต่นอกเหนือจากนั้นไม่มีข้อกำหนดสำหรับสิ่งที่Date.parse/ new Date(string)ควรยอมรับนอกเหนือจากที่พวกเขาต้องยอมรับสิ่งที่Date#toStringเอาท์พุท (โดยไม่บอกว่ามันคืออะไร)

ในฐานะของ ECMAScript 2017 (รุ่น 8) การใช้งานจะต้องแยกวิเคราะห์ผลลัพธ์ของพวกเขาสำหรับDate#toStringและDate#toUTCStringแต่รูปแบบของสตริงเหล่านั้นไม่ได้ระบุ

ตั้งแต่วันที่ ECMAScript 2019 (ฉบับที่ 9) รูปแบบสำหรับDate#toStringและDate#toUTCStringได้รับการระบุว่าเป็น (ตามลำดับ):

  1. ddd MMM DD YYYY HH: mm: ss ZZ [(ชื่อเขตเวลา)]
    เช่นอังคาร 10 ก.ค. 2018 18:39:58 GMT + 0530 (IST)
  2. ddd, DD MMM YYYY HH: mm: ss Z
    เช่น อ. 10 ก.ค. 2018 13:09:58 น. GMT

การจัดรูปแบบเพิ่มเติม 2 รูปแบบที่Date.parseควรแยกวิเคราะห์ได้อย่างน่าเชื่อถือในการนำไปใช้งานใหม่ (การสังเกตว่าการสนับสนุนไม่ใช่การนำไปปฏิบัติที่ไม่แพร่หลายและไม่สอดคล้องจะยังคงใช้อยู่ในบางครั้ง)

ฉันอยากจะแนะนำว่าสตริงวันที่มีการแยกวิเคราะห์ด้วยตนเองและตัวสร้างวันที่ใช้กับข้อโต้แย้งปีเดือนและวันเพื่อหลีกเลี่ยงความคลุมเครือ:

// parse a date in yyyy-mm-dd format
function parseDate(input) {

  let parts = input.split('-');

  // new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
  return new Date(parts[0], parts[1]-1, parts[2]); // Note: months are 0-based
}

ยอดเยี่ยมฉันต้องใช้สิ่งนี้เนื่องจากDate.parseไม่ได้ทำงานกับรูปแบบวันที่ในสหราชอาณาจักรด้วยเหตุผลบางอย่างที่ฉันไม่สามารถทำได้
เบ็

1
ส่วนเวลามีการบันทึกไว้ในรหัส @CMS ฉันใช้รหัสนี้ในรูปแบบวันที่ "2012-01-31 12:00:00" return new Date(parts[0], parts[1] - 1, parts[2], parts[3], parts[4], parts[5]);ทำงานได้อย่างสมบูรณ์ขอบคุณ!
Richard Rout

1
@CMS คุณหมายถึงอะไร ขึ้นอยู่กับการติดตั้งใช้งาน ?
Royi Namir

3
@RoyiNamir หมายความว่าผลลัพธ์ขึ้นอยู่กับว่าเว็บเบราว์เซอร์ใด (หรือการติดตั้ง JavaScript อื่น ๆ ) กำลังเรียกใช้รหัสของคุณ
ซามูเอลเอ็ดวินวอร์ด

1
ฉันยังมีปัญหากับวันที่ใหม่ (สตริง) ในเบราว์เซอร์ที่แตกต่างกันที่ทำงานแตกต่างกัน ไม่ใช่แม้แต่คำถามว่ามันใช้งานไม่ได้กับ IE เวอร์ชั่นเก่าเบราว์เซอร์ที่แตกต่างกันนั้นไม่สอดคล้องกัน ห้ามใช้ Date.parse หรือ Date ใหม่ (สตริง) เลยทีเดียว
Hoffmann

195

ในช่วงที่ผ่านมาประสบการณ์การเขียนล่าม 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"

ฉันหวังว่าคำตอบนี้มีประโยชน์


3
ก่อนอื่นนี่เป็นบทความที่น่าอัศจรรย์ อย่างไรก็ตามฉันต้องการชี้ให้เห็นถึงการพึ่งพา เกี่ยวกับตัวระบุเขตเวลาคุณระบุ: "การไม่มีตัวระบุควรเข้าใจการป้อนข้อมูลเวลาท้องถิ่น" โชคดีที่มาตรฐาน ECMA-262 ขจัดความต้องการใด ๆ มันระบุว่า : "ค่าของออฟเซ็ตเขตเวลาขาดคือ" Z " ดังนั้นสตริงวันที่ / เวลาที่ไม่มีเขตเวลาที่ระบุจะถือว่าเป็น UTC แทนเวลาท้องถิ่น แน่นอนเช่นเดียวกับ JavaScript หลายสิ่งดูเหมือนมีข้อตกลงเล็กน้อยระหว่างการใช้งาน
Daniel

2
... รวมถึงรูปแบบที่ใช้บ่อยที่สุด '1/1/1970' และ '1/1/1970 00:00:00 AM' - ใช้บ่อยที่สุดที่ ? นั่นไม่ใช่ในประเทศของฉันแน่นอน
ulidtko

2
@ulidtko - ขออภัยฉันอยู่ในสหรัฐอเมริกา ว้าว ... คุณอยู่ที่นั่นในเคียฟ ฉันหวังว่าคุณและครอบครัวของคุณจะปลอดภัยและสิ่งต่าง ๆ จะมีเสถียรภาพในไม่ช้า ดูแลตัวเองและขอให้โชคดีกับทุกสิ่ง
drankin2112

เพิ่งทราบที่นี่ ดูเหมือนว่าสิ่งนี้จะไม่สามารถใช้ได้กับเบราว์เซอร์ Safari (เช่น iOS หรือ OSX) หรือฉันมีปัญหาอื่น ๆ เกิดขึ้น
keyneom

1
@ Daniel— โชคดีที่ผู้เขียน ECMAScript แก้ไขข้อผิดพลาดเกี่ยวกับเขตเวลาที่หายไปสำหรับการแสดงวันที่และเวลา ตอนนี้สตริงวันที่และเวลาโดยไม่มีเขตเวลาใช้เขตเวลาชดเชยโฮสต์ (เช่น "ท้องถิ่น") แบบฟอร์มวันที่ ISO 8601 เท่านั้นจะถือว่าเป็น UTC (แม้ว่าจะไม่ชัดเจนโดยเฉพาะจากข้อมูลจำเพาะ) ในขณะที่ ISO 8601 ถือว่าพวกเขาเป็นท้องถิ่นดังนั้นพวกเขาจึงไม่ได้แก้ไขทุกอย่าง
RobG

70

มีวิธีการบางอย่างที่บ้า ตามกฎทั่วไปหากเบราว์เซอร์สามารถตีความวันที่เป็น ISO-8601 ก็จะ "2005-07-08" ตกลงไปในค่ายนี้และแยกวิเคราะห์เป็น UTC "8 ก.ค. 2548" ไม่สามารถทำได้และแยกวิเคราะห์ในเวลาท้องถิ่น

ดูจาวาสคริปต์และวันที่เป็นระเบียบ! มากขึ้น


3
" ตามกฎทั่วไปหากเบราว์เซอร์สามารถแปลวันที่เป็น ISO-8601 ได้ก็จะไม่รองรับ " "2020-03-20 13:30:30" ถือเป็น ISO 8601 และในท้องที่โดยเบราว์เซอร์จำนวนมาก แต่ Invalid Date by Safari มีหลายรูปแบบ ISO 8601 ที่เบราว์เซอร์ส่วนใหญ่ไม่รองรับเช่น 2004-W53-7 และ 2020-092
RobG

7

อีกวิธีหนึ่งคือการสร้างอาเรย์แบบเชื่อมโยงที่มีรูปแบบวันที่แล้วจัดรูปแบบข้อมูลใหม่

วิธีนี้มีประโยชน์สำหรับการจัดรูปแบบวันที่ด้วยวิธีที่ไม่จำเป็น

ตัวอย่าง:

    mydate='01.02.12 10:20:43':
    myformat='dd/mm/yy HH:MM:ss';


    dtsplit=mydate.split(/[\/ .:]/);
    dfsplit=myformat.split(/[\/ .:]/);

    // creates assoc array for date
    df = new Array();
    for(dc=0;dc<6;dc++) {
            df[dfsplit[dc]]=dtsplit[dc];
            }

    // uses assc array for standard mysql format
    dstring[r] = '20'+df['yy']+'-'+df['mm']+'-'+df['dd'];
    dstring[r] += ' '+df['HH']+':'+df['MM']+':'+df['ss'];

5

ใช้moment.jsเพื่อแยกวันที่:

var caseOne = moment("Jul 8, 2005", "MMM D, YYYY", true).toDate();
var caseTwo = moment("2005-07-08", "YYYY-MM-DD", true).toDate();

อาร์กิวเมนต์ที่ 3 กำหนดการแยกวิเคราะห์ที่เข้มงวด (มีให้ตั้งแต่ 2.3.0) ถ้าไม่มีมันชั่วครู่ก็อาจให้ผลลัพธ์ที่ไม่ถูกต้อง


4

ตามที่http://blog.dygraphs.com/2012/03/javascript-and-dates-what-mess.htmlรูปแบบ "yyyy / mm / dd" แก้ปัญหาตามปกติ เขาพูดว่า: "ยึดติดกับ" YYYY / MM / DD "สำหรับสตริงวันที่ของคุณเมื่อใดก็ตามที่เป็นไปได้มันสนับสนุนในระดับสากลและไม่คลุมเครือด้วยรูปแบบนี้ทุกครั้งเป็นแบบท้องถิ่น" ฉันได้ตั้งค่าการทดสอบ: http://jsfiddle.net/jlanus/ND2Qg/432/ รูปแบบนี้: + หลีกเลี่ยงความคลุมเครือในการสั่งซื้อรายวันและรายเดือนโดยใช้การสั่ง ymd และปี 4 หลัก + หลีกเลี่ยงปัญหา UTC กับปัญหาในพื้นที่ สอดคล้องกับรูปแบบ ISO โดยใช้ slashes + danvkผู้ชายdygraphsกล่าวว่ารูปแบบนี้ดีในทุกเบราว์เซอร์


คุณอาจต้องการที่จะเห็นคำตอบของผู้เขียน
แบรดโคช์

ฉันจะบอกว่าการแก้ปัญหาด้วยตัวอย่างใน jsFiddle ทำงานได้ดีพอถ้าคุณใช้ jQuery เพราะมันใช้ตัวแยกวิเคราะห์ datepicker ในกรณีของฉันมีปัญหากับ jqGrid แต่พบว่ามันมีวิธีการ parseDate แต่อย่างไรก็ตามตัวอย่างก็ช่วยฉันด้วยการให้แนวคิดกับ +1 ดังนั้นขอบคุณ
Vasil Popov

2
บทความใน dygraphs นั้นผิดและตัวอย่างแรกในหน้านั้นแสดงให้เห็นอย่างชัดเจนว่าทำไมการใช้เครื่องหมายทับแทนการใช้เครื่องหมายขีดคั่นเป็นคำแนะนำที่ไม่ดีจริงๆ ในขณะที่เขียนบทความโดยใช้ "2012/03/13" ทำให้เบราว์เซอร์แยกวิเคราะห์เป็นวันที่ท้องถิ่นแทนที่จะเป็น UTC ข้อมูลจำเพาะ ECMAScript กำหนดการสนับสนุนที่ชัดเจนสำหรับการใช้ "YYYY-MM-DD" (ISO8601) ดังนั้นให้ใช้เครื่องหมายขีดคั่นเสมอ ควรสังเกตว่าในขณะที่ฉันเขียนความคิดเห็นนี้ Chrome ได้รับการแก้ไขเพื่อรักษาเครื่องหมายทับเป็น UTC
Lachlan Hunt

4

แม้ว่าCMS จะถูกต้องว่าการส่งสตริงไปยังวิธีการวิเคราะห์โดยทั่วไปนั้นไม่ปลอดภัย แต่ข้อมูลจำเพาะECMA-262 5th Edition (aka ES5) ใหม่ในส่วนที่ 15.9.4.2 แสดงให้เห็นว่าDate.parse()จริง ๆ แล้วควรจัดการกับวันที่จัดรูปแบบ ISO ข้อกำหนดเก่าไม่ได้อ้างสิทธิ์ดังกล่าว แน่นอนเบราว์เซอร์รุ่นเก่าและเบราว์เซอร์ปัจจุบันบางรุ่นยังไม่สามารถใช้งาน ES5 นี้ได้

ตัวอย่างที่สองของคุณไม่ผิด เป็นวันที่ที่ระบุใน UTC ตามที่ระบุโดยDate.prototype.toISOString()แต่จะแสดงในเขตเวลาท้องถิ่นของคุณ


1
และการแยกสตริงวันที่จะเปลี่ยนไปอีกครั้งใน ECMAScript 2015 ดังนั้น "2005-07-08" เป็นท้องถิ่นไม่ใช่ UTC BTW, ES5 ไม่ใช่มาตรฐานจนถึงเดือนมิถุนายน 2554 (และปัจจุบันคือ ECMAScript 2015) ;-)
RobG

1
TC39 ตัดสินใจตุลาคม (เพียงหนึ่งเดือนหลังจากโพสต์ก่อนหน้า) ว่า "2005-07-08" ควรเป็น UTCอย่างไรก็ตาม "" 2005-07-08T00: 00: 00 "ควรเป็น Local ทั้ง ISO 8601 รูปแบบที่สอดคล้องกันทั้งสองไม่มีเขตเวลา แต่ได้รับการปฏิบัติแตกต่างกันไปคิด
RobG

2

ไลบรารีการแยกวิเคราะห์น้ำหนักเบานี้ควรแก้ไขปัญหาที่คล้ายกันทั้งหมด ฉันชอบห้องสมุดเพราะค่อนข้างยืดได้ง่าย นอกจากนี้ยังเป็นไปได้ที่จะ i18n มัน (ไม่ตรงไปข้างหน้ามาก แต่ไม่ยากที่)

ตัวอย่างการแยก:

var caseOne = Date.parseDate("Jul 8, 2005", "M d, Y");
var caseTwo = Date.parseDate("2005-07-08", "Y-m-d");

และการจัดรูปแบบกลับไปเป็นสตริง (คุณจะสังเกตเห็นว่าทั้งสองกรณีให้ผลลัพธ์เหมือนกันทุกประการ):

console.log( caseOne.dateFormat("M d, Y") );
console.log( caseTwo.dateFormat("M d, Y") );
console.log( caseOne.dateFormat("Y-m-d") );
console.log( caseTwo.dateFormat("Y-m-d") );

2

นี่คือตัวอย่างข้อมูลสั้น ๆ ที่มีความยืดหยุ่นในการแปลงสตริงข้อมูลและเวลาในรูปแบบข้ามเบราว์เซอร์ที่ปลอดภัยตามที่ nicel ได้ให้รายละเอียดโดย @ drankin2112

var inputTimestamp = "2014-04-29 13:00:15"; //example

var partsTimestamp = inputTimestamp.split(/[ \/:-]/g);
if(partsTimestamp.length < 6) {
    partsTimestamp = partsTimestamp.concat(['00', '00', '00'].slice(0, 6 - partsTimestamp.length));
}
//if your string-format is something like '7/02/2014'...
//use: var tstring = partsTimestamp.slice(0, 3).reverse().join('-');
var tstring = partsTimestamp.slice(0, 3).join('-');
tstring += 'T' + partsTimestamp.slice(3).join(':') + 'Z'; //configure as needed
var timestamp = Date.parse(tstring);

เบราว์เซอร์ของคุณควรให้ผลลัพธ์การประทับเวลาเช่นเดียวDate.parseกับ:

(new Date(tstring)).getTime()

ฉันขอแนะนำให้เพิ่ม T ใน regex เพื่อตรวจสอบวันที่จัดรูปแบบ JS แล้วเช่น: inputTimestamp.split (/ [T \ /: -] / g)
andig

หากคุณแยกสตริงออกเป็นส่วนต่าง ๆ ขั้นตอนต่อไปที่น่าเชื่อถือที่สุดคือใช้ส่วนเหล่านั้นเป็นอาร์กิวเมนต์กับตัวสร้างวันที่ การสร้างสตริงอื่นเพื่อมอบให้กับ parser เพียงนำคุณกลับไปที่ขั้นตอนที่ 1 "2014-04-29 13:00:15" ควรทำการแยกวิเคราะห์เป็นโลคัลโค้ดของคุณจะจัดรูปแบบใหม่เป็น UTC :-(
RobG

2

ทั้งสองถูกต้อง แต่ถูกตีความว่าเป็นวันที่ที่มีเขตเวลาที่แตกต่างกันสองเขต ดังนั้นคุณเปรียบเทียบแอปเปิ้ลและส้ม:

// local dates
new Date("Jul 8, 2005").toISOString()            // "2005-07-08T07:00:00.000Z"
new Date("2005-07-08T00:00-07:00").toISOString() // "2005-07-08T07:00:00.000Z"
// UTC dates
new Date("Jul 8, 2005 UTC").toISOString()        // "2005-07-08T00:00:00.000Z"
new Date("2005-07-08").toISOString()             // "2005-07-08T00:00:00.000Z"

ฉันลบการDate.parse()โทรออกเนื่องจากถูกใช้โดยอัตโนมัติในอาร์กิวเมนต์สตริง ฉันเปรียบเทียบวันที่ด้วยรูปแบบ ISO8601เพื่อให้คุณสามารถเปรียบเทียบวันที่ระหว่างวันที่ท้องถิ่นของคุณกับวันที่ UTC เวลาอยู่ห่างกัน 7 ชั่วโมงซึ่งเป็นความแตกต่างของเขตเวลาและสาเหตุที่การทดสอบของคุณแสดงวันที่แตกต่างกันสองวัน

วิธีอื่นในการสร้างวันที่ท้องถิ่น / UTC เหล่านี้จะเป็น:

new Date(2005, 7-1, 8)           // "2005-07-08T07:00:00.000Z"
new Date(Date.UTC(2005, 7-1, 8)) // "2005-07-08T00:00:00.000Z"

แต่ฉันยังคงแนะนำMoment.jsที่เรียบง่าย แต่ทรงพลัง :

// parse string
moment("2005-07-08").format()       // "2005-07-08T00:00:00+02:00"
moment.utc("2005-07-08").format()   // "2005-07-08T00:00:00Z"
// year, month, day, etc.
moment([2005, 7-1, 8]).format()     // "2005-07-08T00:00:00+02:00"
moment.utc([2005, 7-1, 8]).format() // "2005-07-08T00:00:00Z"

0

คำตอบที่ได้รับการยอมรับจาก CMSถูกต้องฉันมีเพียงเพิ่มคุณสมบัติบางอย่าง:

  • ตัดแต่งและทำความสะอาดช่องว่างอินพุต
  • แยกเครื่องหมายสแลชขีดกลางโคลอนและช่องว่าง
  • มีวันและเวลาเริ่มต้น

// parse a date time that can contains spaces, dashes, slashes, colons
function parseDate(input) {
    // trimes and remove multiple spaces and split by expected characters
    var parts = input.trim().replace(/ +(?= )/g,'').split(/[\s-\/:]/)
    // new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
    return new Date(parts[0], parts[1]-1, parts[2] || 1, parts[3] || 0, parts[4] || 0, parts[5] || 0); // Note: months are 0-based
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.