NHN Cloud NHN Cloud Meetup!

JavaScriptでタイムゾーンの処理(1)

先日、JavaScriptのカレンダーライブラリにタイムゾーン機能を追加する作業を経験しました。JavaScriptのタイムゾーン対応が他の言語に比べて劣っているという話はすでに聞いていましたが、既存のデータオブジェクトをうまく抽象化すれば難なく達成できると考えていました。
しかし、作業を進めるうち、JavaScriptでタイムゾーンを扱うのが決して容易ではないということに気づきました。特に時間情報を単にフォーマットする程度ではなく、カレンダーのように時間情報に対して複雑な演算が加わることになれば、タイムゾーンはますます悩みの種になります。
今回は、これらの経験をもとに、JavaScriptでタイムゾーンを処理する際の問題と解決方法を紹介したいと思います。記事を2つに分けて、1部ではタイムゾーンと関連基準について、2部では本格的にJavaScriptと関連した内容について、詳しく説明していきたいと思います。

タイムゾーンとは?

タイムゾーンは同じローカル時間に従う地域を意味し、主に国によって法的に指定されます。通常は国別にそれぞれ固有のタイムゾーンを使用しており、米国やカナダのように面積の広い国である場合、地域別に異なるタイムゾーンを使用することもあります。(一方、中国はその広い面積にもかかわらず1つのタイムゾーンを利用しています。)

GMT、UTCとのオフセット(Offset)

GMT

韓国のタイムゾーンは、通常、GMT+09:00と表現されます。GMTはGreenwich Mean Timeの略で経度0度に位置する英国グリニッジ天文台を基準とする太陽時間を意味します。GMT時間は1925年2月5日から使用を開始し、1972年1月1日まで世界標準時として使用されました。

UTC

GMTはUTCとも呼ばれ、両方の用語が混在して使われていますが、厳密には別の意味を持ちます。UTCは地球自転周期の流れが遅延している問題を解決するため、1972年にセシウム原子の振動数に基づく国際原子を基準に再指定された時間帯です。つまり、UTCはより正確な時間測定のためGMTに代わり制定された新規格で、両者間の時間差は非常に微細なものの、ソフトウェアで使用する際はUTCがより正確な表現です。

UTCという略字が誕生した背景には面白いエピソードがあります。当初、英語圏ではCUT(Coordinated Universal Time)を、フランス語圏ではTUC(Temps Universel Coordonn)の使用を希望していました。しかし最終的に一方が勝利することはできず、両方の言語とも、C、T、Uが入っているため、仲裁案としてUTCが採用されたといいます。

オフセット

UTC+09:00をサンプルにして説明しよう。「+09:00」の意味は、UTCの基準時間より9時間早いという意味です。つまりUTC基準で現在正午なら、韓国時間では午後9時になるでしょう。このようにUTCとの差を示したものをオフセットと呼び、+09:00、もしくは-03:00などのように表現されます。

一般的には国や地域ごとに使用されているタイムゾーンには固有名詞を付与します。例えば、大韓民国のタイムゾーンはKST(Korea Standard Time)とも呼ばれますが、これは前述したように特定のオフセットを指すので、KST = UTC+09:00だと理解すればよいでしょう。しかし、+09:00オフセットは韓国だけでなく、日本、インドネシアなどさまざまな地域でも使用されています。オフセットとタイムゾーンの名称との関係は1:1ではなく、1:Nです。+09:00を使用している国、地域のリストは、UTC+09:00から確認できます。

オフセットは1時間単位ではないこともあります。例えば、北朝鮮は+08:30を基準時として使用しており、オーストラリアでは地域によって+08:45、もしくは+09:30を基準時として利用しています。

全体のUTCオフセットや名称のリストは、List of UTC Time offsetsから確認できます。

タイムゾーン!=オフセット?

前述のとおりタイムゾーンは、KST、JST(Japan Standard Time)などのように、オフセットと同一の意味として使われます。しかし下記の理由から、特定の地域のタイムゾーンを単にオフセットと呼ぶことはできません。

サマータイム(DST)

国内ではまだ馴染みのない概念ですが、海外の多くの国では、夏時間(サマータイム)が存在します。サマータイムは主に米国、ヨーロッパで使われる用語ですが、より汎用的な用語では日光時間節約、DST(Daylight Saving Time)と呼ばれます。これは夏季に標準時を元々の時間より1時間繰り上げて利用することを意味します。

例えば、米国カリフォルニア州の地域は、普段はPST(Pacific Standard Time)を基準時に利用して、夏期はPDT(Pacific Daylight Time、UTC-07:00)を基準時として使用します。2つの時間帯を使用するタイムゾーンをまとめてPacific Time(PT)とも呼び、米国の多くの地域およびカナダの一部の地域でも使用しています。

それではDSTが適用される夏期は正確にいつを意味するのでしょうか?実際のところDSTの適用には普遍的なルールがあるわけではなく、国家や地域の法律に従い異なって適用されます。例えば米国とカナダでは、2006年までは各地域の時間基準で4月の最初の日曜日の午前2時に始まり、10月の最後の日曜日の午前12時に解除していましたが、2007年からは3月の第二日曜日の午前2時に開始し、11月の最初の日曜日の2時に解除するように変更されました。ヨーロッパではDSTが一括適用されるのに対し、米国では各地域の時間帯ごとに順次適用されます。

タイムゾーンは変わる?

各地域がどのようなタイムゾーンを利用するかは地域や国が法的に決定するため、政治的または経済的理由によって変更されることがあります。例えば、米国で2007年からDSTの適用時点が変更されたのは、2005年にジョージ・ブッシュ大統領が署名したエネルギー政策法によってでした。またエジプトやロシアは、既存のDSTを用いたが、2011年からはDSTを利用しないことを決定しました。

DSTのみならず、国の標準時が変更されることもあります。例えばサモア島の場合、元々UTC-10:00のオフセットを基準時として使用しましたが、2011年にオーストラリア、ニュージーランドとの貿易で日付の差による損失を低減するため、UTC+14:00に変更して1日を繰り上げました。これにより2011年12月30日が消えてしまい、大きなニュースになりました。(ニュースリンク
オランダの場合は、1909年から+0:19:32.13という必要以上に正確なオフセットを使用していましたが、1937年から+00:20に変更し、その後1940年からは+01:00を基準時として使用しています。

タイムゾーン1:オフセットN

上記の内容をまとめると、ある地域のタイムゾーンは1つ、あるいはそれ以上のオフセットを持ち、どの時点でどのようなオフセットを基準時として利用するかは、当該地域の政治/経済状況によって変わり続けます。

日常生活では、このような状況が大きな問題にはならないかもしれませんが、これをルールに基づいてシステム化させようとすると問題が発生します。スマートフォンの基準時をオフセットを使って指定するとしましょう。もし私がDSTが適用される地域に住んでいる場合、スマートフォンの基準時をDSTが適用、または解除されるたびに変更しなければなりません。このような場合、Pacific Timeのように標準時とDSTを結びつけて1つのタイムゾーンとして認識できる別の概念が必要でしょう。

しかし、これを単にいくつかのルールで指定することは難しいでしょう。例えば米国の場合、2007年を基準にDSTの適用時点が変更されたため、2006年3月31日はPDT(-07:00)が基準時となりますが、2007年3月31日はPST(-08:00)が基準時にならなければなりません。つまり、特定の地域のタイムゾーンを指すためには、標準時間帯あるいはDST適用ルールがいつ変更されたか、歴史的なデータをすべて取得する必要があります。つまり、私たちは「ニューヨークのタイムゾーンはPST(-08:00)だ」と言うことができません。もう少し正確に言うと「ニューヨークのタイムゾーンは、現在のPSTだ」となるでしょう。システムの観点からより正確に言えば、タイムゾーンという言葉ではなく「ニューヨークは現在PSTを基準時に使用している」と言うのが正しいです。

それでは、特定の地域のタイムゾーンはオフセットではなく何で指すべきでしょうか?それは、まさしく地域名です。正確に言えば「歴史的に標準時間帯やDSTの変更が等しく適用された地域」を1つのタイムゾーンとして指すことができるでしょう。PT(Pacific Time)と同じ名称が使用されることがありますが、これは現在の標準時とDSTだけをまとめた概念であるため、歴史的な変更履歴をすべて含むとは言えないでしょう。またPTは、米国/カナダ地域でのみ使用されている名称で、ソフトウェアで汎用的に使用するためには、信頼できる機関で管理されている標準が必要になります。

IANA time zone database

実際、タイムゾーンの標準は単にルールというよりもデータベースに近いようです。それは歴史的な変更履歴をすべて保存しておく必要があるためです。このような標準的なデータベースは複数ありますが、現在最も信頼性の高い標準は、IANA time zone databaseである。IANA time zone databaseは、一般的にtz database(あるいはtzdata)と呼ばれ、全世界のすべての地域の標準時とDSTの変更内容が入っています。現在、歴史的に確認できるすべてのデータが格納して、UNIX時間(1970.01.01 00:00:00)以降のデータの精度を確保するためにまとめられました。(1970年以前のデータもありますが、この時期のデータは完璧な正確性を保証するものではありません。)

名称はArea/Locationのルールを利用しますが、Areaは通常、大陸や大洋名(Asia、America、Pacificなど)を指定し、Locationは主に大規模な都市名(Seoul、New_Yorkなど)で指定されます。例えば大韓民国のタイムゾーンはAsia/Seoulで、日本の場合はAsia/Tokyoであるが、現在、両地域ともUTC+09:00を標準時として使用しています。しかし実際の歴史的な変更履歴が異なり、他国に属しているため、別のタイムゾーンで管理されています。

IANA time zone databaseは、多くの開発者や歴史学者のコミュニティによって管理されています。歴史的発見が追加されたり、政府の政策に変更がある場合は直ちに更新されるため、信頼性が最も高いです。またLinux、macOSなどのUnixベースのOSやJava、PHP等の有名プログラミング言語など、すでに多くのシステムでは、このデータベースを内部的に使用しています。

上記の対応環境リストに、Windowsがないことに気づかれた方がいるでしょう。実のところWindowsは、別のデータベースを内蔵しており、これをMicrosoft Time Zone Databaseと呼びます。しかし、ここには歴史的な変更履歴が不足している部分が多く、Microsoftという会社によってのみ管理されることもあり、正確性や信頼性の面ではIANAに比べ劣ると考えられます。

JavaScriptとIANA time zone database

冒頭でも言及しましたが、JavaScriptはタイムゾーン対応が微弱です。基本的には現在の地域(より正確に言えば、インストールされたOSに設定されたタイムゾーン)の影響を受けているので、明示的にタイムゾーンを変更できる方法がありません。それだけではなく、データベースの標準仕様も明確ではなく、実際のES2015のスペックをみると、ローカルタイムゾーンとDSTの適用について2~3行程度の曖昧な定義だけがあります。例えばDSTの場合、次のように定義されています。リンク:ECMAScript 2015 – Daylight Saving Time Adjustment

An implementation dependent algorithm using best available information on time zones to determine the local daylight saving time adjustment DaylightSavingTA(t), measured in milliseconds. An implementation of ECMAScript is expected to make its best effort to determine the local daylight saving time adjustment.

つまり実装するブラウザのベンダーごとに実装が異なる可能性があるということです。

NOTE:It is recommended that implementations use the time zone information of the IANA Time Zone Database http://www.iana.org/time-zones/.

結論としてJavaScriptでは、事実上明示された標準的なデータベースがなく、ただIANA Time Zone Databaseを推奨しているだけです。当然ですがこれによって実際にブラウザごとにタイムゾーン演算が異なる動作をする場合もあります。後日、国際化API向けのECMA-402スペックにおいて、Intl.DateTimeFormatでIANA timezoneを使用するオプションが追加されましたが、他の言語に比べると信頼できるタイムゾーンの対応が非常に不足していると考えられます。

1部まとめ

これまで見てきたように、タイムゾーンは思ったより複雑な概念で、これを正確に計算するためには、単純な演算だけでなく、標準化されたデータベースが必要です。これらの作業を言語レベルのサポートなしで実装するのは容易ではなく、残念ながらJavaScriptはこれらの対応が大幅に少ない状態です。2部では、実際にJavaScriptでタイムゾーンを処理する際に発生する問題と、その解決方法について紹介したいと思います。

参考リンク

NHN Cloud Meetup 編集部

NHN Cloudの技術ナレッジやお得なイベント情報を発信していきます
pagetop