Son zamanlarda bir JS tercümanı yazarken, ECMA / JS tarihlerinin iç çalışmaları ile bolca güreştim. Yani, 2 sentimi buraya atacağım. Umarım bu şeyleri paylaşmak, başkalarının tarayıcılar arasındaki tarihleri nasıl ele aldıkları arasındaki farklar hakkında herhangi bir soru sormasına yardımcı olur.
Giriş Tarafı
Tüm uygulamalar, tarih değerlerini dahili olarak 1970-01-01 UTC'den (GMT, UTC ile aynı şeydir) milisaniye (ms) sayısını temsil eden 64 bitlik sayılar olarak depolar. Bu tarih, Java ve UNIX gibi POSIX sistemleri gibi diğer diller tarafından da kullanılan ECMAScript çağıdır. Dönemden sonraki tarihler pozitif sayılardır ve önceki tarihler negatiftir.
Aşağıdaki kod, tüm geçerli tarayıcılarda aynı tarih olarak yorumlanır, ancak yerel saat dilimi uzaklığı ile yorumlanır:
Date.parse('1/1/1970'); // 1 January, 1970
Saat dilimimde (-05: 00 olan EST) sonuç 18000000 olur, çünkü bu 5 saatte kaç ms'dir (gün ışığından yararlanma aylarında sadece 4 saattir). Değer, farklı saat dilimlerinde farklı olacaktır. Bu davranış ECMA-262'de belirtilmiştir, böylece tüm tarayıcılar bunu aynı şekilde yapar.
Giriş dizesi formatlarında büyük tarayıcıların tarih olarak ayrıştıracağı bir miktar farklılık olsa da, ayrıştırma büyük ölçüde uygulamaya bağlı olsa bile, esas olarak bunları saat dilimleri ve gün ışığından yararlanma söz konusu olduğunda aynı şekilde yorumlarlar.
Ancak, ISO 8601 biçimi farklıdır. ECMAScript 2015'te (ed 6) özetlenen, özellikle tüm uygulamalar tarafından aynı şekilde ayrıştırılması gereken iki formattan biridir (diğeri Date.prototype.toString için belirtilen formattır ).
Ancak, ISO 8601 biçim dizeleri için bile, bazı uygulamalar yanlış anlıyor. Bu yanıt ilk olarak 1/1/1970 (dönem) için , tüm uygulamalarda tam olarak aynı değere ayrıştırılması gereken ISO 8601 biçim dizeleri kullanılarak makinemde yazıldığında Chrome ve Firefox'un bir karşılaştırma çıktısıdır :
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
- İlk durumda, "Z" belirteci, girişin UTC saatinde olduğunu, bu nedenle çağdan uzak olmadığını ve sonucun 0 olduğunu belirtir.
- İkinci durumda, "-0500" belirteci girişin GMT-05: 00'da olduğunu ve her iki tarayıcı da girişi -05: 00 saat diliminde olduğunu yorumlar. Bu, UTC değerinin çağdan mahsup edildiği anlamına gelir; bu da tarihin dahili zaman değerine 18000000ms eklenmesi anlamına gelir.
- Herhangi bir belirteç bulunmayan üçüncü durum, ana sistem için yerel olarak ele alınmalıdır . FF, girişi yerel saat olarak doğru bir şekilde ele alırken Chrome, UTC'yi olarak ele alır ve böylece farklı zaman değerleri üretir. Benim için bu saklı değerde 5 saatlik bir fark yaratıyor, ki bu sorunlu. Farklı ofsetleri olan diğer sistemler farklı sonuçlar alacaktır.
Bu fark 2020 itibariyle düzeltildi, ancak ISO 8601 biçim dizelerini ayrıştırırken tarayıcılar arasında başka tuhaflıklar var.
Ama daha da kötüleşiyor. ECMA-262'nin tuhaflığı, ISO 8601 yalnızca tarih biçiminin (YYYY-AA-GG) UTC olarak ayrıştırılması gerektiğinde, ISO 8601'in yerel olarak ayrıştırılması gerekiyor. Burada, zaman dilimi belirticisi olmayan uzun ve kısa ISO tarih formatları ile FF'den çıktı.
Date.parse('1970-01-01T00:00:00'); // 18000000
Date.parse('1970-01-01'); // 0
Dolayısıyla ilki yerel olarak ayrıştırılır, çünkü saat dilimi olmadan ISO 8601 tarih ve saati, ikincisi ise yalnızca ISO 8601 tarihi olduğu için UTC olarak ayrıştırılır.
Bu nedenle, orijinal soruyu doğrudan cevaplamak için "YYYY-MM-DD"
ECMA-262'nin UTC olarak yorumlanması, diğerinin ise yerel olarak yorumlanması gerekir. Bu yüzden:
Bu eşdeğer sonuçlar üretmez:
console.log(new Date(Date.parse("Jul 8, 2005")).toString()); // Local
console.log(new Date(Date.parse("2005-07-08")).toString()); // UTC
Bu yapar:
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08T00:00:00")).toString());
Sonuç olarak, tarih dizelerini ayrıştırmak için bu satır. Tarayıcılar arasında güvenle ayrıştırabileceğiniz SADECE ISO 8601 dizesi, ofseti olan uzun formdur (± HH: mm veya "Z"). Bunu yaparsanız, yerel ve UTC saati arasında güvenle ileri geri gidebilirsiniz.
Bu, tarayıcılarda çalışır (IE9'dan sonra):
console.log(new Date(Date.parse("2005-07-08T00:00:00Z")).toString());
Mevcut tarayıcıların çoğu, sık kullanılan '1/1/1970' (M / D / YYYY) ve '1/1/1970 00:00:00 AM' (M / D / YYYY ss) dahil olmak üzere diğer giriş biçimlerine eşit davranır. : mm: ss ap) biçimleri. Aşağıdaki biçimlerin tümü (sonuncusu hariç) tüm tarayıcılarda yerel saat girişi olarak kabul edilir. Bu kodun çıktısı saat dilimimdeki tüm tarayıcılarda aynı. Son zaman ana bilgisayar saat diliminden bağımsız olarak -05: 00 olarak değerlendirilir, çünkü uzaklık zaman damgasında ayarlanır:
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"));
Bununla birlikte, ECMA-262'de belirtilen formatların bile ayrıştırılması tutarlı olmadığından, yerleşik ayrıştırıcıya asla güvenmemeniz ve dizeleri her zaman manuel olarak ayrıştırmanız, örneğin bir kitaplık kullanmanız ve ayrıştırıcıya format sağlamanız önerilir.
Örneğin moment.js içinde şunları yazabilirsiniz:
let m = moment('1/1/1970', 'M/D/YYYY');
Çıktı Tarafı
Çıktı tarafında, tüm tarayıcılar saat dilimlerini aynı şekilde çevirir, ancak dize biçimlerini farklı şekilde işler. İşte toString
fonksiyonları ve çıktıları. toUTCString
Ve toISOString
fonksiyonları makinemde 05:00 AM çıktı dikkat edin . Ayrıca, saat dilimi adı bir kısaltma olabilir ve farklı uygulamalarda farklı olabilir.
Yazdırmadan önce UTC'den Yerel saate dönüştürür
- toString
- toDateString
- toTimeString
- toLocaleString
- toLocaleDateString
- toLocaleTimeString
Depolanan UTC saatini doğrudan yazdırır
- toUTCString
- toISOString
Chrome'da
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'ta
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
Normalde dize girişi için ISO biçimini kullanmıyorum. Bu biçimi kullanmanın benim için yararlı olduğu tek zaman, tarihlerin dizeler olarak sıralanması gerektiğidir. ISO formatı diğerleri gibi sıralanabilir. Tarayıcılar arası uyumluluğunuz olması gerekiyorsa, saat dilimini belirtin veya uyumlu bir dize biçimi kullanın.
Kod new Date('12/4/2013').toString()
aşağıdaki dahili sözde dönüşümden geçer:
"12/4/2013" -> toUCT -> [storage] -> toLocal -> print "12/4/2013"
Umarım bu cevap yardımcı olmuştur.