世界を動かす技術を、日本語で。

プログラミングにおける時間の考え方

概要

  • 時刻処理は ソフトウェア開発 に不可欠だが、複雑さゆえに多くのプログラマーが苦手意識
  • 時刻の本質的な理解には「 持続時間(duration)」と「 瞬間(instant)」の区別が重要
  • 絶対時刻 はエポック(基準点)からの経過秒数として表現可能
  • グレゴリオ暦・UTC・タイムゾーン の仕組みや調整(うるう秒含む)が現代の時刻管理を支える
  • 精密な時刻計算ではうるう秒や各地域の標準時ルールの考慮が不可欠

プログラミングにおける時間の概念モデル

  • 時刻処理 は多くのソフトウェアで不可欠だが、複雑なタイムゾーンやうるう秒のために誤解やバグの温床
  • 「UTCを使えばよい」という単純なアドバイス は、ユーザー向け日時表示や精密な計時には不十分
  • 時間には「 持続時間(duration)」と「 瞬間(instant)」という2つの重要な概念
    • 持続時間:例)Usain Boltの100m走にかかった秒数
    • 瞬間:例)ゴールした瞬間やレースの途中の任意の時点
  • 絶対時刻(absolute time) は、カレンダーやタイムゾーンとは独立した一意の瞬間を指す
  • 瞬間同士の順序や間隔を正確に表すには、「 エポック(epoch)」という基準点からの経過時間で管理
    • 例:Unixエポック(1970年1月1日00:00:00 UTC)からの秒数
    • 他にもJaiのApollo_Timeなど独自エポックも存在

エポックと絶対時刻の表現

  • エポック は任意の瞬間を0秒と定義し、以降の時刻をそこからの経過秒数で表現
  • Unix timeはUnixエポックを基準にし、32ビット整数で1970〜2038年を効率的に表現可能
  • 日常会話でも「昨夜」「明日の正午」など、無意識に現在時刻を基準点として利用

グレゴリオ暦と市民時間

  • 人間は「 市民時間(civil time)」としてグレゴリオ暦を使い、年/月/日/時/分/秒で時刻を表現
  • ロケールによる表示形式の違い(例:en-US 6/5/2025 6:00 PM vs de-DE 5.6.2025, 18:00)があるが、指す瞬間は同じ
  • 期間(period) は曖昧な持続時間(例:1ヶ月=28〜31日)であるため、計算時に注意が必要

カレンダーとエポックの相互変換

  • Unix timeとグレゴリオ暦は、異なるエポックを持つ「秒数基準」表現であり、相互変換が可能
  • エポックの選択は、システム要件や表現範囲、リソース効率に応じて柔軟に行う

時刻の基準と人類の時間管理

  • 古くは太陽や地球の自転・公転を基準に1日24時間・1年365日を定義
  • 1秒=1太陽日の1/86400としたが、地球の自転は年々遅くなっており、厳密には不正確
  • うるう年で公転周期(約365.25日)に対応するが、自転速度の変化には非対応

UTCとうるう秒

  • UTC(協定世界時) は、原子時計を基準にした国際標準時
  • 1秒=セシウム133原子の放射周期9,192,631,770回分(SI秒)で厳密に定義
  • 世界中の原子時計の平均値をIERSが管理し、NTPサーバーを通じて各コンピュータに配信
  • 地球の自転とのズレを補正するため「 うるう秒(leap second)」を不定期に挿入・削除
    • 例:23:59:59の次に23:59:60(正のうるう秒)、または23:59:58→00:00:00(負のうるう秒)
    • 1972年以降、27回追加(削除は未実施)
    • うるう秒の調整は突発的で予測困難、今後2035年に廃止予定

うるう秒と精密な時刻計算

  • うるう秒の存在により、1日は必ずしも86,400秒ではなく、86,401秒や86,399秒の場合も
  • 日付間の正確な持続時間計算には「うるう秒の総和」を加減する必要
  • うるう秒はIERSが事前に告知し、各システムがデータを更新して対応

プログラムとうるう秒の対応例

  • JavaScriptのDateオブジェクト(Unix timeベース)はうるう秒を考慮しないため、「真のUTC」ではない
  • Date.now()の返すミリ秒数は、実際の経過時間より少なくなる場合がある

タイムゾーンと標準時

  • UTCは絶対時刻を提供するが、世界各地で太陽の位置が異なるため「 タイムゾーン」が必要
  • タイムゾーンは各地域の「標準時(standard time)」のルールを定め、UTCからのオフセットで表現
    • 例:日本標準時(JST)はUTC+9、Line IslandsにはUTC+14も存在
  • 同じ瞬間でも、タイムゾーンによって表示される日時が異なる

まとめ

  • 時刻管理の本質 は、「持続時間」と「瞬間」、「エポック基準の表現」、「うるう秒やタイムゾーンの考慮」
  • 精密な時刻計算やユーザー向け表示には、 カレンダー・UTC・タイムゾーンの仕組み を正確に理解・実装する必要
  • システム要件に応じて適切な時刻表現・計算方法を選択することが、バグや混乱を防ぐ鍵

Hackerたちの意見

OPが夏時間の悪夢について話してくれて嬉しい!でも、毎年の天気チャートを重ねるとき、4年ごとにグラフが1日ずれて1/366の幅になっちゃって、月の区切りがぴったり合わないのが嫌なんだよね。それか、2月28日を重複させないと線が途切れちゃうし。どうやって表現するかまだ決めてないけど、ほんとイライラする。

いい投稿だね。時間のこと、いつも考えちゃうよ(笑)。君が楽しめそうな別の情報源があるよ(NTPと同期の質問について)TigerBeetleからの「Implementing Time」だよ:Implementing Time

(投稿者)リンクありがとう!

他のエポックも使えるよ(例:JaiのApollo_Timeは、1969年7月20日20:17:40 UTCのアポロ11号の着陸を使ってる)。他にもヴァーナー・ヴィンジのファンがいるね。でも、エポックとしてはちょっと変わった選択だよね。だって、システムとのインターフェースを考えると、そのエポックが始まるのは大体5ヶ月後なんだから。

ソフトウェア考古学のポイントって、そういうことだよね?最初の数百年では明らかだったことが、後になっては理由がわからなくなることもあるし、5ヶ月なんてどうでもいいじゃん。ロゼッタストーンでもないと、時間がずれてるかどうかすら確信できないし、歴史家が説明できない数ヶ月が出てくるかもしれないよ。

時間ってほんとに混乱してるよね。常に。著者は問題の表面をちょっと触れただけだし。GPS/GNSS衛星に影響を与える相対性理論の時間の遅れを除いても、やっぱり混乱してる。タイムゾーンはもちろんだけど、タイムゾーンが使われる前はどうだったの?途中で、どのタイムゾーンを使うかも問題だよね。ケーニヒスベルクはドイツの時はCETだったけど、ロシアになったらEETに変わったし。15分ずれてる国もあるし、夏時間の話はもうやめておこう。レバノンでは、DSTが同時に使われていたこともあったし、予約を取るのが大変だよ…。ユリウス暦からグレゴリオ暦への移行も、何年もかけて行われたし、国境によっても違ったりして…。特定の年にうるう日を入れ忘れた国もあって、数年間3月1日が全然違う日になったこともあった。時間ってほんとに混乱してる。今も、これからもずっと。

著者はIANAがケーニヒスベルクをどう扱うかを説明してるけど、論理的には独自のタイムゾーンだね。IANAのタイムゾーンは、現在のルールと将来のルールを共有する地域のセットを指すだけじゃなく、1970年1月1日00:00+0以降の民間時間の歴史も共有してるんだ。つまり、この定義はどの地域が単一のIANAタイムゾーンにまとめられるかについて、より制限的なんだよね。1970年以降に民間時間のルールを変更した地域は、他の地域と一緒にまとめることができないから。時間が混乱してるのには同意するよ。それに、15分のずれは狂ってるし、なんで誰かがそれを使ってるのか理解できない。

そうだね。幸いなことに、多くのアプリはローカルの民間時間とOSで設定されたタイムゾーンだけでやっていけるよね。うるう秒とか気にすることはあまりないし、多くのアプリはミリ秒の精度とかも気にしない。もし君のアプリがそれらを気にするなら、すぐに混乱しちゃうよ。

そうなんだ、いくつかのタイムゾーンでは、1時間の差だけじゃなくて、30分や45分の差もあるんだよ。インドはUTC +5:30、ロードハウ島はUTC +10:30 / +11:00、ニュージーランドのチャタム諸島はUTC +12:45 / +13:45、イランはUTC +3:30 / +4:30とかね。X / Yの形式では、Xが標準時間で、Yが夏時間を意味するんだ。ややこしいよね。完全なリストはここにあると思うよ。https://www.timeanddate.com/time/time-zones-interesting.html Bashスクリプトを使えば、/usr/share/zoneinfo/のファイルを基にした詳細なリストが得られるよ。つまり、整数以外の時間オフセットを持つタイムゾーンを見つけられるんだ。

さらに悪いことに、宗教によってタイムゾーンが変わる地域もあるんだ。レバノンでは「イスラム時間」と「キリスト教時間」があった時期があったよ(今もそうなのかは不明だけど)。

一番ひどいのは多分モロッコの夏時間だね。計算するのはソフトウェアじゃ無理だと思う。月の見え方を正確に知るには、正確な場所を把握して、山が視界を遮ってないか確認しないといけないと思う。

あまり触れられてない2つのことがあるんだ:- システムクロックのドリフト。Googleのインスタンスはデータセンターで原子時計を使って正確な時間を保ってるし、うるう秒を1日で分散させてる。正確な時間の測定には重要かもしれない。- 時間情報がどう消費されるかを考えてみて。写真共有サイトなら、各写真に必要なのは場所とローカルの日付と時間だよね。だから、これが欠けてても、大晦日の写真はタイムゾーンや場所を考慮しなくてもほぼ真夜中になる。実際にそういうケースがあって、自動で調整されない文字列表現を選んだんだ。視聴者のローカル時間に変換するのはあまり役に立たない。

カレンダーのイベントスケジューリング問題も難しいよね。シドニーにいて、3週間後のサンフランシスコでの午後4時の会議を受け入れたとしたら、どうやってそのイベントの日時をカレンダーに保存するの?それに、カレンダーは電話が場所やタイムゾーンを変えたときにどう反応するの?標準時間と夏時間の切り替えが、イベントが作成されたタイムゾーンと実際のイベントの時間の間に起こる場合、どうなるか考えてみて。シドニーが冬から夏時間に、サンフランシスコが夏から冬時間に切り替わる場合、たぶん2回の夏時間の変更があるし、その切り替えが同時に起こるわけじゃないし、同じ週ですらないかもしれない。

すごくいいまとめだね!でも、時間が混乱する必要はないっていう君のポイントは、君が挙げたすべてのポイントで反論されてると思う。投稿の長さを制限しなきゃいけなかったのはわかるけど、時間には興味があるから、君が面白いと思うかもしれないいくつかのポイントを追加するね:UTCは略語じゃないんだ。聞いた話では、英語の略語は「CUT」になるはずで(名前は「協定世界時」)、フランス人が文句を言ったから、フランスの略語は「TUC」になって、英語を話す委員が文句を言ったから、どちらの言語でも発音できないものに落ち着いたんだ。(ちなみに、「ISO」も略語じゃないよ!)うるう秒はデータセンターで大混乱を引き起こしたから、今後はうるう秒は使われないことになったよ。(未来に何が起こるかは誰にもわからないけど。)でも今は、安心して無視しても大丈夫。時間(とNTP)に関するリンクの短いリストがあるよ:https://wpollock.com/Cts2322.htm#NTP>。

「時間が混乱する必要はないという君の意見は、君が言ったすべてのポイントで反論されているよ(笑)」その通りだね…複雑さを強調しようとして、実際の複雑さを過小評価してたかも。例えば、自分がこのトピックを学んでいたとき、特に市民時間やタイムゾーンについて、タイムゾーンのルール変更を複雑さの証拠として挙げる投稿をたくさん読んだり、「時間についてのプログラマーの誤解を見てみろ」と言うけど、ほとんどの例は「すべての地域の市民時間を予測する一般的なルールは存在しないけど、1970年以降はIANAデータベースのおかげで、UTCオフセットを知るのが簡単になる」という高いレベルのメンタルモデルでほぼカプセル化できるんだ。特定のタイムゾーンのルール変更を心配する必要はないよ。実際の複雑さ(多くのプログラムには関係ないかもしれない)は「存在しない/曖昧な市民時間をどう扱うか」とか「未来の時間ルール変更をどう扱うか」とかの質問に関するものだよ。リンクありがとう!

abslライブラリには、時間プログラミングについての素晴らしい記事があるよね。https://abseil.io/docs/cpp/guides/time

Googleがうるう秒を実装した方法は、12月31日の最後に23:59:60を追加するんじゃなくて、もっと面白い方法だったんだ。彼らは、12月31日の毎秒に1/86400秒を加えることで、うるう秒を「スミア」したんだ。1/86400秒はNTPの誤差範囲内だから、コンピュータはエラーを出さずにそのまま動き続けられるんだよ。追記:彼らはうるう秒の前の正午から、うるう秒の後の正午までスミアしたんだ。つまり、12月31日12時から1月1日12時までね。

RachelByTheBayがそのことについて書いてるよ。http://rachelbythebay.com/w/2025/01/09/lag/ たぶん、We Used To Be Do No Evil Incじゃなくて、Move Fast And Break Things Corpでのことだね。

他の多くの人が言ってるように、時間とカレンダーはややこしいし、正しい解決策はないことが多いよ。ただいろんなトレードオフがあるだけ。Jon Skeetの「UTCを保存することは銀の弾丸ではない」(https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a...)は、ユーザーにとっての時間のポイントが何を意味するのか、その微妙な部分を理解するのにすごく影響を与えたんだ。それがシステムのデザインにも関わってくるんだよね。

UTCで全部保存するのが完璧な解決策じゃないシステムで働いたことないな。そういうのもあるけど、ニッチな存在だと思う。UTCを使えないアプリケーションでも、ほとんどのタイムスタンプはUTCだよ。一部の特別なケースでちょっと手間がかかることがあるけど、他は全部UTCで済むよ。

時間に特別なことなんてないよ。こういう問題は、いくつかの過負荷な用語を使って多くの概念をモデル化しようとするときに出てくる。特殊相対性理論と一般相対性理論の混乱は、プログラマーが実際に遭遇する問題にはほとんど関係ないよ。もしそれが君の使い方なら、時間は特別で難しいってことになるけど。最も一般的な問題は、モデルとビューの概念を分けられないことだね。例えば、タイムスタンプはモデルだけど、ローカルタイム、曜日、うるう秒はビューの概念だよ。次に多いのは、UTCをモデルとして使うのが適切だと思ってしまうこと。もっと合理的なTAI64を使うべきなのに。その後は、スケジュールリクエストと実際に起こったことのログの違いだね。「会議は来週の水曜日の午前9時にローカルタイムで予定されている」と「会議はこの時間帯に行われた」の違い。一つは過去の事実で、もう一つは単なるスケジュールの基準。例えば「隔週水曜日」とか「偶数日水曜日」とか、「この2台のマシンが使えるようになったらこのタスクに取り組む」とか、「このプロセスは今から2つの時間帯で実行される」とかね。

ローカルタイムは単なるビューじゃなくて、モデルにも関わってくるよ。「キャンセルされるまで毎週火曜日の午後4時」というのをタイムスタンプのシリーズとして効果的にモデル化するのは無理だよ。なぜなら「夏時間の変更に関係なく」という暗黙の前提があるから。例えば「2030年10月15日の午後2時」をタイムスタンプとしてモデル化するのも同じ理由で無理だよ。二人の人間が同じタイムゾーンでの約束なら、どこかでローカルタイムの概念を使ってルールを保存する必要があるんだ。

TAI64の話が出るまで同意してたのに…なんで超レアなケース(超高精度な時間間隔)に合わせてソフトウェアを最適化するの?一般的なケース(タイムスタンプ→カレンダー時間)を難しくするなんて。コンピュータ天文学や珍しい物理実験に興味がない限り、うるう秒やTAIを心配する必要なんてないよ。長い間隔なら、数年ごとに1秒の誤差が出てもコードの変換の複雑さを減らすためには全然問題ないよ。(短い間隔では、1秒が重要な場合はTAIもUTCも使わず、OSが提供するモノトニックタイマーを使うべきだよ)