JSインタープリターを書いた最近の経験の中で、ECMA / JSの日付の内部の仕組みを十分に取りました。だから、私はここに私の2セントを投入すると思います。うまくいけば、この情報を共有することで、ブラウザ間の日付の処理方法の違いについて疑問を持つ人を助けることができます。
入力側
すべての実装は、1970-01-01 UTC(GMTはUTCと同じ)からのミリ秒(ms)の数を表す64ビットの数値として内部的に日付値を格納します。この日付は、Javaなどの他の言語やUNIXなどのPOSIXシステムでも使用されるECMAScriptエポックです。エポックの後に発生する日付は正の数であり、それ以前の日付は負の数です。
次のコードは、現在のすべてのブラウザで同じ日付として解釈されますが、ローカルタイムゾーンオフセットが含まれます。
Date.parse('1/1/1970'); // 1 January, 1970
私のタイムゾーン(EST、つまり-05:00)では、結果は18000000です。これは、5時間で何ミリ秒なのかです(夏時間の月は4時間しかないため)。値はタイムゾーンによって異なります。この動作はECMA-262で指定されているため、すべてのブラウザーで同じように動作します。
主要なブラウザが日付として解析する入力文字列形式にはいくつかの違いがありますが、解析は主に実装に依存していますが、タイムゾーンと夏時間に関する限り、それらは基本的に同じように解釈されます。
ただし、ISO 8601形式は異なります。ECMAScript 2015(ed 6)で概要が説明されている2つの形式のうちの1つであり、すべての実装で同じ方法で解析する必要があります(もう1つはDate.prototype.toStringに指定された形式です)。
しかし、ISO 8601形式の文字列であっても、一部の実装ではそれが間違っています。ここではISO 8601形式の文字列を使用して私のマシン上でこの答えは、もともと1970年1月1日のために書かれたChromeとFirefoxのの比較出力(エポック)である必要があり、すべての実装に正確に同じ値に解析することは:
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であることを示します。
- 2番目の場合、「-0500」指定子は入力がGMT-05:00であることを示し、両方のブラウザーが入力を-05:00タイムゾーンであると解釈します。これは、UTC値がエポックからオフセットされていることを意味します。つまり、日付の内部時刻値に18000000msが追加されます。
- 指定子がない3番目のケースは、ホストシステムのローカルとして扱う必要があります。FFは入力を現地時間として正しく処理し、Chromeは入力をUTCとして処理するため、異なる時間値が生成されます。私にとっては、これにより、保存された値に5時間の差が生じ、問題が生じます。オフセットが異なる他のシステムでは、異なる結果が得られます。
この違いは2020年の時点で修正されていますが、ISO 8601形式の文字列を解析する場合、ブラウザー間に他の問題があります。
しかし、それはさらに悪化します。ECMA-262の奇妙な点は、ISO 8601の日付のみの形式(YYYY-MM-DD)をUTCとして解析する必要があるのに対し、ISO 8601はローカルとして解析する必要があることです。以下は、タイムゾーン指定子のないISOの長い形式と短い形式のFFからの出力です。
Date.parse('1970-01-01T00:00:00'); // 18000000
Date.parse('1970-01-01'); // 0
したがって、1つ目はタイムゾーンのないISO 8601の日付と時刻であるためローカルとして解析され、2つ目はISO 8601の日付のみであるためUTCとして解析されます。
したがって、元の質問に直接答えるに"YYYY-MM-DD"
は、ECMA-262によってUTCとして解釈される必要があり、もう1つはローカルとして解釈されます。それが理由です:
これは同等の結果を生成しません:
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)フォーマット。次のすべての形式(最後を除く)は、すべてのブラウザーで現地時間入力として扱われます。このコードの出力は、私のタイムゾーンのすべてのブラウザーで同じです。タイムスタンプにオフセットが設定されているため、最後の1つはホストのタイムゾーンに関係なく-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を。また、タイムゾーン名は省略形である場合があり、実装によって異なる場合があります。
印刷前に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"
この回答がお役に立てば幸いです。