Date.parseの結果が正しくないのはなぜですか?

Case One:

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

Output:

Fri Jul 08 2005 00:00:00 GMT-0700 (PST)

ケース2

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

アウトプット

Thu Jul 07 2005 17:00:00 GMT-0700 (PST)


2回目のパースが正しくないのはなぜ?

ソリューション

第5版の仕様が発表されるまで、Date.parseメソッドは完全に実装依存でした(new Date(string)は、Date.parse(string)と同等ですが、後者はDateではなく数値を返します)。第5版仕様では、simplified (and slightly incorrect) ISO-8601をサポートする要件が追加されました(What are valid Date Time Strings in JavaScript?も参照)。しかし、それ以外には、「Date.parse」や「new Date(string)」が何を受け入れるべきかについては、「Date#toString*」の出力を何でも受け入れなければならないということ以外には、何の要求もありませんでした(それが何であるかについては言及していません)。

ECMAScript 2017(第8版)では、実装はDate#toStringDate#toUTCStringの出力を解析することが求められましたが、それらの文字列のフォーマットは指定されませんでした。

ECMAScript 2019(第9版)では、[Date#toString][3]および[Date#toUTCString][4]の形式が(それぞれ)指定されています。

  1. ddd MMM DD YYYY HH:mm:ss ZZ [(タイムゾーン名)]
    例: Tue Jul 10 2018 18:39:58 GMT+0530 (IST) 2.ddd, DD MMM YYYY HH:mm:ss Z
    e.g. Tue 10 Jul 2018 13:09:58 GMT

は、新しい実装でDate.parseが確実に解析できるように、さらに2つのフォーマットを提供しています(ただし、どこでもサポートされているわけではなく、準拠していない実装はしばらくの間使われ続けることに注意してください)。

曖昧さを避けるため、日付文字列は手動で解析し、Dateコンストラクタに年、月、日の引数を指定して使用することをお勧めします。

// parse a date in yyyy-mm-dd format
function parseDate(input) {
  var 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
}

[3]: http://ecma-international.org/ecma-262/9.0/#sec-date.prototype.tostring [4]: http://ecma-international.org/ecma-262/9.0/#sec-date.prototype.toutcstring

解説 (8)

この問題にはいくつかの方法があります。一般的なルールとして、ブラウザは日付をISO-8601として解釈できる場合はそうします。 "2005-07-08"はこれに該当するため、UTCとして解析されます。 "Jul 8, 2005"はできないため、ローカル時間で解析されます。

詳しくはJavaScriptと日付、なんてこった!をご覧ください。

解説 (1)

文字列をparseメソッドに渡すことは一般的に安全ではないというCMSは正しい一方で、新しいECMA-262 5th Edition(通称ES5)の仕様では、15.9.4.2節で、Date.parse()が実際にISO形式の日付を扱うべきであることを示唆しています。 旧仕様ではそのような主張はありませんでした。 もちろん、古いブラウザや一部の現行ブラウザでは、このES5の機能はまだ提供されていません。

2つ目の例は間違っていません。 Date.prototype.toISOString()`が意味するように、指定された日付はUTCですが、ローカルのタイムゾーンで表現されています。

解説 (2)