MySQL 11 min read

MySQLのDATETIMEとTIMESTAMPの違い ― time_zoneで変わる値と2038年の上限

※この記事にはアフィリエイト広告(プロモーション)が含まれます。

DATETIME は書いた日時をそのまま持ち、TIMESTAMP は UTC に直して持ちます。この記事の出力は MySQL 8.4.11(Ubuntu 26.04、time_zone=SYSTEM・system_time_zone=JST)で採りました。

まず見る(結論)

DATETIME TIMESTAMP
time_zone を変えたとき 同じ値のまま そのタイムゾーンに直して表示
入る範囲 1000-01-01 00:00:00〜9999-12-31 23:59:59 UTC で 1970-01-01 00:00:01〜2038-01-19 03:14:07
範囲外を入れたとき — ERROR 1292(厳格モード)
DEFAULT CURRENT_TIMESTAMP / ON UPDATE 使える 使える
  • 2038 年より先の日時を入れる列、タイムゾーンをまたがずに使う列は DATETIME
  • アプリやサーバーごとにタイムゾーンが違い、同じ時点を指したい列は TIMESTAMP

time_zone を変えると TIMESTAMP だけ値が変わる

DATETIME 列 dt と TIMESTAMP 列 ts に、time_zone = '+09:00' の接続から同じ '2026-11-08 10:00:00' を入れ、time_zone を変えて読みました。

読んだときの time_zone dt ts ts = dt
+09:00 2026-11-08 10:00:00 2026-11-08 10:00:00 1
+00:00 2026-11-08 10:00:00 2026-11-08 01:00:00 0
-05:00 2026-11-08 10:00:00 2026-11-07 20:00:00 —

UNIX_TIMESTAMP() で見ると、ts はどの time_zone でも 1794099600 です。dt は +09:00 で 1794099600、+00:00 で 1794132000 と、読む側のタイムゾーンで解釈されて 9 時間ずれました。WHERE の比較も読む側の time_zone で行われ、+00:00 の接続では WHERE ts = '2026-11-08 01:00:00' で同じ行が返りました。

サーバーの time_zone が SYSTEM のままなら、アプリのドライバーやセッション設定でタイムゾーンを変えない限り差は出ません。接続プールやバッチの片方だけ UTC にしている構成で問題になります。TIMESTAMP 列を使うなら、接続ごとの time_zone をそろえます。

‘Asia/Tokyo’ のような名前は既定では使えない

SET time_zone = 'Asia/Tokyo' は ERROR 1298 (HY000): Unknown or incorrect time zone: 'Asia/Tokyo' でした。mysql.time_zone_name が 0 行で、タイムゾーン表が読み込まれていないためです。'+09:00' の形なら通ります。

オフセット付きのリテラルは DATETIME でも変換される

'2026-11-08 10:00:00+00:00' を +09:00 の接続で入れると、dt も ts も 2026-11-08 19:00:00 になりました。オフセットそのものは残りません。

TIMESTAMP に入る範囲(2038 年問題)

time_zone 入れた値 結果
+00:00 2038-01-19 03:14:07 入る
+00:00 2038-01-19 03:14:08 ERROR 1292 (22007): Incorrect datetime value
+09:00 2038-01-19 12:14:07 入る
+09:00 2038-01-19 12:14:08 ERROR 1292
+00:00 1970-01-01 00:00:00 ERROR 1292
+00:00 1970-01-01 00:00:01 入る

上限・下限は UTC で決まっています。DATETIME 列には 9999-12-31 23:59:59 と 1000-01-01 00:00:00 がそのまま入りました。

SELECT では範囲外の値を返してもエラーになりません。SELECT DATE_ADD(ts, INTERVAL 20 YEAR) は 2046-11-08 10:00:00 を返しますが、同じ式で UPDATE すると ERROR 1292 です。

sql_mode を空にした接続では、2050-01-01 00:00:00 が警告 1264 Out of range value が出るだけでエラーにならず、0000-00-00 00:00:00 が入りました。

DEFAULT CURRENT_TIMESTAMP と ON UPDATE

CREATE TABLE a2 (
  id int PRIMARY KEY, v int,
  created_at timestamp DEFAULT CURRENT_TIMESTAMP,
  updated_at timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  c_dt datetime DEFAULT CURRENT_TIMESTAMP,
  u_dt datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

挿入から 2 秒後に v を書き換えると、updated_at と u_dt がどちらも 2 秒進みました。同じ値で UPDATE したとき(ROW_COUNT() が 0)は、どちらの列も変わりませんでした。

操作 結果
型だけ書いた ts timestamp timestamp NULL DEFAULT NULL になり、自動では入らない
DEFAULT CURRENT_TIMESTAMP の列に NULL を明示して INSERT NULL が入る
timestamp NOT NULL(既定値なし)に NULL を INSERT ERROR 1048: Column 'ts' cannot be null
同じ列を省略して INSERT ERROR 1364: Field 'ts' doesn't have a default value
DEFAULT '0000-00-00 00:00:00' ERROR 1067: Invalid default value for 'ts'

型だけ書いた最初の TIMESTAMP 列に現在時刻が自動で入るのは、explicit_defaults_for_timestamp が OFF の場合の動作です。8.4 の既定は ON(1)です。

DATETIME には式の既定値も書けます。d datetime DEFAULT (CURRENT_DATE + INTERVAL 7 DAY) は、11 月 8 日に INSERT すると 2026-11-15 00:00:00 が入りました。

小数秒は四捨五入される

列 入れた値 入った値
datetime 10:00:00.789 10:00:01
datetime(3) 10:00:00.12345 10:00:00.123
timestamp(6) 10:00:00.1234567 10:00:00.123457

警告は出ません。切り捨てたいときは sql_mode に TIME_TRUNCATE_FRACTIONAL を足します。足した接続で datetime に .789 を入れると 10:00:00 が入りました。

小数秒付きの列では、既定値も列と同じ精度で書きます。timestamp(3) DEFAULT CURRENT_TIMESTAMP は ERROR 1067 で、DEFAULT CURRENT_TIMESTAMP(3) と書くと通りました。

型を変えるときの注意

TIMESTAMP を DATETIME に変えると、ALTER を実行した接続の time_zone で表示される値が DATETIME に書き込まれます。+09:00 で入れた 10:00:00 の列を +00:00 の接続で ALTER TABLE c MODIFY ts datetime すると、01:00:00 が残りました。+09:00 に戻して読んでも 01:00:00 のままです。ALTER は、アプリが普段使うのと同じ time_zone の接続で実行します。

DATETIME から TIMESTAMP へは、範囲外の行が1行でもあると ERROR 1292 で ALTER 全体が失敗し、表は元のままでした。

関連: MySQLの日付計算 / MySQLのDATE_FORMAT / PostgreSQLのtimestamptzとは

MySQL をもっと深く知るための本

MySQL徹底入門 第5版 MySQL 8.4 LTS対応

翔泳社・2025年6月・MySQL 8.4 LTS 対応

MySQL 8.4 に対応した入門書。日本MySQLユーザ会のメンバーが執筆している。

Amazonで見る

MySQL運用・管理[実践]入門 〜安全かつ高速にデータを扱う内部構造・動作原理を学ぶ

技術評論社・2024年5月

内部構造と動作原理から、運用・管理の考え方を学ぶ。

Amazonで見る

Amazonのアソシエイトとして、FIRE SE は適格販売により収入を得ています。