ゆるテックノート

Unix時間とタイムゾーン

同じ瞬間のUnix時間は、どの地域でも同じです。ただし、日時の入力・表示・将来の予定を扱うときは地域のルールが必要です。「時点」「ローカル日時」「経過時間」を分けて考えます。

UTCとローカル時間の役割分担

発生済みのイベントにはUTC基準の時点が向いています。一方、毎朝の予定や誕生日などは、UTCの数値1つへ変換するだけでは意図を残せません。

押さえるポイント

  • イベントの発生時刻:Unix時間などで時点を保存し、表示時に利用者のタイムゾーンへ変換する。
  • 毎朝9時の予定:ローカル時刻、America/New_York等のIANA ID、繰り返し条件を保存し、各日付のルールで次回時刻を求める。
  • 誕生日・営業日など時点を表さない値は日付型として扱う。24時間の経過と「翌日の同じ時刻」は夏時間の切り替え時に一致しない。

保存・API・ログの方針

どこでタイムゾーンを決め、どう伝えるかを決めておくと混乱が減ります。

DB保存

  • PostgreSQLのtimestamp with time zoneは内部でUTCの時点を保持し、入力された元の地域名を保存しません。出力はセッションのTimeZoneで変わります。必要なIANA IDは別の列などに保存します。
  • timestamp without time zoneはタイムゾーンを持たない日時です。地域の予定の一部には使えますが、時点へ変換するには地域と曖昧時刻の扱いを決める必要があります。

API/メッセージ

  • 時点を文字列で渡すなら、RFC 3339等の仕様を選び、2024-02-29T12:00:00+09:00のようにオフセットを含めます。オフセットだけではAsia/Tokyoなどの地域名は分かりません。
  • Unix時間を返す場合は秒・ミリ秒などの単位を明記します。日時文字列の扱い、端数、受け付ける範囲もそろえます。

ログ/監視

  • 解析しやすいようUTCで統一するか、UTCとローカルの両方を出す。
  • Zや+09:00などのUTCオフセットを含めます。地域ルールを調べる必要があればIANA IDも別に記録します。

変換の具体例

コマンドやコードでTZを明示すると、意図しないローカル設定に影響されにくくなります。

Pythonで地域を指定して変換する

Python 3.9以降のzoneinfoを使う例です。OSのタイムゾーンデータ、またはPythonのtzdataパッケージが必要です。地域データを持たないWindows等では、アプリの依存関係として用意します。

from datetime import datetime, timezone
from zoneinfo import ZoneInfo
instant = datetime.fromtimestamp(1735689600, tz=timezone.utc)
print(instant.isoformat())
print(instant.astimezone(ZoneInfo("Asia/Tokyo")).isoformat())
# 2025-01-01T00:00:00+00:00
# 2025-01-01T09:00:00+09:00

アプリでの扱い

  • オフセット付き入力はそのオフセットを尊重します。地域名付きのローカル日時では、存在しない時刻・2回現れる時刻をライブラリ任せにせず、拒否・選択などの方針を決めます。
  • 表示に使う地域はユーザー設定や業務要件から選びます。OS、言語ランタイム、DB接続のタイムゾーン設定は別々の場合があるため、明示して確認します。

よくある落とし穴

デフォルトTZの違いやDST、曖昧な文字列パースでバグが起きやすいです。

主な注意点

  • 文字列にタイムゾーンがなければ、ローカル時刻として扱うAPIも、UTCとして扱うAPIもあります。仕様により拒否される場合もあるため、「必ず環境のTZになる」とは限りません。
  • 夏時間の開始時には存在しないローカル時刻、終了時には2回現れるローカル時刻があります。毎回1時間の変更とも限りません。
  • ブラウザーとサーバーで別の地域を使えば、同じUnix時間でも表示が違います。表示の差と、異なる瞬間を保存した不具合を区別します。
  • 翌日の同じローカル時刻を求める処理に、単純な86,400秒加算を流用しません。経過時間を求めたいのか、カレンダー上の予定を求めたいのかを決めます。

ニューヨークの切り替え例

America/New_Yorkの2026年の例です。11月の01:30は、オフセットを見れば異なる瞬間だと分かります。

UTC ローカル日時
2026-03-08 06:59 2026-03-08 01:59 -05:00
2026-03-08 07:00 2026-03-08 03:00 -04:00
2026-11-01 05:30 2026-11-01 01:30 -04:00
2026-11-01 06:30 2026-11-01 01:30 -05:00

テストと運用のコツ

TZを変えても意図通りかを確認するテストを持っておくと安心です。

チェック項目

  • UTCとAsia/Tokyoで変換結果を比較し、元のUnix時間へ戻せるか確認する。
  • 夏時間開始の欠落時刻と終了の重複時刻、24時間後と翌日の同時刻をそれぞれ試す。
  • 入力オフセット、秒・ミリ秒、データベース接続のTZ、タイムゾーンデータの更新状態を確認する。
  • 具体的な地域の違いはタイムゾーン一覧、単位と変換の基本はUnix時間の解説を参照する。

仕様と出典

IANA Time Zone Database / tzdb theory / RFC 3339 / PostgreSQL date/time types / Python zoneinfo