結論: メモ:JavaScript で システム時刻から別のタイムゾーンの時刻へ変換 - ねじろぐ @drillbits のとおりにする。
自分で書いた関数 https://gist.github.com/1342852
理屈がわからなかったので調べたり考えたりした。
結論は同じだけど News, Tips, and Advice for Technology Professionals - TechRepublic は説明がもう少し詳しい。あとコメント欄の議論も参考になる。
以下、この記事のステップを追いつつ自分なりにどう考えたかを書いていく。まだ疑問は残るけど・・・(主に時刻とタイムゾーンについての理解不足のため)
- まず前提として、 JavaScript の Date 型では UTC とローカルタイムゾーンにおける現在時刻しか得ることができない
- ひとつの Date インスタンスから直接違うタイムゾーンにおける現在時刻を得ることはできない、ということ
- まずローカルタイムゾーンにおける現在時刻を得る。 getTime() でその時刻を UNIX timestamp に変換する。 UNIX timestamp は UTC の 1970-01-01 00:00:00 からの経過秒数。
- 次にローカルタイムゾーンが UTC に比べて何分進んで・遅れているかを getTimezoneOffset() で得る。単位は分。値がマイナスだったらローカルタイムゾーンは UTC より進んでおり、値がプラスだったらローカルタイムゾーンは UTC より進んでいる。単位をミリ秒に揃えておく。
- 東京は +09:00 なので UTC で 2011-11-06 12:30:00 のとき東京では 2011-11-06 21:30:00 となり9時間進んでいる)
- ホノルルは -10:00 なので UTC で 2011-11-06 12:30:00 のときホノルルでは 2011-11-06 02:30:00 となり10時間遅れている)
- 現在の UTC での時刻を得るために UNIX timestamp にオフセット値を足す(ここがピンとこない!)
- getTime() で UTC 時刻を得られるのだから足さなくていいのでは・・・?と思ってしまう
- klizza さんのコメントによれば、この説明は間違いで、 Date インスタンスは toLocaleString() で文字列表現を得るとき内部で自動的にローカルタイムゾーンを考慮して時間を足したり引いたりする。ので、それを見越してここで先に足したり引いたりしておくことでそれを「キャンセル」する目的なのだと思う。だから UTC での時刻を得るために〜、という説明は誤っている。
- 得られた UTC での時刻に、目的のタイムゾーンのオフセット値を足す(ここもピンとこない!)例えばインドのボンベイは +05:00 なので 5.5 時間すすめる。得られるのはやはり UNIX timestamp
- 前のステップが納得できてないので当然ここもわからない
- klizza さんのコメントをみて納得できたことにする・・・
- UNIX timestamp の形だと読みづらいので new Date() で新しいインスタンスを作り toLocaleString() で目的の時刻の文字列表現を得る
- toLocaleString() はあくまでローカルタイムゾーンでの時刻表現を得るのだからここで使ってる UNIX timestamp は本当にボンベイの時刻を表してるわけじゃないよね・・・?
- ともかくこれで欲しい時刻は得られた
・・・ということなんだけどやっぱり納得できない。うまく言えないけど狐につままれたような感じ。理屈がわかってないのでたぶん覚えられないだろうし。
コメント欄では「タイムゾーンオフセットが一定じゃない地域もあるよ」とか「夏時間はどうするの?」とか、いまその話はしてないだろ的な重箱の隅をつつかれてるのが多い印象だけど、長文書いてるひととそれへの返信は読み応えがあった。いずれも7割くらいしか読解できないのが悲しい。で、 klizza さんのコメントを読み込むと彼の指摘はおれの疑問をだいたい解決してくれたんだけどそのあとについてるコメントを読むとまた少し揺らぐ・・・。 klizza さんのコメントがやはりまっとうに思える。これらの処理で得てる時刻は「現在時刻の別タイムゾーンにおける時刻」ではなくて、別の時刻を作っている。タイムゾーンの部分を無視して年月日時分秒だけみると一見つじつまがあっている、ということ(というかそれ以外に方法がない)
もうちょっとちゃんとまとめられるはずだけど、もう一週間くらいずっと書きたいけど書けないままになってて気持ち悪いのでさっさと書いてすっきりしてしまおう。
正攻法でやるなら Olson のタイムゾーンデータを利用する timezone-js というのがあるけどまぁ大掛かりかな・・・。