MySQLのDATETIME型を使っているとき、INSERTした時刻がなぜか1秒遅れて保存される——そんな現象に遭遇したことはありませんか?
正直、初めて発見したときは「何か重大な不具合?」と焦りました。実は、DATETIME型に精度指定をしない場合、秒未満の小数部分が四捨五入される仕様が原因で1秒ズレることがあるのです。
本記事では、私が実際に経験した「秒が1秒遅れる」問題の詳細と、その対策方法をわかりやすく解説します。
なぜ1秒ズレが発生するのか
MySQLで精度指定なしのDATETIMEを使うと、ミリ秒・マイクロ秒の部分は四捨五入されます。例えば、12:30:59.600000
のように0.5秒以上が含まれると、秒が1つ繰り上がる形で登録されることがあります。
私の場合、外部APIから受け取った時刻をそのままINSERTしたら、意図しない秒数に丸められ、DBに保存される値が1秒遅れてしまうという現象に苦しみました。
INSERT文やタイムゾーンを疑った末に行き着いたのが、このDATETIMEの丸め仕様でした。
DATETIME精度指定の仕様
MySQLのDATETIME型には、カラム定義で精度を指定できます。
DATETIME(3)
:ミリ秒まで保存DATETIME(6)
:マイクロ秒まで保存
一方で、ただのDATETIMEだと秒単位までしか保持せず、小数点以下は四捨五入されてしまいます。
例えば、以下のようなINSERT文の場合
INSERT INTO logs (created_at) VALUES ('2023-10-01 12:30:59.600000'); DATETIME(精度なし) → 2023-10-01 12:31:00として切り上げられる可能性あり DATETIME(3)やDATETIME(6) → 0.600まで正確に保持できるので、12:30:59.600になる
監査ログや時間を厳密に管理したいケースでは、1秒の違いが大きな影響を及ぼすこともあります。「正直、1秒くらい大丈夫」と思われるかもしれませんが、いざ問題が起きたときにログのタイムスタンプが合わないのは非常に厄介です。
具体例:意図せぬ丸め
私が経験した例では、本来12:00:05.800000
で受け取った時刻が内部処理で12:00:05.600000
になり、最後にMySQLに書き込まれるときに12:00:06
に切り上げられていました。
「正直、このぐらいのズレなら気付かない人もいるかも…」と思いつつも、後々ログを分析するときに「あれ、タイムスタンプが合わない…」と混乱する原因になります。
対策方法
MySQLのDATETIMEで1秒のズレをなくすには、大まかに次のような方法があります。
- カラムの精度指定をする
- TIMESTAMPやタイムゾーン設定を再点検する
- アプリケーション側で秒以下の扱いを工夫する
カラムの精度指定をする
一番確実なのは、DATETIME(3)やDATETIME(6)などの精度付きカラムにしてしまうことです。
テーブル定義を変更しなければならないので面倒ですが、長期的には1秒の誤差で悩むことを防げます。監査用やログ用など、正確な時刻管理が必要なカラムには精度を設定しておくのがおすすめです。
TIMESTAMPとタイムゾーンの再確認
TIMESTAMP型を利用すると、MySQLサーバー側のタイムゾーンとアプリケーションサーバーのタイムゾーンの変換関係が自動で働くため、日時を統合的に管理しやすい側面があります。
ただしTIMESTAMPは格納可能な範囲がDATETIMEより狭かったり、UTC変換が行われる点に留意が必要です。また、サーバーとアプリのタイムゾーン設定に差異があると、やはりズレが生まれます。
アプリケーション側で秒以下の扱いを工夫する
カラム定義を変えられない場合、アプリ側であらかじめ秒以下を切り捨て・切り上げしてからINSERTする方法もあります。
ただしこれは根本的な解決策というよりは応急処置です。将来的な拡張や正確性を考えるなら、カラム精度の変更を視野に入れたほうが無難でしょう。
まとめ
MySQLのDATETIME型で時刻が1秒ずれてしまう原因は、多くの場合秒以下の小数部分が四捨五入される仕様にあります。
これを防ぐには、DATETIME(3)やDATETIME(6)など精度付きのカラムを使うのが最も確実な対策です。タイムゾーンの整合性を見直したり、TIMESTAMP型を活用する方法も検討してみると良いでしょう。
私自身、最初はINSERT文やサーバーの設定を疑いましたが、最終的には「DATETIMEが秒以下を丸めている」というシンプルな理由に行き着きました。同じように悩んでいる方は、ぜひ本記事の内容を参考に、1秒のズレを解消してみてください。