moment.js - UTC memberikan tanggal yang salah

95

Mengapa moment.js UTC selalu menunjukkan tanggal yang salah. Misalnya dari konsol pengembang chrome:

moment(('07-18-2013')).utc().format("YYYY-MM-DD").toString()
// or
moment.utc(new Date('07-18-2013')).format("YYYY-MM-DD").toString()

Keduanya akan mengembalikan "2013-07-17" mengapa mengembalikan ke 17, bukan 18 , yang diteruskan.

Tetapi jika saya menggunakan momentjs tanpa utc:

moment(new Date('07-18-2013')).format("YYYY-MM-DD").toString()

Saya mendapatkan kembali "2013-07-18" yang juga saya harapkan saat menggunakan moment.js UTC.

Apakah ini berarti kita tidak bisa mendapatkan tanggal yang benar saat menggunakan moment.js UTC?

brg
sumber
4
Saya tidak berpikir Anda perlu toString()setelah format()(itu sudah mengembalikan string).
alex

Jawaban:

160

Secara default, MomentJS mengurai dalam waktu lokal. Jika hanya string tanggal (tanpa waktu) yang disediakan, default waktunya adalah tengah malam.

Dalam kode Anda, Anda membuat tanggal lokal dan kemudian mengonversinya ke zona waktu UTC (pada kenyataannya, itu membuat momen instan beralih ke mode UTC ), jadi ketika diformat, itu bergeser (tergantung pada waktu lokal Anda) maju atau ke belakang.

Jika zona waktu lokal adalah UTC + N (N adalah angka positif), dan Anda mengurai string khusus tanggal, Anda akan mendapatkan tanggal sebelumnya.

Berikut adalah beberapa contoh untuk mengilustrasikannya (offset waktu lokal saya adalah UTC + 3 selama DST):

>>> moment('07-18-2013', 'MM-DD-YYYY').utc().format("YYYY-MM-DD HH:mm")
"2013-07-17 21:00"
>>> moment('07-18-2013 12:00', 'MM-DD-YYYY HH:mm').utc().format("YYYY-MM-DD HH:mm")
"2013-07-18 09:00"
>>> Date()
"Thu Jul 25 2013 14:28:45 GMT+0300 (Jerusalem Daylight Time)"

Jika Anda ingin string tanggal-waktu ditafsirkan sebagai UTC, Anda harus menjelaskannya secara eksplisit:

>>> moment(new Date('07-18-2013 UTC')).utc().format("YYYY-MM-DD HH:mm")
"2013-07-18 00:00"

atau, seperti yang disebutkan Matt Johnson dalam jawabannya, Anda dapat ( dan mungkin harus ) menguraikannya sebagai tanggal UTC sejak awal menggunakan moment.utc()dan menyertakan string format sebagai argumen kedua untuk mencegah ambiguitas.

>>> moment.utc('07-18-2013', 'MM-DD-YYYY').format("YYYY-MM-DD HH:mm")
"2013-07-18 00:00"

Untuk sebaliknya dan mengubah tanggal UTC menjadi tanggal lokal, Anda dapat menggunakan local()metode ini, sebagai berikut:

>>> moment.utc('07-18-2013', 'MM-DD-YYYY').local().format("YYYY-MM-DD HH:mm")
"2013-07-18 03:00"
MasterAM
sumber
Terimakasih banyak. Jadi pada dasarnya, saya harus selalu melewatkan waktu saat menggunakan UTC atau meneruskan dalam UTC seperti pada pendekatan kedua Anda.
brg
Entah itu atau tetap berpegang pada zona waktu lokal. Jika Anda mengirim waktu dari server, Anda dapat mengekspresikannya sebagai stempel waktu Unix (X) atau sebagai string pada zona waktu tertentu. Mengapa menggunakan UTC sebagai ganti zona waktu lokal pengguna (kecuali untuk tujuan mengirim data yang dinormalisasi ke server)?
MasterAM
1
Ketahuilah bahwa new Date('07-18-2013 UTC')tidak akan berfungsi di IE8, jika Anda peduli.
Dzmitry Lazerka
2
Aku sudah lama bergumul dengan ini. Mereka seharusnya menjelaskan hal ini dengan baik di situs mereka, karena saya berasumsi bahwa ini adalah kasus penggunaan moment.js yang paling umum. Terima kasih banyak! Kamu benar-benar menyelamatkan kulitku!
WebWanderer
kode ini bekerja untuk saya: [code] moment (strDate, 'DD / MM / YYYY h: mm A'). utc (strDate) .format ("YYYY-MM-DD HH: mm") [/ code]
Omar Isaid
36

Keduanya Datedan momentakan mengurai string input dalam zona waktu lokal browser secara default. Namun Dateterkadang tidak konsisten dengan hal ini. Jika stringnya secara khusus YYYY-MM-DD, menggunakan tanda hubung , atau jika ya YYYY-MM-DD HH:mm:ss, itu akan menafsirkannya sebagai waktu lokal . Tidak seperti Date, momentakan selalu konsisten tentang cara mengurai.

Cara yang benar untuk mengurai momen masukan sebagai UTC dalam format yang Anda berikan adalah seperti ini:

moment.utc('07-18-2013', 'MM-DD-YYYY')

Lihat dokumentasi ini .

Jika Anda ingin memformatnya secara berbeda untuk keluaran, Anda akan melakukan ini:

moment.utc('07-18-2013', 'MM-DD-YYYY').format('YYYY-MM-DD')

Anda tidak perlu menelepon toStringsecara eksplisit.

Perhatikan bahwa sangat penting untuk memberikan format masukan. Tanpanya, tanggal seperti 01-04-2013mungkin akan diproses sebagai 4 Jan atau 1 April, tergantung pada pengaturan budaya browser.

Matt Johnson-Pint
sumber
Hanya untuk belajar, di konsol: moment.utc ('2013-07-18 0:00 +0100', 'YYYY-MM-DD HH: mm') memberi saya "2013-07-18 0:00 +0100 " Tapi apa yang ditampilkan di jsfiddle saat dijalankan berbeda yaitu: Kam 25 Juli 2013 01:00:00 GMT + 0100 Perhatikan 01:00:00 . Terima kasih.
brg
Mengeluarkan mentah momentdi konsol tidak terlalu berguna. Anda mungkin melihat salah satu properti internalnya. Anda harus memformatnya sebelum memeriksa hasilnya. Misalnya moment.utc().format()atau moment().format().
Matt Johnson-Pint
Tanggal dan momen akan mengurai string input di zona waktu lokal browser secara default. Saya di EDT sekarang. new Date('2010-12-12')memberi saya Date {Sat Dec 11 2010 19:00:00 GMT-0500 (Eastern Daylight Time)}di FF 38.0.5. Hanya untuk mengontekstualisasikan apa arti "dalam waktu lokal", tepatnya - dalam hal ini, tampaknya berarti, " Dateakan menganggap string tanpa zona waktu dalam UTC dan akan mengurai ke waktu lokal." d.getUTCDate()= 12dan d.getDate()=11
ruffin
1
Ya, ada beberapa pengecualian. ES5 (browser terbaru) akan menafsirkan tanggal dengan tanda hubung sebagai UTC, tetapi hampir semua hal lainnya diartikan sebagai waktu lokal. ES6 mengubah perilaku ini untuk menafsirkan string yang sama sebagai waktu lokal. Saya memperbarui jawabannya.
Matt Johnson-Pint
Ha, ya, baru saja menemukan ini di MDN yang mengatakan dengan tepat ( '2012-12-12'adalah UTC b / c itu dalam format ISO, tetapi 'December 12, 2012'dan bahkan '2012/12/12'diuraikan dengan zona waktu lokal di ES5), tetapi Anda telah mengalahkan saya untuk itu. Saking hebatnya ES6 membuat semuanya lokal [katanya sinis]. Tanggal itu
menyebalkan