Mengapa “2016-02-16” tidak sama dengan “2016-02-16 00:00”?

96

Saya mencoba untuk meneruskan kedua string tanggal ke new Date(t).

Saya berharap kedua string mewakili waktu yang sama, lagipula, jika saya menghilangkan waktu, bukankah seharusnya tengah malam pada hari itu?

Tapi sementara,

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

mengembalikan 2016-02-16, tengah malam, waktu setempat seperti yang diharapkan,

new Date("2016-02-16")

mengembalikan 2016-02-16, tengah malam UTC, yang salah, atau setidaknya tidak seperti yang saya harapkan mengingat apa yang parsing string lain.

Saya akan memahaminya jika mereka berdua memiliki perilaku yang sama, apakah itu mengembalikan waktu sebagai waktu lokal, atau sebagai UTC, tetapi tampaknya sangat tidak konsisten mengapa mereka mengembalikan hal yang berbeda seperti ini.

Sebagai solusinya, setiap kali saya menemukan tanggal yang tidak memiliki stempel waktu yang sesuai, saya dapat menambahkan "00:00" untuk mendapatkan perilaku yang konsisten, tetapi tampaknya ini agak rapuh.

Saya mendapatkan nilai ini dari elemen INPUT, jenis 'datetime-local' jadi tampaknya sangat tidak konsisten bahwa saya harus mengatasi nilai yang dikembalikan oleh elemen halaman.

Apakah saya melakukan sesuatu yang salah, atau haruskah saya melakukan sesuatu yang berbeda?

Michael
sumber
2
2016-02-16 00:00- ini sama sekali bukan waktu yang valid. ecma-international.org/ecma-262/6.0/… , tetapi bahkan setelah Anda meletakkannya di Tsana memang berperilaku berbeda
zerkms
Bagaimana tepatnya Anda saat ini mendapatkan objek Date dari elemen input berdasarkan nilainya?
BoltClock
4
Sesuai standar - "Jika bidang HH, mm, atau ss tidak ada," 00 "digunakan sebagai nilai dan nilai bidang sss yang tidak ada adalah" 000 ". Jika offset zona waktu tidak ada, tanggal-waktu diartikan sebagai waktu lokal. " --- itu harus berperilaku sama.
zerkms
@BoltClock Hmm, sepertinya itu mengambil bidang nilai elemen dan (seperti yang diperhatikan zerkms) menghapus T karena beberapa alasan (saya pikir karena nilainya ditampilkan kepada pengguna dalam konteks di mana "T" akan membingungkan)
Michael
1
@ Michael: Fantastis. Keunikan browser seperti ini adalah favorit saya. (Atau mungkinkah itu kekhasan spesifikasi DOM? Tidak tahu, saya belum benar-benar melihat.)
BoltClock

Jawaban:

100

Itulah yang dikatakan spesifikasi ES5.1 :

Nilai offset zona waktu yang tidak ada adalah "Z".

Ia juga mengatakan:

Fungsi ini pertama kali mencoba mengurai format String sesuai dengan aturan yang disebut dalam Format String Waktu Tanggal (15.9.1.15). Jika String tidak sesuai dengan format tersebut, fungsi tersebut dapat kembali ke heuristik khusus implementasi atau format tanggal khusus implementasi.

Karena format memerlukan Tpemisah antara tanggal dan waktu, waktu yang valid masuk ke 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)

... sementara di node.js, waktu yang tidak valid (tanpa pemisah T) tampaknya mengarah ke waktu lokal spesifik implementasi:

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

Perhatikan bahwa ES6 mengubah ini, di bagian yang sama dari dokumentasi itu berubah menjadi:

Jika offset zona waktu tidak ada, tanggal-waktu diartikan sebagai waktu lokal.

Sukacita dalam memecahkan perubahan .

Edit

Menurut TC39 , spesifikasi dimaksudkan untuk diinterpretasikan sebagai string tanggal dan waktu tanpa zona waktu (mis. "2016-02-16T00: 00: 00") diperlakukan sebagai lokal (sesuai ISO 8601), tetapi string hanya tanggal (mis. "2016-02-16") sebagai UTC (yang tidak konsisten dengan ISO 8601).

Joachim Isaksson
sumber
20
Sukacita karena melanggar berubah, memang. Dalam draf baru standar ES7, bagian dari perubahan UTC ke waktu lokal dikembalikan Standar baru mengatakan tanggal harus diinterpretasikan sebagai UTC jika tidak ada zona waktu yang ditentukan, sedangkan tanggal-waktu harus diartikan sebagai waktu lokal jika tidak ada zona waktu ditentukan.
hichris123
3
Keduanya benar-benar harus merupakan waktu lokal, terlepas dari bentuk tanggal-saja atau tanggal-waktu. Begitulah cara kerja ISO8601. Sungguh konyol bahwa formulir khusus tanggal diartikan sebagai tengah malam UTC, karena tanggal UTC yang tidak terbatas bahkan tidak masuk akal secara konseptual. Ada perdebatan sengit tentang hal ini di ECMA tc39. Saya berjuang untuk waktu lokal, dan kalah. github.com/tc39/ecma262/issues/87
Matt Johnson-Pint
10

Menurut spesifikasinya :

Fungsi ini pertama kali mencoba mengurai format String sesuai dengan aturan yang disebut dalam Format String Waktu Tanggal (15.9.1.15). Jika String tidak sesuai dengan format tersebut, fungsi tersebut dapat kembali ke heuristik khusus implementasi atau format tanggal khusus implementasi.

Dan Format String Waktu Tanggal menerima 2016-02-16sebagai tanggal yang valid

Format ini mencakup formulir khusus tanggal:

YYYY
YYYY-MM
YYYY-MM-DD

[...] Jika kolom HH, mm, atau ss tidak ada, "00" digunakan sebagai nilai dan nilai kolom sss yang tidak ada adalah "000". Nilai offset zona waktu yang tidak ada adalah "Z".

Jadi 2016-02-16diterjemahkan menjadi 2016-02-16T00:00:00.000Z.

Tanggal lain 2016-02-16 00:00tidak sesuai dengan format dan oleh karena itu penguraiannya khusus untuk penerapan. Rupanya, tanggal tersebut diperlakukan sebagai memiliki zona waktu lokal dan tanggal contoh Anda akan mengembalikan nilai yang berbeda bergantung pada zona waktu:

/* 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

Ringkasan:

  • Untuk menyesuaikan format waktu tanggal, perilakunya ditentukan dengan baik - jika tidak ada offset zona waktu, string tanggal diperlakukan sebagai UTC (ES5) atau lokal (ES6).
  • Untuk format waktu tanggal yang tidak sesuai, perilakunya adalah penerapan yang spesifik - jika tidak ada offset zona waktu, perilaku yang biasa adalah memperlakukan tanggal sebagai lokal.
  • Faktanya, implementasi dapat memilih untuk kembali NaNdaripada mencoba mengurai tanggal yang tidak sesuai. Cukup uji kode Anda di Internet Explorer 11;)
Salman A
sumber
7

Anda mungkin mengalami perbedaan antara penerapan ES5, ES6, dan hasil yang Anda harapkan. Per Date.parse di MDN, "terutama di seluruh implementasi ECMAScript yang berbeda di mana string seperti" 2015-10-12 12:00:00 "dapat diurai menjadi NaN, UTC, atau zona waktu lokal" signifikan.

Pengujian tambahan di Firefox 44 dan IE 11 mengungkapkan bahwa keduanya mengembalikan objek tanggal new Date("2016-02-16 00:00"), yang objek mengembalikan NaN saat mencoba mendapatkan nilai komponen tanggal, dan nilai toStringnya adalah "Tanggal Tidak Valid" (bukan "NaN"). Karenanya menambahkan "00:00 untuk mendapatkan perilaku yang konsisten" dapat dengan mudah merusak browser yang berbeda.

Seperti dicatat dalam jawaban lain new Date("2016-02-16")menggunakan offset zona waktu nol secara default, menghasilkan tengah malam UTC, bukan lokal.

traktor53
sumber
OP tampaknya mendapatkan hasil yang berbeda pada implementasi yang sama.
Salman A
6

Per DateParser::Parse()dari kode sumber V8 untuk Chrome.

Tanggal ES5 ISO 8601:

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

Nomor unsigned diikuti dengan ':' adalah nilai waktu, dan ditambahkan ke TimeComposer.

zona waktu default ke Z jika tidak ada

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

Sebuah string yang cocok dengan kedua format (misalnya 1970-01-01) akan diurai sebagai string waktu-tanggal ES5 - yang berarti akan default ke UTC time-zone. Itu tidak bisa dihindari jika mengikuti spesifikasi ES5.

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

mengembalikan 2016-02-16, tengah malam UTC, yang salah, atau setidaknya tidak seperti yang saya harapkan mengingat apa yang parsing string lain.

Ini menambahkan offset zona waktu ke 00:00

new Date("2016-02-16") keluaran Tue Feb 16 2016 05:30:00 GMT+0530 (India Standard Time)

Zona waktu saya adalah IST dengan nilai offset (dalam menit) +330, jadi ditambahkan 330 menit menjadi 00:00.

Sesuai ecma-262, bagian 20.3.3.2 Date.parse (string)

Jika ToString menghasilkan penyelesaian mendadak, Rekaman Penyelesaian segera dikembalikan. Jika tidak, parse menafsirkan String yang dihasilkan sebagai tanggal dan waktu; ia mengembalikan sebuah Angka, nilai waktu UTC sesuai dengan tanggal dan waktu. String dapat diartikan sebagai waktu lokal, waktu UTC, atau waktu di zona waktu lain, bergantung pada konten String.

Ketika Anda secara eksplisit mengatur unit waktu new Date("2016-02-16 00:00")itu akan menggunakan set itu sebagai hoursdan minutes,

Sebaliknya seperti yang dinyatakan di sini dalam 2 0.3.1.16

Jika offset zona waktu tidak ada, tanggal-waktu diartikan sebagai waktu lokal.

gurvinder372
sumber
Ya, tetapi mengapa ia melakukan ini di satu kasus tetapi tidak di yang lain?
Michael
@Michael ya, periksa ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf section 20.3.3.2
gurvinder372
Jadi kenapa hasilnya berbeda? Klaim standar itu harus sama
zerkms
@zerkms Standard mengatakan apa yang akan dilakukannya ketika Anda melewatkan unit waktu, tidak mengatakan apa yang akan dilakukannya ketika Anda tidak mau. Dengan jelas tertulis 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.Di mana ia mengklaim bahwa itu pasti sama?
gurvinder372
@ gurvinder372 Saya telah memberikan kutipan di komentar pertanyaan: "Jika bidang HH, mm, atau ss tidak ada" 00 "digunakan sebagai nilai dan nilai bidang sss yang tidak ada adalah" 000 ". Jika zona waktu offset tidak ada, tanggal-waktu diartikan sebagai waktu lokal. "
zerkms