- 1 1. 在 MySQL 中获取当前日期/时间的简短 SQL
- 2 2. NOW() / CURRENT_TIMESTAMP / SYSDATE() 的区别
- 3 3. 更改当前日期/时间的显示格式
- 4 4. 使用当前时间进行日期时间计算
- 5 5. 对于范围查询,这比 BETWEEN 更安全
- 6 6. DEFAULT CURRENT_TIMESTAMP and ON UPDATE (table design basics)
- 7 6. DEFAULT CURRENT_TIMESTAMP and ON UPDATE(表设计基础)
- 7.1 6.1 Auto-set created_at (DEFAULT CURRENT_TIMESTAMP)
- 7.2 6.1 自动设置 created_at(DEFAULT CURRENT_TIMESTAMP)
- 7.3 6.2 Auto-update updated_at (ON UPDATE)
- 7.4 6.2 自动更新 updated_at(ON UPDATE)
- 7.5 6.3 DATETIME vs TIMESTAMP (important)
- 7.6 6.3 DATETIME vs TIMESTAMP(重要)
- 7.7 6.4 CURRENT_TIMESTAMP can also be used with DATETIME
- 7.8 6.4 CURRENT_TIMESTAMP 也可用于 DATETIME
- 7.9 6.5 If you want to use NOW(): an alternative (trigger)
- 7.10 6.5 如果想使用 NOW():另一种方式(触发器)
- 7.11 常见设计错误
- 7.12 本节关键要点
- 8 7. 时区设计(以 UTC 存储,在本地显示)
- 9 8. 可直接使用的实用示例
- 10 9. 常见问题解答 (FAQ)
- 11 10. 总结
1. 在 MySQL 中获取当前日期/时间的简短 SQL
如果您想在 MySQL 中获取当前日期/时间,首先要记住的函数是 NOW() 和 CURRENT_TIMESTAMP。以下是按用例分类的最短 SQL 示例。
1.1 获取当前日期/时间(基本)
SELECT NOW();
或
SELECT CURRENT_TIMESTAMP;
- 两者都返回 当前日期/时间 (YYYY-MM-DD HH:MM:SS) 。
- 在同一查询中,时间固定在“查询开始时间”。
用例
- 日志记录
- 获取记录创建时间戳
- 获取参考时间
常见错误
- 应用时区和数据库时区不同,导致“时间偏移”。 → 使用
SELECT @@session.time_zone;检查
1.2 仅获取今天的日期
SELECT CURDATE();
- 返回格式:
YYYY-MM-DD - 类型是 DATE(仅日期,非日期时间)
用例
- 搜索“今天的数据”
- 每日聚合
注意
- 与
NOW()不同,它不包含时间。 - 如果需要用于范围查询的时间,请使用
NOW()。
1.3 仅获取当前时间
SELECT CURTIME();
- 返回格式:
HH:MM:SS - 类型是 TIME(仅时间)
用例
- 显示批处理执行时间
- 在日志中仅打印时间部分
常见陷阱
- 因为不包含日期,无法用于日期时间比较。
1.4 获取 UTC 当前时间(重要)
SELECT UTC_TIMESTAMP();
- 无论服务器时区如何,都返回 UTC。
- 推荐用于全球服务。
实际项目中的基准策略
- 以 UTC 存储
- 显示时转换为本地时间
注意
- 如果应用已转换为 UTC,请注意避免双重转换。
1.5 获取毫秒 / 微秒
SELECT NOW(3); -- milliseconds (3 digits after the decimal point)
SELECT NOW(6); -- microseconds (6 digits after the decimal point)
- MySQL 5.6.4 及更高版本可用。
- 用于高精度日志和事务审计。
常见误解
- 单独的
NOW()无法返回小数秒。 - 您的列也必须指定精度,例如
DATETIME(6)。
1.6 按用例的最佳函数(不确定时从这里开始)
| Use case | Recommended function |
|---|---|
| General current datetime | NOW() |
| Table default value | CURRENT_TIMESTAMP |
| Date only | CURDATE() |
| Time only | CURTIME() |
| Unified UTC management | UTC_TIMESTAMP() |
| High-precision logs | NOW(6) |
人们常忽略的关键点
NOW()和CURRENT_TIMESTAMP实际上等效(相同值)- 在同一查询中,时间是固定的
- 显示的值取决于时区设置
- 对于微秒,您的列定义必须指定精度
2. NOW() / CURRENT_TIMESTAMP / SYSDATE() 的区别
有多个函数可以获取当前时间,但 最令人困惑的部分是 NOW()、CURRENT_TIMESTAMP 和 SYSDATE() 之间的区别。
如果您没有正确理解这一点,可能会在日志和事务处理中出现意外行为。
2.1 NOW() 和 CURRENT_TIMESTAMP 实际上等效
SELECT NOW(), CURRENT_TIMESTAMP;
- 两者都返回 当前日期时间(DATETIME 值) 。
- 在同一查询中,时间固定在“查询开始时间”。
CURRENT_TIMESTAMP也可以使用函数调用语法:SELECT CURRENT_TIMESTAMP();
实际项目中的选择方式
| Use case | Recommended |
|---|---|
| Simple retrieval | NOW() |
| Table DEFAULT / ON UPDATE | CURRENT_TIMESTAMP |
重要点
- 检索值的含义相同。
- 主要区别在于语法/用法(例如,允许在 DEFAULT 中使用)。
- 不是类型区别(容易误解)。
2.2 SYSDATE() 返回“求值时间”的时间
SYSDATE() 看起来类似于 NOW(),但 值固定的时机不同。
SELECT NOW(), SLEEP(2), NOW();
结果(示例):
2025-02-23 14:00:00
2025-02-23 14:00:00
SELECT SYSDATE(), SLEEP(2), SYSDATE();
结果(示例):
2025-02-23 14:00:00
2025-02-23 14:00:02
本质区别
| Function | When the time is fixed |
|---|---|
NOW() | Query start time |
SYSDATE() | Evaluation time |
换句话说,随着时间流逝,SYSDATE() 在同一查询中可能会变化。
2.3 事务和复制的注意事项
因为 NOW() 在查询开始时就固定下来,在事务内部需要一致的参考时间时更安全。
另一方面,SYSDATE() 取决于执行时机,可能会影响以下场景的可复现性:
- 复制
- 批处理作业
- 批量日志处理
经验法则
- 需要固定的参考时间 →
NOW() - 每次都需要精确的瞬间 →
SYSDATE()(使用受限)
2.4 注:CURRENT_DATE / CURRENT_TIME / LOCALTIME
MySQL 还支持:
SELECT CURRENT_DATE;
SELECT CURRENT_TIME;
SELECT LOCALTIME;
CURRENT_DATE→ 等同于CURDATE()CURRENT_TIME→ 等同于CURTIME()LOCALTIME→ 等同于NOW()
常见混淆
- 这些大多是“表面差异”,功能上几乎没有区别。
- 为安全起见,优先考虑可读性,并在项目中统一使用。
本节关键要点
NOW()与CURRENT_TIMESTAMP基本意义相同。SYSDATE()行为不同,因为它基于评估时间。- 需要固定参考时间时,默认使用
NOW()。 - 在 DEFAULT 和 ON UPDATE 中使用
CURRENT_TIMESTAMP。
3. 更改当前日期/时间的显示格式
使用 NOW() 获取的当前时间默认显示为 YYYY-MM-DD HH:MM:SS。在实际工作中,你常常需要以下形式:
- 仅显示日期
- 使用
YYYY/MM/DD - 使用本地化格式(例如 Feb 23, 2025)
- 显示毫秒
为此,可使用 DATE_FORMAT()(将 datetime 转换为任意字符串格式)。
3.1 DATE_FORMAT() 的基本语法
DATE_FORMAT(datetime, 'format_string')
示例:将当前时间转换为 YYYY/MM/DD HH:MM
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d %H:%i');
输出示例:
2025/02/23 14:35
常用格式说明符
| Specifier | Meaning | Example |
|---|---|---|
%Y | 4-digit year | 2025 |
%m | 2-digit month | 02 |
%d | 2-digit day | 23 |
%H | Hour (24-hour) | 14 |
%i | Minutes | 35 |
%s | Seconds | 50 |
%f | Microseconds | 123456 |
注意事项
- 某些说明符在大小写之间有所区别。
%h为 12 小时制,%H为 24 小时制。
3.2 常见格式示例(实际工作中常用)
1. 斜杠分隔
SELECT DATE_FORMAT(NOW(), '%Y/%m/%d');
2. 本地化(英文)
SELECT DATE_FORMAT(NOW(), '%b %d, %Y');
3. 不带秒数
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i');
常见陷阱
- 因为返回的是字符串,不能用于数值计算。
- 进行比较和搜索时,应使用原始的 DATETIME 类型。
3.3 仅提取日期或时间部分
也可以在保持类型的前提下提取部分(而不是格式化为字符串)。
SELECT DATE(NOW()); -- date only (DATE type)
SELECT TIME(NOW()); -- time only (TIME type)
SELECT YEAR(NOW()); -- year only
SELECT MONTH(NOW()); -- month only
SELECT DAY(NOW()); -- day only
推荐用法
| Goal | Recommended |
|---|---|
| Formatting for display | DATE_FORMAT |
| Calculation / comparison | DATE() / TIME() |
3.4 显示微秒
当需要高精度日志时:
SELECT NOW(6);
使用格式化:
SELECT DATE_FORMAT(NOW(6), '%Y-%m-%d %H:%i:%s.%f');
重要说明
- 列必须是
DATETIME(6)或TIMESTAMP(6),否则精度不会被保存。 - 在 MySQL 5.6.4 以下版本不可用(取决于环境)。
常见错误
在 WHERE 子句中使用
DATE_FORMAT()会导致索引失效。 wp:list /wp:list- Bad example:
WHERE DATE_FORMAT(created_at, '%Y-%m-%d') = '2025-02-23' - Recommended:
WHERE created_at >= '2025-02-23 00:00:00' AND created_at < '2025-02-24 00:00:00' - 直接存储显示格式的字符串(会影响搜索性能)
- Bad example:
本节关键要点
- 使用
DATE_FORMAT()更改显示格式。 - 进行计算/比较时,保持原始类型。
- 若需要微秒,请在列定义中指定精度。
- 在 WHERE 子句中使用函数可能导致性能问题。
4. 使用当前时间进行日期时间计算
在 MySQL 中,常常需要基于当前时间计算诸如 “N 小时后”、 “N 天前” 或 “过去 N 天” 等。最常见的实际场景是 “获取过去 24 小时的数据”。
日期时间算术的基础是 INTERVAL(用于添加/减去时间单位的语法)。
4.1 使用 INTERVAL 加/减
获取 1 小时后
SELECT NOW() + INTERVAL 1 HOUR;
获取 3 天前
SELECT NOW() - INTERVAL 3 DAY;
获取 1 个月后
SELECT NOW() + INTERVAL 1 MONTH;
常用单位
| Unit | Meaning |
|---|---|
| SECOND | Seconds |
| MINUTE | Minutes |
| HOUR | Hours |
| DAY | Days |
| WEEK | Weeks |
| MONTH | Months |
| YEAR | Years |
常见错误
DAY在INTERVAL 1 DAY中即使使用小写也能工作,但应在团队内部统一规范。- 月末计算可能与预期不符,因为天数会变化(例如,1 月 31 日加 1 个月)。
4.2 获取过去 24 小时的数据(最常用模式)
SELECT *
FROM users
WHERE created_at >= NOW() - INTERVAL 1 DAY;
含义
- 目标是“从现在起往回 24 小时”。
注意事项
- 如果使用
CURDATE(),基准时间会变成“今天的 00:00”,从而改变含义。
示例(今天的数据):
SELECT *
FROM users
WHERE created_at >= CURDATE();
理解差异至关重要
| Expression | Meaning |
|---|---|
NOW() - INTERVAL 1 DAY | Past 24 hours |
CURDATE() | Since today 00:00 |
4.3 使用 DATEDIFF() 获取天数差异
SELECT DATEDIFF('2025-03-01', '2025-02-23');
结果:
6
- 单位为“天”。
- 时间部分被忽略。
注意事项
- 符号取决于参数的顺序。
- 不能计算小时/分钟/秒的差异。
4.4 使用 TIMESTAMPDIFF() 计算小时/分钟/秒
SELECT TIMESTAMPDIFF(HOUR,
'2025-02-23 12:00:00',
'2025-02-23 18:30:00');
结果:
6
分钟
SELECT TIMESTAMPDIFF(MINUTE,
'2025-02-23 12:00:00',
'2025-02-23 12:30:00');
秒
SELECT TIMESTAMPDIFF(SECOND,
'2025-02-23 12:00:00',
'2025-02-23 12:00:45');
使用场景
- 会话时长计算
- 过期检查
- 超时决策
4.5 获取月份开始/结束(常见实际陷阱)
获取本月的第一天:
SELECT DATE_FORMAT(NOW(), '%Y-%m-01');
下个月的第一天:
SELECT DATE_FORMAT(NOW() + INTERVAL 1 MONTH, '%Y-%m-01');
注意事项
- 月末不是固定的日期。
- 对于范围查询,使用 “>= 开始 AND < 下个月的第一天” 是安全的。
常见错误汇总
- 在仅使用日期值时使用
BETWEEN且忘记时间 - 在 WHERE 子句中使用
DATE(created_at)导致索引失效 - 在月份计算中被 “31 日” 问题困住
- 误解
NOW()与CURDATE()的区别
本节关键要点
INTERVAL是日期时间算术的基础。- 过去 24 小时 =
NOW() - INTERVAL 1 DAY。 - 天数使用
DATEDIFF();更小的单位使用TIMESTAMPDIFF()。 - 在基于月份的计算中要注意结束条件。
5. 对于范围查询,这比 BETWEEN 更安全
在 MySQL 中进行日期范围搜索时,许多初学者会使用 BETWEEN。然而,如果处理时间边界不当,它常常会产生意外结果,因此在实际工作中推荐使用更安全的模式。
5.1 BETWEEN 基础及陷阱
常见查询
SELECT *
FROM orders
WHERE order_date BETWEEN '2025-02-01' AND '2025-02-10';
看起来没问题,但内部等价于:
WHERE order_date >= '2025-02-01 00:00:00'
AND order_date <= '2025-02-10 00:00:00'
因此 它不包括 2 月 10 日 00:00 之后的数据。
5.2 更安全的模式(推荐)
推荐模式
SELECT *
FROM orders
WHERE order_date >= '2025-02-01 00:00:00'
AND order_date < '2025-02-11 00:00:00';
关键点
- 将结束边界指定为“次日 00:00 之前”
- 比
<= 23:59:59更安全(支持微秒)
5.3 获取今天数据的正确方式
Bad example:
错误示例:
WHERE DATE(created_at) = CURDATE();
Problems:
问题:
- 对列应用函数会 禁用索引
- 可能在大表上导致严重的性能下降
Recommended:
推荐:
WHERE created_at >= CURDATE()
AND created_at < CURDATE() + INTERVAL 1 DAY;
This ensures:
这确保了:
- 可以使用索引
- 快速搜索
- 没有边界问题
5.4 过去 7 天 / 过去 30 天的安全模式
Past 7 days
过去 7 天
WHERE created_at >= NOW() - INTERVAL 7 DAY;
Past 30 days
过去 30 天
WHERE created_at >= NOW() - INTERVAL 30 DAY;
Notes
注意
CURDATE() - INTERVAL 7 DAY基于“今天 00:00”NOW() - INTERVAL 7 DAY基于“当前时间”- 根据需求进行选择
5.5 保持索引有效的关键规则
Bad:
错误:
WHERE DATE(created_at) = '2025-02-23';
Good:
正确:
WHERE created_at >= '2025-02-23 00:00:00'
AND created_at < '2025-02-24 00:00:00';
Why:
原因:
- 索引作用于“列本身”
- 对列使用函数会阻止索引使用(全表扫描)
Common mistakes summary
常见错误汇总
- 在
BETWEEN中忘记在结束边界包含时间 - 使用
23:59:59时遗漏微秒 - 在 WHERE 子句中使用
DATE()导致查询变慢 - 对 “now” 与 “today 00:00” 的区别不明确
Key takeaways from this section
本节要点
- 范围查询使用
>= start AND < end最安全。 BETWEEN需要谨慎处理边界。- 为保持索引有效,不要在列上使用函数包装。
- 明确选择基于
NOW()或基于CURDATE()的逻辑。
6. DEFAULT CURRENT_TIMESTAMP and ON UPDATE (table design basics)
6. DEFAULT CURRENT_TIMESTAMP and ON UPDATE(表设计基础)
在数据库设计中,通常会 自动管理 created_at 和 updated_at。
在 MySQL 中,CURRENT_TIMESTAMP 允许在插入和更新时自动设置当前时间。
6.1 Auto-set created_at (DEFAULT CURRENT_TIMESTAMP)
6.1 自动设置 created_at(DEFAULT CURRENT_TIMESTAMP)
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Behavior:
行为:
INSERT INTO users (name) VALUES ('Alice');
→ 当前时间会自动插入到 created_at。
Key points
关键点
CURRENT_TIMESTAMP可以在 DEFAULT 中使用。NOW()不能直接在 DEFAULT 中使用。
Common mistake
常见错误
created_at DATETIME DEFAULT NOW(); -- error
Reason:
原因:
- 在 MySQL 中,通常不能将函数设为 DEFAULT(唯一例外是 CURRENT_TIMESTAMP)。
6.2 Auto-update updated_at (ON UPDATE)
6.2 自动更新 updated_at(ON UPDATE)
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
);
Behavior:
行为:
UPDATE users SET name = 'Bob' WHERE id = 1;
→ updated_at 会自动更新为当前时间。
Use cases
使用场景
- 最近登录追踪
- 更新历史
- 审计日志
6.3 DATETIME vs TIMESTAMP (important)
6.3 DATETIME vs TIMESTAMP(重要)
| Type | Range | Time zone impact |
|---|---|---|
| DATETIME | Year 1000 to 9999 | No |
| TIMESTAMP | 1970 to 2038 | Yes |
The essential difference
本质区别
TIMESTAMP在内部以 UTC 存储,显示时会转换为会话时区。DATETIME直接存储字面值(不进行转换)。
Guidelines
使用指南
| Case | Recommended type |
|---|---|
| Log management | TIMESTAMP |
| Future dates (after 2038) | DATETIME |
| Fixed values not requiring TZ conversion | DATETIME |
6.4 CURRENT_TIMESTAMP can also be used with DATETIME
6.4 CURRENT_TIMESTAMP 也可用于 DATETIME
在 MySQL 5.6 及以后版本中,也可以为 DATETIME 列指定 CURRENT_TIMESTAMP 作为 DEFAULT 和 ON UPDATE。
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
Notes
注意
- 较旧的 MySQL 版本有限制(取决于环境)。
- 如果需要更高精度:
DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6)
6.5 If you want to use NOW(): an alternative (trigger)
6.5 如果想使用 NOW():另一种方式(触发器)
CREATE TRIGGER set_created_at
BEFORE INSERT ON logs
FOR EACH ROW
SET NEW.created_at = NOW();
Use cases
使用场景
- 复杂的初始值逻辑
- 条件时间戳分配
注意
- 触发器难以调试。
- 除非必要,否则避免使用。
常见设计错误
- 同时在
created_at和updated_at上添加 ON UPDATE - 混淆 DATETIME 与 TIMESTAMP 的行为
- 推迟时区决策
本节关键要点
- 使用
CURRENT_TIMESTAMP作为 DEFAULT。 - 使用
ON UPDATE CURRENT_TIMESTAMP进行更新追踪。 - 根据时区行为和 2038 年问题选择数据类型。
- 如需精度,请不要忘记
(6)。
7. 时区设计(以 UTC 存储,在本地显示)
在处理当前时间时,不良的时区设计会导致时间漂移和双重转换。
实际中,最安全的默认做法是“以 UTC 存储,在显示时转换为本地时间”。

图:在 MySQL 中以 UTC 存储并在本地显示的基本架构
7.1 检查当前时区
首先,检查 MySQL 正在使用的时区。
SELECT @@global.time_zone, @@session.time_zone;
@@global.time_zone→ 全局服务器设置@@session.time_zone→ 当前连接/会话设置- 如果显示为
SYSTEM,则取决于操作系统的设置
常见问题
- 生产和开发服务器的操作系统时区不同
- 应用将时间转换为 UTC,数据库再次转换(双重转换)
7.2 为会话更改时区
SET time_zone = 'Asia/Tokyo';
或统一为 UTC:
SET time_zone = '+00:00';
重要点
- 连接关闭后会恢复默认
- 使用连接池时,请检查应用程序设置
7.3 为什么使用 UTC(实际原则)
推荐策略
- 将数据以 UTC 存储
- 显示时转换为用户所在时区
原因:
- 更易支持国际化
- 避免夏令时(DST)问题
- 在迁移服务器时影响最小化
获取 UTC:
SELECT UTC_TIMESTAMP();
7.4 使用 CONVERT_TZ() 转换时区
示例:UTC → JST
SELECT CONVERT_TZ('2025-02-23 05:35:00', '+00:00', '+09:00');
使用时区名称:
SELECT CONVERT_TZ('2025-02-23 05:35:00', 'UTC', 'Asia/Tokyo');
实际示例
SELECT CONVERT_TZ(created_at, 'UTC', 'Asia/Tokyo')
FROM users;
7.5 为什么 CONVERT_TZ() 返回 NULL
如果返回 NULL,常见原因包括:
- MySQL 时区表未加载
- 指定的时区名称不存在
Linux 上的加载示例:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
注意
- 在生产环境中,确认权限并检查是否需要重启
- 在 Docker 中,zoneinfo 可能未随镜像一起包含,取决于具体镜像
7.6 更改全局服务器时区
配置文件(my.cnf / my.ini):
[mysqld]
default_time_zone = '+00:00'
重启后验证:
SELECT @@global.time_zone;
重要
- 对生产环境的更改,请确认影响范围
- 注意已有数据(有时仅是显示层面的更改)
常见错误
- 先以本地时间存储,后期再添加国际化支持
- 应用与数据库之间的双重转换
- 使用 DATETIME 设计时未考虑时区
- 测试与生产的时区设置不一致
本节关键要点
- 以 UTC 存储,显示时进行转换。
- 使用
UTC_TIMESTAMP()。 - 使用
CONVERT_TZ()时,验证时区表。 - 始终考虑环境之间的时区差异。
8. 可直接使用的实用示例
下面提供具体的 SQL 示例,展示 如何在实际开发/运维中使用 MySQL 当前时间。这些示例可直接复制粘贴使用。
8.1 自动向日志插入当前时间
创建表
CREATE TABLE system_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
level VARCHAR(50),
message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
插入日志
INSERT INTO system_logs (level, message)
VALUES ('ERROR', 'Failed to connect to the DB');
关键点
created_at自动插入- 无需从应用传递时间戳
- 对审计和事件调查至关重要的设计
常见错误
- 在应用和数据库中都管理时间
- 由于时区不统一导致的时间不一致
8.2 更新最后登录时间
表设计
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255),
last_login TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
);
登录时更新
UPDATE users
SET last_login = NOW()
WHERE id = 1;
备注
ON UPDATE仅在列值变化时触发- 某些 UPDATE 语句可能未改变值,从而不触发更新
8.3 在批处理作业中固定参考时间
对于长时间运行的进程,固定参考时间更安全。
SET @base_time = NOW();
SELECT *
FROM orders
WHERE created_at >= @base_time - INTERVAL 1 DAY;
原因
- 时间在过程进行中不会变化
- 保持一致性
8.4 今日/昨日/过去7天的聚合
今日销售
SELECT SUM(amount)
FROM orders
WHERE created_at >= CURDATE()
AND created_at < CURDATE() + INTERVAL 1 DAY;
昨日销售
SELECT SUM(amount)
FROM orders
WHERE created_at >= CURDATE() - INTERVAL 1 DAY
AND created_at < CURDATE();
过去7天
SELECT COUNT(*)
FROM users
WHERE created_at >= NOW() - INTERVAL 7 DAY;
重要
- 不要在 WHERE 子句中对列使用函数
- 编写能够保持索引可用的条件
8.5 过期检查(会话/令牌)
SELECT *
FROM sessions
WHERE expires_at > NOW();
删除过期行
DELETE FROM sessions
WHERE expires_at <= NOW();
常见错误
- 使用
CURDATE()并忽略时间 - 存储 UTC 但使用本地
NOW()进行比较
8.6 获取在 N 小时内过期的行
SELECT *
FROM coupons
WHERE expires_at <= NOW() + INTERVAL 1 HOUR;
使用场景:
- 过期警报
- 过期通知
您必须始终牢记的要点
- 明确基准是“现在”还是“今天 00:00”
- 编写能够保持索引有效的谓词
- 提前决定时区设计
- 未制定明确策略时不要混用
NOW()和UTC_TIMESTAMP()
本节关键要点
- 对于日志/审计/更新跟踪,使用
CURRENT_TIMESTAMP - 对于聚合,使用安全的
>= AND <模式 - 对于会话管理,使用
NOW()进行比较 - 固定基准时间以保持处理一致性
9. 常见问题解答 (FAQ)
以下是关于 MySQL 当前时间的常见问题,特别是实际工作中出现的陷阱。
9.1 NOW() 与 CURRENT_TIMESTAMP 有何区别?
结论:返回值的含义相同。
SELECT NOW(), CURRENT_TIMESTAMP;
两者都返回当前日期时间。
主要区别在于语法使用
CURRENT_TIMESTAMP可用于DEFAULT和ON UPDATENOW()不能直接用于 DEFAULT
备注
- 这不是类型差异
- 可以使用类似
(6)的参数指定精度
9.2 是否应该使用 SYSDATE()?
SYSDATE() 返回“评估时刻”的时间。
SELECT SYSDATE(), SLEEP(2), SYSDATE();
即使在同一查询中,该值也可能会变化。
何时使用
- 当需要记录精确的真实时间点时
何时避免
- 复制
- 对一致性重要的事务处理
大多数情况下,使用 NOW() 已足够。
9.3 为什么时间会偏移?
主要原因:
- 服务器时区设置
- 会话时区设置
- 应用程序的双重转换
- TIMESTAMP 类型的自动转换行为
检查方式:
SELECT @@global.time_zone, @@session.time_zone;
缓解措施
- 默认使用 UTC 存储
- 仅在显示时进行转换
- 统一应用和数据库之间的策略
9.4 CONVERT_TZ() 返回 NULL
原因:
- 时区表未加载
- 时区名称不正确
解决方法:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
在 Docker 环境中需特别小心。
9.5 使用 BETWEEN 的范围偏移
错误示例:
WHERE created_at BETWEEN '2025-02-01' AND '2025-02-10';
正确示例:
WHERE created_at >= '2025-02-01 00:00:00'
AND created_at < '2025-02-11 00:00:00';
原因:
- 结束边界时间问题
- 微秒泄漏
- 索引效率
9.6 如何在 DATETIME 与 TIMESTAMP 之间做选择?
- 国际化支持 / UTC 管理 → TIMESTAMP
- 2038 年之后或固定日期时间 → DATETIME
在设计阶段决定。
9.7 微秒未被存储
原因:
- 列定义中未指定精度
解决方法:
created_at DATETIME(6)
本节关键要点
- 以
NOW()和CURRENT_TIMESTAMP为起点 - 范围查询使用
>= AND < - UTC 存储是最安全的默认方式
- 不要忘记类型选择和精度
10. 总结
本文整理了在 MySQL 中检索、格式化、计算和管理当前时间的实用方法。
最后,这里是您应了解的 保持安全的最基本要点。
10.1 获取当前时间(基础)
- 通用获取 →
NOW() - 表的 DEFAULT / 更新追踪 →
CURRENT_TIMESTAMP - 仅日期 →
CURDATE() - 仅时间 →
CURTIME() - UTC →
UTC_TIMESTAMP() - 高精度 →
NOW(6)
经验法则
- 如果不确定,使用
NOW()在大多数情况下是可以的。 - 在模式设计时,使用
CURRENT_TIMESTAMP。
10.2 范围查询:“>= AND <” 是黄金法则
安全模式:
WHERE created_at >= 'START'
AND created_at < 'END'
原因:
- 防止遗漏结束边界的行
- 支持微秒
- 保持索引可用
错误示例
WHERE DATE(created_at) = CURDATE();
不要在列上使用函数包装。
10.3 日期时间算术基础
- 加/减 →
INTERVAL - 天数差异 →
DATEDIFF() - 时间差异 →
TIMESTAMPDIFF()
始终记住基准是“now”还是“today 00:00”。
10.4 时区设计原则
- 使用 UTC 存储
- 显示时进行转换
- 统一应用与数据库的策略
检查方式:
SELECT @@global.time_zone, @@session.time_zone;
10.5 表设计最佳实践
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
- 标准的审计/日志/更新追踪设施
- 如需精度,指定
(6)
四个最重要的实际要点
- 不要误解
NOW()与CURRENT_TIMESTAMP的区别 - 范围查询使用
>= AND < - 默认使用 UTC 存储
- 在设计阶段决定类型(DATETIME 与
TIMESTAMP)
MySQL 的当前时间处理是日志记录、销售汇总、认证/会话管理、批处理等的基础。
如果遵循本文的原则,您可以避免诸如时间漂移、边界错误和性能下降等常见问题。


