こんにちは!ベネフィット・ワンでエンジニアをしている酒井と申します!
はじめに
本記事では、
「Lambda から JST で保存したはずなのに、DB を見たらタイムゾーンが消えていた…」
という失敗を経験したので共有します。
結論から言うと、
timestamp without time zoneを使っていると、
タイムゾーンの前提がコード側に埋もれて事故りやすい
という話です。
AWS Lambda(Python)と Java アプリケーション、そして PostgreSQL を組み合わせた構成で、見事に timestamp でつまずきました。
システム構成(ざっくり)
- DB:PostgreSQL
- バックエンド:
- AWS Lambda(Python)
- Java アプリケーション(Spring Boot)
- DB の時刻カラム:
- すべて
timestamp(3) without time zone
- すべて
何が問題だったのか
Lambda の実行環境は UTC
AWS Lambda の実行環境は デフォルトで UTC です。
そのため、Python 側では JST に変換してから DB に保存する処理を実装していました。
from datetime import datetime, timezone, timedelta
import pytz
JST = pytz.timezone('Asia/Tokyo')
current_time = datetime.now(JST)
「JST に変換しているし問題ないだろう」と思っていました。
DB 側の型が何かを注意していなかった
timestamp without time zone とは?
PostgreSQL の timestamp without time zone は、タイムゾーンの情報を保存しません。
つまり、保存されるのは「日時の値」だけで、それがどのタイムゾーンなのかという情報は消えてしまいます。
何が起きたか
Python から、例えばこんな値を保存したとします。
2026-01-07 12:00:00 +09:00(JST)
でも、DB にはこう保存されます。
2026-01-07 12:00:00
→ +09:00(JST) の情報が消えます。
これでは、DB 上のデータが「JST」か「UTC」なのか、見た目ではわかりません…
(※ 実際の挙動は使用するドライバーや ORM によっても異なる場合があります)
暗黙のルール
「このカラムは JST で入ってるよね」
「いや、UTC だった気がする」
こういった 人の記憶に依存するルール が危険です。
改善案
根本的な改善案としては、時刻を保存するカラムに timestamp with time zone を使うことです。
timestamp with time zone は、
- タイムゾーン付きの時刻を受け取る
- DB 内部では UTC に変換して保存
- 取得するときは、接続セッションのタイムゾーン設定に応じて自動変換される
という動きをします。
これで、
- DB が時刻を一貫して UTC で管理できる
- 「これは JST?UTC?」と悩まなくていい
- Lambda(UTC)でも Java でも安全に使える
- JST / UTC 混在事故を防げる
既存データがある場合の注意
timestamp without time zone をそのまま timestamp with time zone に変更すると、
今まで JST として保存していた時刻が UTC として扱われてしまう 可能性があります。
(9 時間ズレる)
既存データがある場合は、「今まで何の時間として入っていたか」を確認し、分からない場合は テスト環境で必ず検証 してください。
また、Java 側で LocalDateTime を使っている場合は注意が必要です。
LocalDateTime は日時は持ちますが、タイムゾーン情報は持ちません。
タイムゾーンを扱う場合は、OffsetDateTime や ZonedDateTime を使いましょう。
// タイムゾーン付きで現在時刻を取得する例
OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
このように、日時とタイムゾーン(またはオフセット)をセットで扱うクラスを使うと安全です。
おわりに
timestamp の違いを特に気にしていなかった初心者の私が気付いたことは、
- クラウド
- 複数言語
- 複数サービス
が絡むと、「前提のズレ」が静かにバグになります。
この記事が、同じところで悩む人の助けになれば嬉しいです。