MySQL 当前时间:NOW()、CURDATE()、UTC_TIMESTAMP()、时区及最佳实践

目次

1. 在 MySQL 中获取当前时间(要点:最简 SQL 速查表)

如果你想在 MySQL 中获取当前时间,只需记住少数几个 SQL 函数。
下面是针对搜索关键词 “MySQL get current time” 的 最简答案

1.1 MySQL 当前日期和时间:NOW() / CURRENT_TIMESTAMP

返回当前的 日期 + 时间 (YYYY-MM-DD HH:MM:SS)。

SELECT NOW();
SELECT CURRENT_TIMESTAMP;

示例输出

2025-02-01 15:30:45
  • NOW()CURRENT_TIMESTAMP 通常返回相同的结果。
  • 两者都返回当前的日期和时间。
  • 如果需要毫秒,请使用以下方式。
    SELECT NOW(3);
    

注意事项(常见错误)

  • 取决于服务器的时区设置。
  • 在 UTC 环境下,可能会得到 UTC 时间而非日本时间。
  • 在查询执行期间,它基本上返回相同的时间(在单个语句内固定)。

1.2 MySQL 当前日期:CURDATE()

仅返回日期(不含时间)。

SELECT CURDATE();

示例输出

2025-02-01

使用场景

  • 查询今天的数据
  • 日期比较(例如,仅过滤今天的记录)

注意事项

  • 返回值为 DATE 类型。
  • 当需要处理时间(时分秒)时不适用。

1.3 MySQL 当前时间:CURTIME()

仅返回时间。

SELECT CURTIME();

示例输出

15:30:45

使用场景

  • 检查营业时间
  • 根据时间窗口进行分支逻辑

注意事项

  • 不包含日期信息。
  • 不能与 DATE 类型的列进行比较。

1.4 MySQL 当前 UTC 时间:UTC_TIMESTAMP()

返回 UTC(协调世界时)时间,不受服务器时区设置影响。

SELECT UTC_TIMESTAMP();

示例输出

2025-02-01 06:30:45

何时使用

  • 全球化服务
  • 需要统一以 UTC 存储日志的设计

常见错误

  • NOW() 混用会导致时间偏移
  • 若应用假设为日本时间(JST),会出现 9 小时的差异

1.5 MySQL 带毫秒的当前时间:NOW(3) / CURRENT_TIMESTAMP(3)

MySQL 5.6 及以上版本支持小数秒。

SELECT NOW(3);
SELECT CURRENT_TIMESTAMP(3);

示例输出

2025-02-01 15:30:45.123

存储注意事项

你的列也必须支持小数秒。

DATETIME(3)
TIMESTAMP(3)

如果存入不支持的小数秒的列,小数部分将被截断。

1.6 按用途快速速查表

PurposeSQL
Current date and timeSELECT NOW();
Get UTCSELECT UTC_TIMESTAMP();
Date onlySELECT CURDATE();
Time onlySELECT CURTIME();
Get millisecondsSELECT NOW(3);

常见陷阱汇总

  • 时间偏移是因为未确认时区
  • 在没有支持毫秒的列上使用 NOW(3)
  • 混用 UTC 与本地时间
  • 未理解 DATETIME 与 TIMESTAMP 的区别

MySQL DATETIME と TIMESTAMP の違いを示した図。TIMESTAMPはUTC変換され、DATETIMEは変換されない。

MySQL DATETIME 与 TIMESTAMP 的区别(比较是否进行时区转换)

2. MySQL NOW() 与 CURRENT_TIMESTAMP 的区别

NOW()CURRENT_TIMESTAMP 看起来相似,但 误解它们的使用场景和行为很容易导致 bug。这里我们整理了它们的区别、正确用法以及常见陷阱。

2.1 应该使用哪一个?(SELECT 用法 / DEFAULT 用法)

■ 在 SELECT 中检索当前日期/时间时

SELECT NOW();
SELECT CURRENT_TIMESTAMP;

通常,两者返回相同的结果。

  • 它们是等价的(同义词)
  • 返回值等同于 DATETIME
  • 受时区设置影响

实用要点

  • 更注重可读性 → NOW()
  • 更倾向标准 SQL 风格 → CURRENT_TIMESTAMP

■ 在设置列默认值时

CREATE TABLE logs (
    id INT AUTO_INCREMENT PRIMARY KEY,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

关键点在于:

对于默认值,通常使用 CURRENT_TIMESTAMP

.某些环境也允许使用 NOW(),但 行为可能因 MySQL 版本和 SQL 模式而异。更安全的选择是 CURRENT_TIMESTAMP

2.2 DEFAULT / ON UPDATE 的正确用法

如果你想自动更新 “updated at” 时间戳:

CREATE TABLE logs (
    id INT AUTO_INCREMENT PRIMARY KEY,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        ON UPDATE CURRENT_TIMESTAMP
);

行为

  • 在 INSERT 时 → 为 created_at / updated_at 设置当前时间
  • 在 UPDATE 时 → 仅更新 updated_at

常见错误

  • 使用 DATETIME 时未匹配小数秒精度
  • 遇到旧版 MySQL 对多个 TIMESTAMP 列的限制(MySQL 5.6 及更早版本有此限制)

2.3 NOW() 与 SYSDATE() 的区别(重要)

容易忽视的一点是与 SYSDATE() 的区别。

SELECT NOW(), SYSDATE();

■ 行为差异

  • NOW() → 在查询开始时固定时间
  • SYSDATE() → 返回调用时的时间

示例:

SELECT NOW(), SLEEP(3), NOW();

NOW() 返回相同的值。

SELECT SYSDATE(), SLEEP(3), SYSDATE();

SYSDATE() 显示出 3 秒的差异。

2.4 应该使用哪一个?

FunctionBehaviorRecommended use
NOW()Fixed within a queryLogging, consistency-focused
SYSDATE()Call-time valuePrecise real-time retrieval

为什么在实际系统中常推荐使用 NOW()

  • 在事务内部保持一致性(事务是一种一次处理多个 SQL 语句的机制)
  • 在复制环境中更安全

2.5 常见误解与问题

❌ “NOW() 和 CURRENT_TIMESTAMP 完全相同,所以不需要考虑它们。”

→ 根据环境的不同,差异可能会体现在默认值或更新行为上。

❌ “SYSDATE() 更精确,所以总是更好。”

→ 在复制环境中可能会导致问题。

❌ 未验证时区

SHOW VARIABLES LIKE '%time_zone%';

如果在未检查的情况下使用它,可能会出现时间偏移。

2.6 实用最佳实践

  • SELECT 检索 → NOW()
  • 列默认值 → CURRENT_TIMESTAMP
  • 自动更新 → ON UPDATE CURRENT_TIMESTAMP
  • 注重一致性 → 默认使用 NOW()
  • 基于 UTC 的设计 → 使用 TIMESTAMP 并以 UTC 存储
  1. MySQL 当前时间格式化 (DATE_FORMAT / TIME_FORMAT)
    在 MySQL 中检索当前时间后,更改显示格式是非常常见的。
    对于搜索意图“MySQL current time format”,最重要的是理解 DATE_FORMAT() 函数。
    3.1 MySQL 日期时间格式化:DATE_FORMAT(NOW(), …)
    基本语法:
    SELECT DATE_FORMAT(target_datetime, 'format_string');
    示例:格式化当前时间
    SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s');
    示例输出
    2025-02-01 15:30:45
    常用格式说明符
    说明符
    含义
    示例
    %Y
    年(4 位数字)
    2025
    %m
    月(2 位数字)
    02
    %d
    日(2 位数字)
    01
    %H
    小时(24 小时制)
    15
    %h
    小时(12 小时制)
    03
    %i
    分钟
    30
    %s

    45
    %p
    AM/PM
    PM
    3.2 转换为日式格式或 12 小时制
    ■ 日式格式
    SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i');
    示例输出:
    2025-02-01 15:30
    ■ 12 小时制 + AM/PM
    SELECT DATE_FORMAT(NOW(), '%Y-%m-%d %h:%i:%s %p');
    示例输出:
    2025-02-01 03:30:45 PM
    3.3 MySQL 仅时间格式化:TIME_FORMAT()
    专用于 TIME 类型数据的格式化函数。
    SELECT TIME_FORMAT(CURTIME(), '%H:%i');
    示例输出:
    15:30
    注意
    TIME_FORMAT() 仅适用于 TIME 类型
    对于 DATETIME,使用 DATE_FORMAT()
    3.4 字符串 → 日期时间转换:STR_TO_DATE()
    将字符串数据转换为日期时间类型:
    SELECT STR_TO_DATE('2025-02-01 15:30:45', '%Y-%m-%d %H:%i:%s');
    示例输出:
    2025-02-01 15:30:45
    常见错误
    格式不匹配将返回 NULL
    混淆 %m%c(带零填充的月 vs 不带零填充的月)
    3.5 生产环境中的重要要点
    ❌ 不要在格式化后进行比较
    不良示例:
    WHERE DATE_FORMAT(created_at, '%Y-%m-%d') = '2025-02-01';
    不推荐,因为索引会失效(查询性能下降)
    推荐:
    WHERE created_at >= '2025-02-01' AND created_at < '2025-02-02';
    ❌ 不要在数据库端过度格式化
    在 Web 应用中,显示格式化通常在应用端更灵活
    数据库应专注于“存储和计算”
    3.6 带毫秒的格式化
    SELECT DATE_FORMAT(NOW(3), '%Y-%m-%d %H:%i:%s.%f');
    %f 表示微秒(6 位数字)。
    注意
    如果列不是 DATETIME(3) 或类似类型,小数部分将被截断
    MySQL 5.6 及更高版本可用
    3.7 按格式化目的总结
    目的
    函数
    更改显示格式
    DATE_FORMAT
    仅格式化时间
    TIME_FORMAT
    字符串 → 日期时间转换
    STR_TO_DATE
    显示毫秒
    %f
  2. MySQL 日期和时间加减 (DATE_ADD / DATE_SUB)
    即使可以检索当前时间,如果没有“X 天后”或“X 小时前”等日期/时间计算,也无法在生产环境中有效使用它
    这里解释如何在 MySQL 中使用 DATE_ADD()DATE_SUB() 与当前时间。
    4.1 MySQL 日期时间加法:DATE_ADD()
    基本语法:
    SELECT DATE_ADD(base_datetime, INTERVAL value unit);
    示例:7 天后
    SELECT DATE_ADD(NOW(), INTERVAL 7 DAY);
    示例:2 小时后
    SELECT DATE_ADD(NOW(), INTERVAL 2 HOUR);
    常用单位
    单位
    含义
    SECOND

    MINUTE
    分钟
    HOUR
    小时
    DAY

    MONTH

    YEAR

    4.2 MySQL 日期时间减法:DATE_SUB()
    基本语法:
    SELECT DATE_SUB(base_datetime, INTERVAL value unit);
    示例:30 天前
    SELECT DATE_SUB(NOW(), INTERVAL 30 DAY);
    示例:1 小时前
    SELECT DATE_SUB(NOW(), INTERVAL 1 HOUR);
    使用场景
    到期检查
    删除旧日志
    提取最近数据
    4.3 常见生产模式
    ■ 检索过去 24 小时的数据
    SELECT * FROM logs WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY);
    ■ 设置 7 天后的截止日期
    INSERT INTO tasks (deadline) VALUES (DATE_ADD(NOW(), INTERVAL 7 DAY));

4.4 常见错误与注意事项
❌ 对列使用函数
错误示例:
WHERE DATE(created_at) = CURDATE();
这会导致索引失效(查询性能优化受影响)。
推荐做法:
WHERE created_at >= CURDATE() AND created_at < DATE_ADD(CURDATE(), INTERVAL 1 DAY);
❌ 忽略时区
NOW() 基于服务器时区
如果使用 UTC 存储,请以 UTC_TIMESTAMP() 为基准
示例:
SELECT DATE_ADD(UTC_TIMESTAMP(), INTERVAL 1 DAY);
❌ 月份加法的陷阱
SELECT DATE_ADD('2025-01-31', INTERVAL 1 MONTH);
→ 由于月末调整,日期可能会变化。
(结果可能会变为 2025-02-28,取决于运行环境。)
在使用基于月份的计算前,请先了解规范。
4.5 毫秒级加法
SELECT DATE_ADD(NOW(3), INTERVAL 500 MILLISECOND);
※ MySQL 不直接支持 MILLISECOND。
请使用微秒指定:
SELECT DATE_ADD(NOW(3), INTERVAL 500000 MICROSECOND);
4.6 最佳实践
统一使用 NOW()UTC_TIMESTAMP() 作为基准
不要在 WHERE 子句中对列使用函数
了解基于月份加法的行为
如果需要更高精度,请使用 DATETIME(3) 或更高版本

5. MySQL 中的日期/时间差值计算(DATEDIFF / TIMESTAMPDIFF)

在生产系统中,仅获取当前时间往往不够。
通常需要计算已经过去了多少天还剩多少小时

5.1 计算日期差值:DATEDIFF()

DATEDIFF() 计算两个日期之间的天数差。

SELECT DATEDIFF('2025-02-10', '2025-02-01');

结果

9

关键点

  • 仅返回 天数 差值
  • 忽略时间部分
  • 结果可能为负数

示例:计算自创建以来的天数

SELECT DATEDIFF(NOW(), created_at)
FROM users;

5.2 按单位计算差值:TIMESTAMPDIFF()

TIMESTAMPDIFF() 允许指定差值单位。

SELECT TIMESTAMPDIFF(unit, start_datetime, end_datetime);

示例:小时差值

SELECT TIMESTAMPDIFF(HOUR, '2025-02-01 10:00:00', '2025-02-01 15:00:00');

结果

5

常用单位

UnitMeaning
SECONDSeconds
MINUTEMinutes
HOURHours
DAYDays
MONTHMonths
YEARYears

示例:计算自登录以来的分钟数

SELECT TIMESTAMPDIFF(MINUTE, login_at, NOW())
FROM users;

5.3 生产环境使用案例

  • 会话超时检查
  • 订阅到期检查
  • 计算日志中的耗时
  • 限流逻辑

5.4 常见错误

❌ 在需要时间精度时使用 DATEDIFF

DATEDIFF() 会忽略小时和分钟。

❌ 参数顺序颠倒

顺序应为:

TIMESTAMPDIFF(unit, start, end)

如果颠倒顺序,结果会变为负数。

❌ 忽略时区

如果混用 UTC 与本地时间,计算结果可能不正确。

5.5 最佳实践

  • 在需要时间精度时使用 TIMESTAMPDIFF()
  • 对于简单的天数计算使用 DATEDIFF()
  • 确保时区使用一致
  • 在分布式系统中统一使用 UTC

6. 使用当前时间的日期范围查询

最常见的实际需求之一是检索特定时间范围内的记录,例如:

  • 当天记录
  • 最近 7 天
  • 最近 24 小时
  • 本月

6.1 检索当天记录(索引友好)

SELECT *
FROM logs
WHERE created_at >= CURDATE()
  AND created_at < DATE_ADD(CURDATE(), INTERVAL 1 DAY);

为什么这样写是正确的

  • 列上未使用函数
  • 索引仍可使用
  • 高效的范围查询

6.2 最近 7 天

SELECT *
FROM logs
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY);

6.3 最近 24 小时

SELECT *
FROM logs
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY);

6.4 本月

SELECT *
FROM logs
WHERE created_at >= DATE_FORMAT(NOW(), '%Y-%m-01')
  AND created_at < DATE_ADD(DATE_FORMAT(NOW(), '%Y-%m-01'), INTERVAL 1 MONTH);

在生产系统中,通常更好在应用程序端计算边界并将其作为参数传递。

6.5 常见性能错误

❌ 对索引列应用函数

WHERE DATE(created_at) = CURDATE();

这会阻止使用索引并导致全表扫描。

❌ 不慎使用 BETWEEN

BETWEEN 是包含性的,可能导致秒级的越界问题。

6.6 最佳实践摘要

  • 始终在日期过滤时使用范围条件
  • 避免对索引列应用函数
  • 在全球系统中优先使用 UTC 存储
  • 明确时区假设