エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

18分59秒をめぐって日本標準時の歴史をひもとくことに

この記事は エムスリー Advent Calendar 2018 12日目の記事です。

こんにちは。エムスリー エンジニアリンググループの三浦(@yuba)です。基盤開発チームというところで各サービスチーム共用のシステムの開発保守に携わっており、そこで見つけた面白い動作を掘っていったら意外な知識にたどり着いたという話をいたします。

化けた日付はどこから来た?

あるサービスの管理画面の動作を検証していたときのことです。バリデーションの振る舞いを確かめるためにいくつかテストケースを作りながら実際の動きを試していたところ、不思議な現象を見つけました。

次のように日時入力をするところで年の欄を空のままにして送信したところ⋯

f:id:Sampo:20181210214453p:plain

次のようにおかしな日時が設定されたのです。

f:id:Sampo:20181210214555p:plain

0013年? 18分59秒? 入力した覚えのない数字が3つも紛れ込んでいます。 これが C で書かれたプログラムなら何か不定値を拾ってしまうバグでもあったかと疑うところですが、このサービスはScala製です。案の定動作に再現性もあります。なんらかの出所があって生成されている日時データだということです。では一体どこから来たものなのでしょう?

Joda-Timeの寛容すぎる文字列パース

このサービスは Skinny Framework ベースで作られており、Skinny Framework は日時データを Joda-Time で扱うよう作られています。 このJoda-Time、文字列をパースして日時インスタンスを生成することができるのですがこのパース仕様が非常に寛容です。 どのくらい寛容かというと、"20181201" という文字列をパースさせてもエラーにはせず「裸の整数値は月以下が省略された日時」と解釈して 20181201-01-01 00:00:00 という約2千万年後の時刻を生成してしまうほど。

一方、フレームワークはこの日時入力フィールドのように複数のHTML入力欄からなるフィールドの入力データをいったん一本の文字列に変換した上でバリデーションを通しインスタンス化するという流れで処理します。 数値の組から直接日付時刻インスタンスを生成するのでなく Joda-Time の例の寛容な文字列パースを経ることになります。

ここで0013年の方は解決です。 空文字列の年ごと文字列結合されて"-12-01"となった日付をパースした結果、「日が省略された -12年(=紀元前13年)1月」と解釈されていたというもの。0013というのはAD/BC表示の省略された紀元前13年でした。

18分59秒はどこから来た

では分・秒の位の18と59はどこから来た数字でしょう。 実際にJoda-TimeのDateTimeクラスに "-12-01 13:00:00" をパースさせてみます。するとこんなインスタンスが生成されました。

-0012-01-01T13:00:00.000+09:18:59

時分秒は意図通りにパースされているのにおかしなタイムゾーンオフセットが付いています。 PC環境のデフォルトタイムゾーンはAsia/Tokyoなのに+09:18:59とは一体どこの都市のものでしょう、いや、こんな半端なタイムゾーンオフセット値などあるものでしょうか。どんなに細かくても15分単位だったのでは?

国際子午線会議以前の地方時

+09:18:59という重要なキーワードを手に入れたのでここからウェブ検索が捗り、知らなかったタイムゾーンの歴史に触れていくことになります。

+09:18:59というオフセット値は tz database に収録されていました。

# Zone  NAME            GMTOFF  RULES   FORMAT  [UNTIL]
Zone    Asia/Tokyo      9:18:59 -       LMT     1887 Dec 31 15:00u
                        9:00    Japan   J%sT

https://github.com/eggert/tz/blob/2018g/asia#L1692

1887年の大晦日まではこの数字だったとなっており、なるほど、それなら紀元前13年だってオフセットは+09:18:59であるわけです。 では、1888年に何があったのでしょう。

この年は、日本に標準時が施行された年でした。私達のよく知るUTC+9時間の日本標準時です。 それまでは日本標準時というものはなく、都市ごとに時刻を持っていました。日本に限らず世界中がそうで、米国では100以上の地方時があったと言います。現代人の感覚ではなぜそんな不便なことをと思ってしまいますが、考えてみれば電話のようなリアルタイムの通信手段がない時代には遠隔地間で時計を合わせる術もなければ必要性もなく、むしろ各都市で頭上の太陽と星に時計を合わせるのが最も自然なことだったのでしょうね。

オフセット+09:18:59は東京の地方時でした。 地方時といっても東京の地方時は特別で、太陽暦の基準となるなど日本標準の地位を持っていたようですが。

地方時が乱立する時代を終わらせ標準時を打ち立てるきっかけとなったのは鉄道の発展だったそうです。駅ごとに時刻のオフセットが違うのではまともに時刻表を書けないというのが動機になったのだとか。 1884年に国際子午線会議が開催されて標準時の枠組みが決まり、日本では2年後の1886年に明石を通る東経135度を標準子午線と(=+09:00:00を日本標準時と)決定、2年間の準備期間をおいて1888年にそれが施行されたのでした。

東京の地方時は +09:18:59 だったのか?

日本の暦の歴史については国立天文台がまとめている暦Wikiが大変詳しいです。

さてこのサイトで日本の本初子午線のページを読んでいくのですが、どこにも+09:18:59という数字が出てきません。

東京の標準子午線は当初江戸城天守台上に設定され、一時期赤坂の内務省地理局測量課上に移されてからまた江戸城天守台上に戻されるという経緯を辿り、測量技術の進歩による変化も加わったもののだいたい+09:19:01前後の数字だったとあります。

f:id:Sampo:20181210235028j:plain
江戸城に天守閣はない。明暦の大火で焼けた後の再建が、戦国の象徴である天守閣は時代遅れであるという提言により土台である天守台までで打ち切られたためである。
(ペン太 [CC BY-SA 3.0], 日本語版ウィキペディアより

それでは+09:18:59という数字はどこから来たのでしょう。

この数字の典拠はtz database内に注釈で示されていました。

# 'Tokyo' usually stands for the former location of Tokyo Astronomical
# Observatory: 139° 44' 40.90" E (9h 18m 58.727s), 35° 39' 16.0" N.
# This data is from 'Rika Nenpyou (Chronological Scientific Tables) 1996'
# edited by National Astronomical Observatory of Japan....

139° 44' 40.90" E, 35° 39' 16.0" N の東京天台上だというのです。 暦Wikiと同じく国立天文台がまとめた理科年表が典拠とのことですがこれはどうしたことでしょう、「天守台」と「天文台」違いだったようです。

暦Wikiには法令文書へのリンクまであって間違いはなく、とすると間違っているのはこの1996年版理科年表の方のようです。 入手する機会があったら理科年表の当該記述が何を参考文献にしているのか確かめてみたいですね。

さて、この2秒の間違いについて tz database に修正を依頼して採用されたなどという後日談があればこの記事も大変ドラマチックにまとまっていたのですが、今回の話はここまでとなります。 tz database は歴史的なオフセット情報について収録こそしているもののやはり中心的な関心事とはしていないのです。 そういうわけで実は以前からこの件に気付いていた方もいたものの、修正の話は進まずにいるという状況なのでした。

附記になりますが、Skinny Frameworkの方にはバリデーション処理を修正するプルリクエストを送りマージしてもらえましたので最新のバージョンでは年の省略で紀元前の東京地方時が生成される前にバリデーションに引っかかるようになっております。

まとめ

  • 1888年に日本標準時が施行される前は都市ごとに地方時が刻まれており、当時の東京地方時も tz database に収録されています。天文台と天守台の違いで2秒ほどずれて。
  • 普段は目にすることのない日本標準時施行前の日時もJoda-Timeの寛容すぎる文字列パースによって出現することがあります。油断なりません。

We are hiring!

発見のあるお仕事一緒にしませんか。 TechTalkやカジュアル面談でお会いしましょう。

jobs.m3.com