見出し画像

MGL週報 #50 - 日付と時刻のあれこれ

このエントリはゲーム開発用フレームワーク「MGL」の開発記録です。MGLはzlibライセンスの下に無償で提供されています。



今週の作業

システムアクセス関連のドキュメント化と整理

現時点での実装内容のドキュメント化としては一通り片付きました。これから実装に関する細かい変更を加えていく予定ですので、それに伴う更新は並行して進めていきます。

修正予定のある作業もそれほど多くはないのですが、破壊的変更を加えるなら今がタイミングの良い機会でもあります。今一度ドキュメントと共に精査してみて、変更した方が良さそうな箇所を探してみるのも良いかもしれません。今がチャンスと思って焦った判断を下すと大惨事になりかねないので、ほどほどに。

それらが一通り落ち着いたら、一旦バージョン1.1.12としてリリースするかもしれません。更新内容としては小規模になりますが、ゲームのアップデートと違って溜め込んで一気に放つ理由も無いですからね。


日付と時刻のあれこれ

日付と時刻、それはコンピュータ誕生以来、多くの技術者に頭痛の種を植え付けてきたモンスターです。

一般的な日付と時刻の扱い

コンピュータにおける日付と時刻の表現には、広く用いられている形式が2つあります。1つは日付と時刻の形式、もう1つはUNIX時間です。

日付と時刻の形式は年/月/日/時/分/秒を基本とした複数のパラメータで表現される時間表現です。普段我々が日常生活で使用するのはこの形式ですね。

普段から見慣れている形式だから扱いも簡単……などと思ってはいけません。この形式、コンピュータから見たら勘弁願いたいルールの凝縮なのです。

まず、各々の要素の基数がちぐはぐです。秒と分は60進数、時は24進数、日は月によって28進数から31進数のどれか、月は12進数……と、この時点でもう投げ出したいフォーマットとなっています。

これに加えて、閏年と閏秒の問題もあります。閏年は(実は4の倍数ではないというトラップを除けば)単純な計算で算出可能ですが、閏秒の挿入時刻は人類が適宜決めているため法則性はなく、今まで挿入してきた閏秒をテーブル化して持つ以外に手段がありません。さらに、地域が異なれば時差も発生します。ついでに夏時間なんてものもあります。

一方、UNIX時間とは、世界協定時における1970年1月1日0時0分0秒を起点として、秒単位で表現される時間です。例えば、2024年4月1日10時ちょうどを表す場合、UNIX時間では1711933200となります。これは、1970年1月1日から1711933200秒が経過した時刻が2024年4月1日10時0分0秒となるためです。

UNIX時間は直感的に分かりにくいため抵抗があるかもしれませんが、秒単位の1つの数値であるため扱いは極めて容易です。ある時刻から一定時間後の時刻を算出するのは単純な足し算で済むし、2つの時刻から経過時間を算出するのも単純な引き算で済みます。未来と過去の判別も比較演算がそのまま使用可能です。

また、UNIX時間はその性質上、時差や閏年といった情報を含みません。これらはUNIX時間から日付と時刻に変換する際に加味すべき情報です。故に、計算過程でこれらのややこしい要素を意識する必要もありません。

ただし、UNIX時間は閏秒の存在を完全に無視している点には注意が必要です。先ほどの例で示した1970年1月1日0時0分0秒の1711933200秒後が2024年4月1日10時0分0秒というのは、世界協定時を基準とした場合は正確ではありません。途中で閏秒が27回挿入されているため、世界協定時に直すと2024年4月1日9時59分33秒になります。

2024/04/08 追記
改めて読み返してみて誤解を招きそうな説明だと感じたため、補足します。

UNIX時間は時刻の表現手段であり、それそのものに誤差が生じている訳ではありません。例で示した1711933200が2024年4月1日10時0分0秒を表現しているという点に関しては何ら間違いはありません。

UNIX時間は閏秒を表現できないため、世界協定時を実時間とした場合、閏秒を跨ぐ際に同じ時間が2秒間発生する事になります。挿入される23時59分60秒と、次の0時0分0秒を同じ値で表現してしまうためです。よって、2点間の経過時間を差分で取得した場合、そこに閏秒が含まれていると実時間とのズレが生じますよ、というお話です。

ややこしい事に、これはどちらかが間違っているという問題ではありません。基準が異なるが故に差異が発生しているのです。(閏秒についてもっと混乱したい方はこちら

閏秒の扱いに注意が必要であるものの、UNIX時間の扱い易さは大きなメリットです。可能な限りプログラム内部ではUNIX時間で取り扱い、ユーザーに表示する場合にのみ日付と時刻に変換して使用というのが比較的低リスクな扱い方です。

MGLでの日付と時刻のサポート

さて、話題をMGL側にフォーカスしましょう。

実のところ、MGLの日付と時刻の扱いはあまり整理されていません。UNIX時間で現在時刻を取得する事や、それを日付と時刻の形式に変換することは可能です。しかし、先述の閏秒については考慮されておらず、中には混乱を招くような機能も存在しています。

一例を挙げると、現行のMGLには現在時刻に時差を加えたUNIX時間を取得する関数GetLocalTime()なるものがあります。矛盾している事がお分かりでしょうか? UNIX時間は世界協定時を基準としているのですから、時差を加えたらそれはもうUNIX時間ではありません。その補正は日付と時刻に変換する際に適用すべきなのです。

そして、実を言うとSteam版「反撃BLOCKS」は見事にこのトラップを踏んでいます。オンラインランキングに登録されているリプレイの情報の日時はプレイ環境の時差で補正した値であるため、プレイヤー間で比較しても日時がちぐはぐなのです。

こういった様々な問題を鑑みて、次のバージョンでは日付と時刻に関する機能に大幅な改修を加える予定です。より扱い易くするのはもちろん、間違いを起こしにくい仕組みを目指してあれこれ悩んでいる最中です。


その他

ツール類の作業にも手を出したくなってきましたが、最近色々あって作業時間が確保しにくい状況が続いています。こればかりはUNIX時間で管理しても解決できそうにありません。

この記事が気に入ったらサポートをしてみませんか?