日期时间处理
MySQL 提供了丰富的日期时间函数,可以灵活地进行获取、提取、运算、格式化和转换。下面按功能分类总结,并附上常用的计算场景和示例。
1. 获取当前日期和时间
| 函数 | 说明 |
|---|
NOW() | 当前日期时间(语句开始时确定) |
CURDATE() | 当前日期 |
CURTIME() | 当前时间 |
SYSDATE() | 执行时的实时日期时间 |
UTC_DATE(), UTC_TIME(), UTC_TIMESTAMP() | 返回 UTC 时间 |
SELECT NOW(); -- 2026-05-26 14:30:00
SELECT CURDATE(); -- 2026-05-26
SELECT CURTIME(); -- 14:30:00
SELECT UTC_TIMESTAMP(); -- 2026-05-26 06:30:00
2. 提取日期/时间分量
| 函数 | 说明 |
|---|
YEAR(date), MONTH(date), DAY(date) | 年、月、日 |
HOUR(time), MINUTE(time), SECOND(time) | 时、分、秒 |
QUARTER(date) | 季度(1-4) |
MONTHNAME(date), DAYNAME(date) | 月份名、星期名 |
DAYOFWEEK(date) | 星期几(1=周日,7=周六) |
WEEKDAY(date) | 星期几(0=周一,6=周日) |
DAYOFYEAR(date) | 一年中的第几天 |
WEEK(date[,mode]) | 周数 |
EXTRACT(unit FROM date) | 灵活提取,如 YEAR_MONTH |
SELECT YEAR('2026-05-26'); -- 2026
SELECT MONTHNAME('2026-05-26'); -- May
SELECT DAYOFWEEK('2026-05-26'); -- 3 (周二)
SELECT WEEKDAY('2026-05-26'); -- 1 (周二)
SELECT EXTRACT(YEAR_MONTH FROM '2026-05-26'); -- 202605
3. 日期/时间运算
| 函数 | 说明 |
|---|
DATE_ADD(date, INTERVAL expr unit) | 日期加 |
DATE_SUB(date, INTERVAL expr unit) | 日期减 |
ADDDATE(date, INTERVAL expr unit) 或 ADDDATE(date, days) | 与上面相同(两用法) |
SUBDATE(date, INTERVAL expr unit) 或 SUBDATE(date, days) | 相减 |
date + INTERVAL expr unit / date - INTERVAL expr unit | 直接运算符 |
DATEDIFF(date1, date2) | 日期差(天数) |
TIMEDIFF(time1, time2) | 时间差 |
TIMESTAMPDIFF(unit, dt1, dt2) | 按单位返回差值 |
PERIOD_ADD(period, n) | 给 YYYYMM 加 n 个月 |
PERIOD_DIFF(p1, p2) | 两个 YYYYMM 相差月数 |
常用 unit:MICROSECOND, SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, YEAR
复合 unit:YEAR_MONTH, DAY_HOUR, DAY_MINUTE, DAY_SECOND, HOUR_MINUTE, HOUR_SECOND, MINUTE_SECOND 等。
SELECT DATE_ADD('2026-05-26', INTERVAL 1 MONTH); -- 2026-06-26
SELECT DATE_SUB('2026-05-26', INTERVAL 10 DAY); -- 2026-05-16
SELECT '2026-05-26' + INTERVAL 1 YEAR; -- 2027-05-26
SELECT DATEDIFF('2026-06-01', '2026-05-26'); -- 6
SELECT TIMESTAMPDIFF(MONTH, '2025-01-15', '2026-05-26'); -- 16
SELECT PERIOD_DIFF(202605, 202412); -- 5
4. 格式化与解析
| 函数 | 说明 |
|---|
DATE_FORMAT(date, format) | 日期/时间按格式输出 |
TIME_FORMAT(time, format) | 仅格式化时间 |
STR_TO_DATE(str, format) | 字符串 → 日期时间 |
GET_FORMAT(type, region) | 预定义格式 |
常用格式符:%Y(4位年), %y(2位年), %m(月), %d(日), %H(24小时), %i(分), %s(秒), %W(星期名), %M(月份名) 等。
SELECT DATE_FORMAT(NOW(), '%Y年%m月%d日 %H:%i:%s'); -- 2026年05月26日 14:30:00
SELECT STR_TO_DATE('26/05/2026', '%d/%m/%Y'); -- 2026-05-26
5. 构造与类型转换
| 函数 | 说明 |
|---|
MAKEDATE(year, dayofyear) | 由年和第几天生成日期 |
MAKETIME(h, m, s) | 生成时间 |
DATE(expr) | 截取日期部分 |
TIME(expr) | 截取时间部分 |
TIMESTAMP(expr) / TIMESTAMP(d, t) | 返回 datetime |
FROM_UNIXTIME(ts[, format]) | Unix 时间戳 → 日期 |
UNIX_TIMESTAMP([date]) | 日期 → Unix 时间戳 |
CONVERT_TZ(dt, from_tz, to_tz) | 时区转换 |
CAST(expr AS DATETIME) | 类型转换 |
SELECT MAKEDATE(2026, 146); -- 2026-05-26
SELECT DATE('2026-05-26 14:30:00'); -- 2026-05-26
SELECT FROM_UNIXTIME(1760000000); -- 转换时间戳
SELECT UNIX_TIMESTAMP('2026-05-26'); -- 对应时间戳
SELECT CONVERT_TZ('2026-05-26 12:00:00','+00:00','+08:00'); -- 时区转换(需时区表)
6. 其他实用函数
| 函数 | 说明 |
|---|
LAST_DAY(date) | 该月最后一天 |
TO_DAYS(date) | 从公元0年算起的天数 |
FROM_DAYS(n) | 天数 → 日期 |
TIME_TO_SEC(time) | 时间 → 秒数 |
SEC_TO_TIME(seconds) | 秒数 → 时间 |
TO_SECONDS(expr) | 从公元0年算起的秒数 |
SELECT LAST_DAY('2026-05-26'); -- 2026-05-31
SELECT TO_DAYS('2026-05-26'); -- 739759
SELECT SEC_TO_TIME(3661); -- 01:01:01
常用日期时间计算方法
1. 计算两个日期相差天数/月数/年数
-- 相差天数
SELECT DATEDIFF('2026-06-01', '2026-05-26'); -- 6
-- 相差月数(忽略日差)
SELECT TIMESTAMPDIFF(MONTH, '2025-01-15', '2026-05-26'); -- 16
-- 相差年数(常用于年龄)
SELECT TIMESTAMPDIFF(YEAR, '1990-05-15', CURDATE()); -- 36
2. 计算精确年龄(过完生日才算一岁)
SELECT TIMESTAMPDIFF(YEAR, birth, CURDATE())
FROM users;
-- 或使用公式
SELECT (YEAR(CURDATE())-YEAR(birth)) -
(DATE_FORMAT(CURDATE(),'%m%d') < DATE_FORMAT(birth,'%m%d'))
FROM users;
3. 本月第一天、最后一天
-- 月初
SELECT DATE_SUB(CURDATE(), INTERVAL DAYOFMONTH(CURDATE())-1 DAY);
SELECT DATE_FORMAT(CURDATE(), '%Y-%m-01'); -- 字符串形式
-- 月末
SELECT LAST_DAY(CURDATE()); -- 2026-05-31
4. 上月最后一天、下月第一天
-- 上月最后一天
SELECT LAST_DAY(DATE_SUB(CURDATE(), INTERVAL 1 MONTH));
-- 下月第一天
SELECT DATE_ADD(LAST_DAY(CURDATE()), INTERVAL 1 DAY);
5. 本周一(以周一为起始)
SELECT DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY);
-- 若当前 2026-05-26 是周二,得 2026-05-25(周一)
6. 本周日、下周一
-- 本周日
SELECT DATE_ADD(CURDATE(), INTERVAL (6 - WEEKDAY(CURDATE())) DAY);
-- 下一个周一(如果今天是周一,则返回下周一的日期)
SELECT DATE_ADD(CURDATE(), INTERVAL (7 - WEEKDAY(CURDATE())) DAY);
7. 简单判断工作日(跳过周六日)
-- 给定日期 @d 的下一个工作日(不考虑节假日)
SELECT CASE DAYOFWEEK(@d)
WHEN 7 THEN DATE_ADD(@d, INTERVAL 2 DAY) -- 周六 -> 周一
WHEN 1 THEN DATE_ADD(@d, INTERVAL 1 DAY) -- 周日 -> 周一
ELSE DATE_ADD(@d, INTERVAL 1 DAY)
END AS next_workday;
8. 季度第一天、季度最后一天
-- 当前季度的第一天
SELECT MAKEDATE(YEAR(CURDATE()), 1) + INTERVAL (QUARTER(CURDATE())-1)*3 MONTH;
-- 当前季度最后一天
SELECT LAST_DAY(MAKEDATE(YEAR(CURDATE()), 1) + INTERVAL QUARTER(CURDATE())*3 MONTH - INTERVAL 1 MONTH);
9. 判断闰年(某年2月有29天)
SELECT DAYOFMONTH(LAST_DAY(CONCAT(@year,'-02-01'))) = 29 AS is_leap;
10. 计算两个时间点的小时/分钟差(如工作时长)
SELECT TIMESTAMPDIFF(MINUTE, '2026-05-26 09:00:00', '2026-05-26 18:30:00') / 60 AS work_hours;
-- 9.5 小时
SELECT SEC_TO_TIME(TIMESTAMPDIFF(SECOND, start_time, end_time)) AS duration;
提示
- 进行月份加减时,若目标月没有该日(如1月31日加1个月),MySQL 会自动调整到该月最后一天(结果为2月28/29日)。
- 时区转换函数
CONVERT_TZ 依赖系统时区表,使用时需确保 mysql.time_zone 表已填充数据(可用 mysql_tzinfo_to_sql 加载),否则会返回 NULL。
- 直接使用
+ INTERVAL 语法与 DATE_ADD 功能等价,写法更简洁。
掌握上述函数与计算方法,基本能覆盖绝大部分业务场景中的日期时间处理需求。