JavaScriptのDateクラスでのISO8601とUNIXタイムスタンプについて

YoutubeFlickrAPIから受け取ったJSONを扱っててちょっとつまづきました。動画や写真の投稿日時に、YoutubeではISO8601が、FlickrではUNIXタイムスタンプが使われていて、そのままDateクラスに渡したところInvalid Dateとなりました。

解決方法

ISO8601
new Date('2009-07-31T15:22:16.000Z');

実はFirefoxChromeでは動いてました。が、SafariではInvalid Dateとなってしまいました。ISO8601としては割と標準的な記述だと思うんですが、Safariでは対応してないようです。

ISO8601な記述を受けてDateやDateを継承したオブジェクトを返すライブラリがありました。今回はISO8601な文字列を扱うのは1回だけだったし、Dateオブジェクトを返してくれる方が良いかなと思ったので、ISO8601.jsを使いました。

UNIXタイムスタンプ
new Date( 1249053736 * 1000 );

JavaScriptは時間をミリ秒単位で扱っているらしく、UNIXタイムスタンプをミリ秒にしたものなら、通りました。

解説

ISO8601
var dobs = [
  "1975-10-27T00:00:00Z",
  "1975-10-27T00:00:00+09:00",
  "1975-10-27T00:00:00",
  "1975-10-27 00:00:00",
  "1975/10/27T00:00:00Z",
  "1975/10/27T00:00:00+09:00",
  "1975/10/27T00:00:00",
  "1975/10/27 00:00:00"
];
for (var idx in dobs) {
  if (typeof dobs[idx] === 'string') {
    var dob = new Date(dobs[idx]);
    console.log(dob.toString());  }
}
Dateオブジェクトの挙動の違い - ペイパー・プログラマーズ・ダイアリー

色々と調べたところ、詳しく挙動を調査してるエントリーがありました。上記の結果はリンク先に書いてありますが、ISO8601的な記述はSafariではまったくだめ。FirefoxChromeも完璧ではないようです。まぁISO8601って表記にかなり自由度があるししかたないのかも。

では方言としてのJavaScriptに対する標準語であるところのECMAScriptの仕様はどうなってるのか見てみます。現時点の最新は5th edition

15.9.1.15 Date Time String Format
ECMAScript defines a string interchange format for date-times based upon a simplification of the ISO 8601 Extended Format. The format is as follows: YYYY-MM-DDTHH:mm:ss.sssZ

ISO 8601 拡張フォーマットをベースに定義されているようですね。この観点からするとGoogle Chromeが「"YYYY-MM-DDThh:mm:ss±hh:mm"」を解釈してくれないのは残念ですね。この形式を解釈してくれるのはFirefoxだけです。

Dateオブジェクトの挙動の違い - ペイパー・プログラマーズ・ダイアリー

おなじエントリーからですが、ECMAScriptの仕様ではISO8601が読めてもおかしくなさそうなので、Safariが対応不足って事なのかな。ISO8601拡張フォーマットってのがなんだかよくわからないけど。

そもそもJavaScriptは"new Date(dateString)"におけるdateStringについて

dateString 日時を表す文字列。この文字列は parse メソッドで認識される書式である必要があります。

Date - JavaScript | MDN

とされてるよう。
じゃあ"parse メソッドで認識される書式"とはなんぞやというと

与えられた日時を表す文字列に対し、parse は時刻の値を返します。このメソッドは IETF 標準日付構文 "Mon, 25 Dec 1995 13:30:00 GMT" を受け付けます。また、アメリカ大陸のタイムゾーンの省略形は理解しますが、一般的な利用では例えば "Mon, 25 Dec 1995 13:30:00 GMT+0430" (グリニッジ標準時より 4 時間 30 分東) というようにタイムゾーンのオフセットを使ってください。タイムゾーンを指定しなかった場合、地方時のタイムゾーンと仮定します。GMTUTC は同じとみなされます。

Date.parse() - JavaScript | MDN

IETF 標準日付構文かぁー。あまり馴染みないですよね、とくに日本なら。

Dateオブジェクトの挙動の違い - ペイパー・プログラマーズ・ダイアリー

さらに同じところのエントリーからですが、Mozillaの情報ではparse()で認識される書式という事で、IETF標準日付構文というのがその書式らしいです。Firefoxnew Date().toString()とかすると出てくる文字列ですね。これも引用元に調査結果がありますが、こちらは概ねサポートされてるみたいです。

W3C-DTF

国際標準化機構(ISO)においてISO 8601:1988[ISO8601](翻訳版がJIS X 0301-1992)が定められました。ただ、この標準はかなり多くのバリエーションを定義している上、西暦年の上2桁の省略を認めているため、あまり使い勝手が良くありません。そこで、IS0 8601のサブセットとして、日時の表記方法を限定し、よりシンプルにしたものがW3Cからノート[W3CDTF]として公開されました。

日付の表記に関するノート

ISO8601の書き方にバリエーションがありすぎるといった様な事から、ISO8601の一部を使ってW3C-DTFという仕様が出てるようです。見た目はISO8601だけど、ISO8601で許される一部の書き方が駄目になってるって事ですね。

Invalid Date
new Date( 'foo' ) === 'Invalid Date'

Dateクラスが解釈できない引数でnewするとInvalid Dateという文字列が返ってきました。とりあえず手元のブラウザでは大体そうだったし、検索して回ってもそれであってるっぽいです。でも、Dateオブジェクトの生成失敗をこれで判別しても良いんだろうか?一応try catchで例外が取れないかなとも思ったんですが、取れなかったし、例外出てればcatchしてなくてもコンソールに出ますよね。if ( new Date( 'foo' ) !== 'Invalid Date' )みたいなのが、どのブラウザでもちゃんと動くのかちょっと心配。