日付と時間の国際規格【ISO8601】を理解する
World Wideなアプリケーション開発でしばしば問題となるのが時刻である。各国に時差が存在するおかげで、ある日時範囲のデータを取得したいために日時指定してもアメリカ時刻として解釈されて正しい値が返って来ないみたいな問題が稀に起こる。
本ページでは日付と時刻を包括的に理解し、日付に関するトラブルを少しでも緩和できるように取り巻く状況を整理する。またISO 8601フォーマットをJavaScriptのnew Date()でパースしてみて実際の挙動を理解する。
時刻に関する用語の整理
時刻の話は用語を抑えて行いと容易に会議などで放っておかれてしまう。それぞれ誤解も多いのできっちり整理を付けておきたい。
- GMT: Greenwich mean time,グリニッジ標準時・・・イギリスの標準時間のこと、これは社会科などで習った事がある人もいるかもしれない。日本はそれより9時間早いのでGMT+0900、夏時間のアメリカ東海岸はそれより7時間遅いのでGMT-0700と表す。
- UTC: Universal Time Corordinated,協定世界時 ・・・イギリスの標準時間のこと、GMTと同様日本はUTC+0900、夏時間のアメリカ東海岸はUTC-0700と表す。UTCはGMTに微調整が加えられたものだが、ほぼ同じと考えて良い。ソフトウェアではGMTよりUTCが基本的に使われる。
- 時間が早い・・・時刻が先に進んでいる状態、イギリスが15日の18時だとすると、日本は16日の3時であり、夏時間のアメリカ東海岸は15日の11時である。ただこれ誤解が多い表現なので使わない方が良い。夏時間に入ると昨日の17:00が今日は18:00になっているわけで、考え方によっては時間が遅いといえる。早い、遅いではなく進んでいる、遅れているで話した方が誤解はない。
- DST・・・Daylight Saving Time、夏時間。夏になると1時間時を進めること。
各国の時刻表記
特にアメリカ西海岸と仕事をする人はソフトウェア関係の中で多いはずなので日本と西海岸だけまとめる。
- JST・・・Japanese Standard Time、日本の標準時、夏時間は日本にはないのでJSTだけである。
- PST ・・・Pacific Standard Time、アメリカ西海岸の標準時、Pacificは太平洋、西海岸は太平洋に面しているからである。ニューヨークなど東海岸はEST
- PDT・・・Pacific Daylight Time、アメリカ西海岸の夏時間. DはDST(Daylight Saving Time)のDである。
RFC 3339
RFCはインターネット技術を議論し、標準化を進める団体であり、そこで定義された文書はRFC○○(番号)として閲覧可能になる。RFC 3339はインターネット上の時刻に取り扱いを定めた仕様であり、この中でISO 8601が使われている。
ISOはInternational Organization for Standardizationの略で、国際標準化機構と日本語では呼ばれている。スイスのジュネーブに本部をおく。ISO 8601は国際的な時刻の標準化である。ちなみに文書を入手するには158フラン(18000円)程度かかる。
RFC 3339はISO 8601をベースとして作られたインターネット時刻規格ということができる。使用される場所としては、サーバのレスポンスバリューとして含まれることが非常に多い。この中身を確認していこう。
ISO 8601の時刻表記
RFC 3339ではISO 8601の一部を仕様として用いていると述べたが、その仕様は
https://tools.ietf.org/html/rfc3339#section-5.6
に述べられている。
完全なdate-timeである2020-07-12T14:00:40+09:00
を例に話す
- 時刻はdate(日付)とtime(時間)からなり、Tで区切られる
- 時差は+09:00,-0800などUTCより進んでいる場合は+で、遅れている場合は-で表す、+09:00の代わりにZが付くとUTC時刻となる
- 時間はHH:MM:SSで表されるが、小数点も定められる、HH:MM:SS.fffなどと表記される。1*DISITとあり、これは小数点の数は1つ以上であることを表す。HH:MM:SS.fでも時刻表記として正しいし、HH:MM:MM.ffffffffffでも正しい。パースする方は何が来ても大丈夫なようにする必要がある。
JavaScriptでのISO 8601形式のパース
Dateオブジェクトの関数を使えばパースできるので、どのような時刻が表示されるかいろいろと最後に試してみる。Chromeで実験
通常
new Date("2020-07-12T14:00:40+09:00");
Sun Jul 12 2020 14:00:40 GMT+0900 (日本標準時)
UTC時刻
new Date("2020-07-12T14:00:40Z");
Sun Jul 12 2020 23:00:40 GMT+0900 (日本標準時)
日本時間では23時である。正しく反映されている。
ミリ秒
new Date("2020-07-12T14:00:40.150+0900");
Sun Jul 12 2020 14:00:40 GMT+0900 (日本標準時)
new Date("2020-07-12T14:00:40.150+0900").getTime();
1594530040150
表記上は秒までしか出せないため、違いは分からないが、getTime()で1970年1月1日(UTC)からのミリ秒を表示するとちゃんと違いが分かる。
うるう秒判定
new Date("2012-07-01T23:59:60Z");
Invalid Date
これは。。ISO 8601の仕様ではtime-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second
と表記されており、問題なくパースできると思ったがダメであった。Chromeブラウザの実装漏れじゃないかと、と思ったらFireFoxもIEもダメ。あかんやん。クライアント側はサーバ側がうるう秒を返してこないか場合によっては確認する必要はありそうだ。
仕様外の値を入れてみる。まず時差表記が無いケース
new Date("2020-07-12T14:00:40");
Sun Jul 12 2020 23:00:40 GMT+0900 (日本標準時)
勝手に日本だと判断された、ブラウザの国設定依存か。違うな、ブラウザでは言語は変えられても国は変えられない。という事はWindowsの設定か、という事でWindowsの設定を西海岸のPDTに変更して再度実験。日付と時刻の設定から変更できる。
そして実験結果
new Date("2020-07-12T14:00:40");
Sun Jul 12 2020 14:00:40 GMT-0700 (アメリカ太平洋夏時間)
案の定アプリ側(Chrome/IE/FireFox)ではなくOS(Windows)側の設定に依存。
今度は日付のみ
new Date("2020-07-12");
Sun Jul 12 2020 09:00:00 GMT+0900 (日本標準時)
今度も日本だと判定されると思いやUTC時刻で判定された、一貫性のないブラウザ実装
時間に:無し
new Date("2020-07-12T140040+09:00");
Invalid Date
仕様通りに:を入れる必要がありそうだ
時差に:無し
new Date("2020-07-12T14:00:40+0900");
Sun Jul 12 2020 14:00:40 GMT+0900 (日本標準時)
こちらは動いた。う~ん。