JavaScriptのプログラムに渡す時刻の文字列の形式は何が良いか

[最終更新] 2018年9月12日

発端は、d=”2018-09-11 10:00:00″というような形式の文字列をjsのプログラムに渡したら、new Date(d)で一部環境(iPhone + Chrome)ではNaNが返ったこと。

それからあれこれ調べて、JavaScriptのプログラムに文字列で時刻を渡すのであれば、”2018-09-11T10:00:00+09:00″というフォーマットが無難かなぁと。JavaScriptと書いたけれど、ISO準拠なので、他のケースでも考え方は同じかと思う。

キーワード: ISO8601拡張形式, RFC3339, ECMAScript

スポンサーリンク

時刻の文字列のフォーマットは何が良い?

アプリケーション側のjsでAPIを叩いて、jsonで時刻”YYYY-mm-dd HH:MM:SS”を受け取り、受け取った文字列dをnew Date(d)したところ、PCでは問題なかったが、iPhone + ChromeでgetHours()やらしてもNaNが返った。「chrome safariでnew Date()したら中身がNaNになってしまう。 | 初めてのレンタルサーバー.NET」の記事によると、iPhone + Chrome/Safariでは”YYYY-mm-dd HH:MM:SS”ではだめで、しかし”YYYY/mm/dd HH:MM:SS”では問題ないという。

自分はAPI側を実装していて、大元の時刻情報をUNIXタイムスタンプで保持していたから、アプリケーション側に渡すフォーマットはなんでもよかった。”YYYY-mm-dd HH:MM:SS”にしたのは、まぁ馴染みがあるかな、くらいで深く考えてはいなかった。けれど、こうして問題が生じたので、一度ちゃんと考えないとなぁ、と。

以下、JavaScriptに渡す時刻を表現した文字列は、どんなフォーマットがよいのか、調べてつらつらと考えてみる。

ISO8601拡張形式が無難

今回JavaScriptで受け取った文字列は、new Date(d)でDate型に変換していた。まぁだいたいそうするものと思われる。このnew Date(d)は何するものぞ、ということで、ECMAScript 2015を見る(「https://www.ecma-international.org/ecma-262/6.0/#sec-date-constructor」)。

20.3.2.2 Date( value )節を見ると、valueが文字列の時はparseメソッドを呼ぶらしい。で、20.3.3.2 Date.parse( string )節によると、20.3.1.16節で定義した Date Time String Formatに則って文字列を解釈するのだと。で、ようやく「ECMAScript 2015 Language Specification – ECMA-262 6th Edition」にたどり着くと、以下に則っているそうな。

YYYY-mm-ddTHH:MM:SS.sssZ

sssはミリ秒、Zはタイムゾーンだ(UTCはZでよく、それ以外は、オフセットを+-HH:MMで表現する)。たとえば日本時間の2018年9月11日13時32分だと、2018-09-11T13:32:00.000+09:00と表現される。これは日時のフォーマットについて定めたISO8601拡張形式に準じている。

なので、「JavaScriptに渡す時刻を表現した文字列は、どんなフォーマットがよいのか」に対する答えは、このフォーマットで良いだろう。

ちなみにけっこう色々と省略できるみたいだ。

  • 日付
    • YYYY
    • YYYY-mm
    • YYYY-mm-dd
  • 時刻
    • 日付との区切りは”T”
    • HH:MM
    • HH:MM:SS
    • HH:MM:SS.sss
  • タイムゾーン
    • オプション(なくてもよい)
    • ±HH:MM

以上を組み合わせて、日付オンリーまたは日付T時刻タイムゾーンとなっていればよい。YYYY-mm-ddとか、知らず知らずのうちにES準拠になっていたパターンはけっこうあるんじゃなかろうか(参考「Date.parseとタイムゾーン | メモログ」YYYY-mm-ddとYYYY/mm/ddで挙動が異なることについての考察記事)。

適宜省略したほうが人間には優しいが、読み取るのはコンピュータであるし、特にタイムゾーンの省略は不安なので、省略しないのが一番安全かなと思う。でもミリ秒くらいは省略してもいいかもなぁ(後述のRFC3339)……000ってつけるのケッタイだし。。。すると、結論的には以下となる。

YYYY-mm-ddTHH:MM:SSZ

なお、Date型に変換した後、普通にgetHours()とかするとLT(ローカルタイム)が適用される。UTCとLT以外で時刻を取得するには、オフセットかますとか一工夫いるそうな。それだったらUTCで取得していじったほうが早いかもしれない。

それ以外のフォーマットのとき

ESの形式から外れた時のことについて。まさに今回の発端がそうだったわけだが、標準から外れると環境依存が生じる。文字列の解釈がブラウザに任されるからだ。「Dateオブジェクト (日付と時刻) | JavaScript プログラミング解説」の記事では色々と試してくれていて、これを見るだけでもけっこうカオス。まぁ標準通りがいいんだね、と。IE8がISO8601拡張形式に対応していないようだが、さすがに2018年現在、考慮しなくて良いだろう。

ISO8601とRFC3339

この手の話を調べていると、ISO8601ではなくてRFC3339という呼ばれ方をしているものが散見される。「RFC3339 インターネット上の日付と時間:タイムスタンプ」を読むと、RFC3339は、ISO8601拡張形式をシンプルかつ明示的に再定義したものだ。

文脈的にはほぼ同義のように扱われていることが多いが、RFC3339においてはミリ秒を定義せず、YYYY-mm-ddTHH:MM:SSZの形式であるようだ。自分はこの表記が一番馴染みがある。GPSモジュールから受信する時刻とかだいたいこれだったような(今回調べてみてそういうことだったんだなーと思った)。ちなみに書籍「Web API: The Good Parts」でも、APIで用いる時刻の文字列のフォーマットとしてこのRFC3339を推奨していた。

UNIXタイムスタンプは?

ところで、UNIXタイムスタンプはどうだろうか。今回のケースなんかは特に、元のデータがそうなので、これをそのまま返す、ということも考えられる。

恐らく、Date( timestamp) でも機能的には特に問題が生じないように思う。2038年問題に注意することくらいだろうか。しかし返り値はデバッグ時に人の目でも確認するところであるし、またミリ秒で数えるので、1000倍するのがなにやらケッタイだから、やはりISO8601拡張形式で返すほうが良いと思う。

参考

関連コンテンツ

関連記事

スポンサーリンク

コメントを残す

メールアドレスが公開されることはありません。