MySQLを使っていると、日付の加算や差分の計算、表示形式の変換は日常的に必要になります。「3日後の日付を求めたい」「契約開始日から今日まで何日経ったか調べたい」「日付を〝2026年06月07日〟と整形したい」—こうした処理はすべて、MySQLの標準関数で完結します。本記事では NOW・CURDATE・DATE_ADD・DATE_SUB・DATEDIFF・TIMESTAMPDIFF・DATE_FORMAT・LAST_DAY・DAYOFWEEK を用途別に整理し、月末計算や曜日取得といった実務パターンもあわせて解説します。
このシナリオで考える
以下の注文テーブル orders を例に進めます。
CREATE TABLE orders (
order_id INT PRIMARY KEY AUTO_INCREMENT,
customer VARCHAR(50) NOT NULL,
ordered_at DATE NOT NULL,
delivered_at DATE
);
INSERT INTO orders (customer, ordered_at, delivered_at) VALUES
('田中', '2026-05-01', '2026-05-05'),
('佐藤', '2026-05-15', '2026-05-20'),
('鈴木', '2026-06-01', NULL),
('高橋', '2026-06-03', NULL);
ordered_at が注文日、delivered_at が配送完了日です。NULL は未配送を意味します。
現在日時を取得する:NOW・CURDATE・CURTIME
まず基本の「今を取得する」関数から押さえます。
| 関数 | 戻り値の型 | 例(2026-06-07 10:30:00 の場合) |
|---|---|---|
NOW() |
DATETIME | 2026-06-07 10:30:00 |
CURDATE() |
DATE | 2026-06-07 |
CURTIME() |
TIME | 10:30:00 |
日付のみが欲しい場面では CURDATE() を使うのが正確です。NOW() をそのまま DATE 型カラムと比較すると、時刻部分が余分になります。
-- 今日注文された件数を調べる
SELECT COUNT(*) AS today_orders
FROM orders
WHERE ordered_at = CURDATE();
NOW() との違いで覚えておきたいのが SYSDATE() です。NOW() はクエリ実行開始時の時刻を返しますが、SYSDATE() は呼び出した瞬間の時刻を返します。通常のクエリでは差がありませんが、ストアドプロシージャ内のループ処理では挙動が変わります。混乱を避けるために NOW() を使うのが安全です。
日付を加算・減算する:DATE_ADD・DATE_SUB
日付に「〇日後」「〇ヵ月前」を計算するには DATE_ADD / DATE_SUB を使います。構文は次のとおりです。
DATE_ADD(date, INTERVAL value unit)
DATE_SUB(date, INTERVAL value unit)
よく使う unit(間隔の単位)をまとめます。
| unit | 意味 | 記述例 |
|---|---|---|
| DAY | 日 | INTERVAL 7 DAY |
| WEEK | 週 | INTERVAL 2 WEEK |
| MONTH | 月 | INTERVAL 1 MONTH |
| YEAR | 年 | INTERVAL 1 YEAR |
| HOUR | 時間 | INTERVAL 3 HOUR |
| MINUTE | 分 | INTERVAL 30 MINUTE |
注文日から7日後の「期待配送日」を計算する例です。
SELECT
customer,
ordered_at,
DATE_ADD(ordered_at, INTERVAL 7 DAY) AS expected_delivery
FROM orders;
+----------+------------+-------------------+
| customer | ordered_at | expected_delivery |
+----------+------------+-------------------+
| 田中 | 2026-05-01 | 2026-05-08 |
| 佐藤 | 2026-05-15 | 2026-05-22 |
| 鈴木 | 2026-06-01 | 2026-06-08 |
| 高橋 | 2026-06-03 | 2026-06-10 |
+----------+------------+-------------------+
負の INTERVAL を指定すると DATE_SUB と同じ意味になります。DATE_ADD(date, INTERVAL -7 DAY) と DATE_SUB(date, INTERVAL 7 DAY) は等価です。引き算には DATE_SUB を使うと読みやすくなります。
日付の差分を求める:DATEDIFFとTIMESTAMPDIFF
2つの日付の間隔を求める関数は2種類あります。用途に応じて使い分けてください。
DATEDIFF:日数のみを返す
DATEDIFF(expr1, expr2) -- expr1 - expr2 の日数を整数で返す
配送日数(注文日から配送完了日まで)を計算する例です。
SELECT
customer,
ordered_at,
delivered_at,
DATEDIFF(delivered_at, ordered_at) AS delivery_days
FROM orders
WHERE delivered_at IS NOT NULL;
+----------+------------+--------------+---------------+
| customer | ordered_at | delivered_at | delivery_days |
+----------+------------+--------------+---------------+
| 田中 | 2026-05-01 | 2026-05-05 | 4 |
| 佐藤 | 2026-05-15 | 2026-05-20 | 5 |
+----------+------------+--------------+---------------+
引数の順序は (新しい日付, 古い日付) が正の値を返します。逆にすると負の値になるので注意してください。
TIMESTAMPDIFF:単位を指定できる
「〇ヵ月差」「〇時間差」を求めたい場合は TIMESTAMPDIFF を使います。
TIMESTAMPDIFF(unit, datetime_expr1, datetime_expr2)
-- datetime_expr2 - datetime_expr1 を unit 単位の整数で返す
注文日から今日まで何ヵ月経過したかを求める例です。
SELECT
customer,
ordered_at,
TIMESTAMPDIFF(MONTH, ordered_at, CURDATE()) AS months_since_order
FROM orders;
+----------+------------+--------------------+
| customer | ordered_at | months_since_order |
+----------+------------+--------------------+
| 田中 | 2026-05-01 | 1 |
| 佐藤 | 2026-05-15 | 0 |
| 鈴木 | 2026-06-01 | 0 |
| 高橋 | 2026-06-03 | 0 |
+----------+------------+--------------------+
TIMESTAMPDIFF の引数順序は (単位, 古い日付, 新しい日付) で正の値を返します。DATEDIFF と引数の順序が逆なので、混用するときは特に注意してください。
日付を好きな形式に変換する:DATE_FORMAT
日付の表示形式を自由に変えるには DATE_FORMAT を使います。
DATE_FORMAT(date, format)
よく使うフォーマット指定子を表にまとめます。
| 指定子 | 意味 | 例(2026-06-07 の場合) |
|---|---|---|
%Y |
4桁の年 | 2026 |
%y |
2桁の年 | 26 |
%m |
2桁の月(01〜12) | 06 |
%c |
月(1〜12、ゼロ埋めなし) | 6 |
%d |
2桁の日(01〜31) | 07 |
%e |
日(1〜31、ゼロ埋めなし) | 7 |
%H |
時(00〜23) | 10 |
%i |
分(00〜59) | 30 |
%s |
秒(00〜59) | 00 |
%W |
曜日(英語フル) | Sunday |
%w |
曜日番号(0=日曜〜6=土曜) | 0 |
日本語表記に整形する例です。
SELECT
customer,
DATE_FORMAT(ordered_at, '%Y年%m月%d日') AS ordered_jp
FROM orders;
+----------+------------------+
| customer | ordered_jp |
+----------+------------------+
| 田中 | 2026年05月01日 |
| 佐藤 | 2026年05月15日 |
| 鈴木 | 2026年06月01日 |
| 高橋 | 2026年06月03日 |
+----------+------------------+
年月のみでまとめて集計したいときは DATE_FORMAT(ordered_at, '%Y-%m') を GROUP BY に使うのが定番です。
月末・曜日を扱う:LAST_DAY・DAYOFWEEK・DAYNAME
実務でよく使うが見落としがちな関数を3つ紹介します。
LAST_DAY:月の最終日を求める
LAST_DAY(date) は、その月の最終日付を返します。月末締めの処理で重宝します。
SELECT
LAST_DAY('2026-02-01') AS feb_end, -- うるう年も自動判定
LAST_DAY(CURDATE()) AS this_month_end;
+------------+----------------+
| feb_end | this_month_end |
+------------+----------------+
| 2026-02-28 | 2026-06-30 |
+------------+----------------+
「今月の最終日を動的に取得したい」という場面で特に活躍します。うるう年の判定も自動なので、手動で計算する必要はありません。
DAYOFWEEK・DAYNAME:曜日を取得する
DAYOFWEEK(date) は曜日番号(1=日曜、2=月曜…7=土曜)を返します。DAYNAME(date) は英語の曜日名を返します。
SELECT
ordered_at,
DAYOFWEEK(ordered_at) AS day_num, -- 1=日 〜 7=土
DAYNAME(ordered_at) AS day_name
FROM orders;
+------------+---------+-----------+
| ordered_at | day_num | day_name |
+------------+---------+-----------+
| 2026-05-01 | 6 | Friday |
| 2026-05-15 | 6 | Friday |
| 2026-06-01 | 2 | Monday |
| 2026-06-03 | 4 | Wednesday |
+------------+---------+-----------+
「土日を除外して平日だけ集計したい」「月曜始まりでカレンダー集計したい」といった場面で使います。DAYOFWEEK の番号は 1 が日曜始まりなので、月曜を 1 としたい場合は MOD(DAYOFWEEK(date) + 5, 7) + 1 で変換できます。
よくある落とし穴 / 注意点
文字列と DATE 型の暗黙変換に注意する。MySQL は '2026-06-07' のような文字列を DATE 型に自動変換しますが、'20260607' や '2026/06/07' はフォーマットによっては変換に失敗します。確実に変換するには STR_TO_DATE('2026/06/07', '%Y/%m/%d') を使ってください。
DATEDIFFとTIMESTAMPDIFFの引数順序が逆。DATEDIFF(a, b) は a - b の日数を返しますが、TIMESTAMPDIFF(unit, a, b) は b - a を返します。2つを混用するときは引数の順を確認してください。
DATE_ADDで月を加算するとき、月末の扱いに注意する。DATE_ADD('2026-01-31', INTERVAL 1 MONTH) は 2026-02-28 になります(2月が28日しかないため)。「3ヵ月後の同日」を求める処理で意図しない結果になることがあります。
タイムゾーンのずれに注意する。NOW() や CURDATE() は MySQL サーバーのタイムゾーン設定に従います。サーバーが UTC で動いている場合、日本時間と9時間ずれます。CONVERT_TZ(NOW(), '+00:00', '+09:00') で変換するか、SELECT @@time_zone; でサーバー設定を確認してください。
まとめ
MySQL の日付関数は、取得・計算・差分・整形の4つに分けると把握しやすくなります。NOW/CURDATE で現在値を取り、DATE_ADD/DATE_SUB で加減算し、DATEDIFF/TIMESTAMPDIFF で差分を求め、DATE_FORMAT で表示を整える—この4パターンを押さえれば、実務の日付処理はほぼカバーできます。引数の順序やタイムゾーンの落とし穴だけ意識しながら使ってみてください。
参考リンク
アイキャッチ画像: Photo by Shubham Dhage on Unsplash
