MySQL 当前时间戳:NOW()、CURRENT_TIMESTAMP、SYSDATE()、UTC 与最佳实践

目次

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 caseRecommended function
General current datetimeNOW()
Table default valueCURRENT_TIMESTAMP
Date onlyCURDATE()
Time onlyCURTIME()
Unified UTC managementUTC_TIMESTAMP()
High-precision logsNOW(6)

人们常忽略的关键点

  • NOW()CURRENT_TIMESTAMP 实际上等效(相同值)
  • 在同一查询中,时间是固定的
  • 显示的值取决于时区设置
  • 对于微秒,您的列定义必须指定精度

2. NOW() / CURRENT_TIMESTAMP / SYSDATE() 的区别

有多个函数可以获取当前时间,但 最令人困惑的部分是 NOW()CURRENT_TIMESTAMPSYSDATE() 之间的区别
如果您没有正确理解这一点,可能会在日志和事务处理中出现意外行为。

2.1 NOW()CURRENT_TIMESTAMP 实际上等效

SELECT NOW(), CURRENT_TIMESTAMP;
  • 两者都返回 当前日期时间(DATETIME 值)
  • 在同一查询中,时间固定在“查询开始时间”。
  • CURRENT_TIMESTAMP 也可以使用函数调用语法:
    SELECT CURRENT_TIMESTAMP();
    

实际项目中的选择方式

Use caseRecommended
Simple retrievalNOW()
Table DEFAULT / ON UPDATECURRENT_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

本质区别

FunctionWhen 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

常用格式说明符

SpecifierMeaningExample
%Y4-digit year2025
%m2-digit month02
%d2-digit day23
%HHour (24-hour)14
%iMinutes35
%sSeconds50
%fMicroseconds123456

注意事项

  • 某些说明符在大小写之间有所区别。
  • %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

推荐用法

GoalRecommended
Formatting for displayDATE_FORMAT
Calculation / comparisonDATE() / 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'
    • 直接存储显示格式的字符串(会影响搜索性能)

本节关键要点

  • 使用 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;

常用单位

UnitMeaning
SECONDSeconds
MINUTEMinutes
HOURHours
DAYDays
WEEKWeeks
MONTHMonths
YEARYears

常见错误

  • DAYINTERVAL 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();

理解差异至关重要

ExpressionMeaning
NOW() - INTERVAL 1 DAYPast 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 &gt;= '2025-02-01 00:00:00'
  AND order_date &lt;= '2025-02-10 00:00:00'

因此 它不包括 2 月 10 日 00:00 之后的数据。


5.2 更安全的模式(推荐)

推荐模式

SELECT *
FROM orders
WHERE order_date &gt;= '2025-02-01 00:00:00'
  AND order_date &lt;  '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 &gt;= CURDATE()
  AND created_at &lt;  CURDATE() + INTERVAL 1 DAY;

This ensures:

这确保了:

  • 可以使用索引
  • 快速搜索
  • 没有边界问题

5.4 过去 7 天 / 过去 30 天的安全模式

Past 7 days

过去 7 天

WHERE created_at &gt;= NOW() - INTERVAL 7 DAY;

Past 30 days

过去 30 天

WHERE created_at &gt;= 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 &gt;= '2025-02-23 00:00:00'
  AND created_at &lt;  '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(重要)

TypeRangeTime zone impact
DATETIMEYear 1000 to 9999No
TIMESTAMP1970 to 2038Yes

The essential difference

本质区别

  • TIMESTAMP 在内部以 UTC 存储,显示时会转换为会话时区。
  • DATETIME 直接存储字面值(不进行转换)。

Guidelines

使用指南

CaseRecommended type
Log managementTIMESTAMP
Future dates (after 2038)DATETIME
Fixed values not requiring TZ conversionDATETIME

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_atupdated_at 上添加 ON UPDATE
  • 混淆 DATETIME 与 TIMESTAMP 的行为
  • 推迟时区决策

本节关键要点

  • 使用 CURRENT_TIMESTAMP 作为 DEFAULT。
  • 使用 ON UPDATE CURRENT_TIMESTAMP 进行更新追踪。
  • 根据时区行为和 2038 年问题选择数据类型。
  • 如需精度,请不要忘记 (6)

7. 时区设计(以 UTC 存储,在本地显示)

在处理当前时间时,不良的时区设计会导致时间漂移和双重转换
实际中,最安全的默认做法是“以 UTC 存储,在显示时转换为本地时间”。

MySQL timezone architecture storing timestamps in UTC and converting to local time (JST)

图:在 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(实际原则)

推荐策略

  1. 将数据以 UTC 存储
  2. 显示时转换为用户所在时区

原因:

  • 更易支持国际化
  • 避免夏令时(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 &gt;= @base_time - INTERVAL 1 DAY;

原因

  • 时间在过程进行中不会变化
  • 保持一致性

8.4 今日/昨日/过去7天的聚合

今日销售

SELECT SUM(amount)
FROM orders
WHERE created_at &gt;= CURDATE()
  AND created_at &lt;  CURDATE() + INTERVAL 1 DAY;

昨日销售

SELECT SUM(amount)
FROM orders
WHERE created_at &gt;= CURDATE() - INTERVAL 1 DAY
  AND created_at &lt;  CURDATE();

过去7天

SELECT COUNT(*)
FROM users
WHERE created_at &gt;= NOW() - INTERVAL 7 DAY;

重要

  • 不要在 WHERE 子句中对列使用函数
  • 编写能够保持索引可用的条件

8.5 过期检查(会话/令牌)

SELECT *
FROM sessions
WHERE expires_at &gt; NOW();

删除过期行

DELETE FROM sessions
WHERE expires_at &lt;= NOW();

常见错误

  • 使用 CURDATE() 并忽略时间
  • 存储 UTC 但使用本地 NOW() 进行比较

8.6 获取在 N 小时内过期的行

SELECT *
FROM coupons
WHERE expires_at &lt;= 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 可用于 DEFAULTON UPDATE
  • NOW() 不能直接用于 DEFAULT

备注

  • 这不是类型差异
  • 可以使用类似 (6) 的参数指定精度

9.2 是否应该使用 SYSDATE()

SYSDATE() 返回“评估时刻”的时间。

SELECT SYSDATE(), SLEEP(2), SYSDATE();

即使在同一查询中,该值也可能会变化。

何时使用

  • 当需要记录精确的真实时间点时

何时避免

  • 复制
  • 对一致性重要的事务处理

大多数情况下,使用 NOW() 已足够。

9.3 为什么时间会偏移?

主要原因:

  1. 服务器时区设置
  2. 会话时区设置
  3. 应用程序的双重转换
  4. 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 &gt;= '2025-02-01 00:00:00'
  AND created_at &lt;  '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 &gt;= 'START'
  AND created_at &lt;  '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)

四个最重要的实际要点

  1. 不要误解 NOW()CURRENT_TIMESTAMP 的区别
  2. 范围查询使用 >= AND <
  3. 默认使用 UTC 存储
  4. 在设计阶段决定类型(DATETIME 与 TIMESTAMP

MySQL 的当前时间处理是日志记录、销售汇总、认证/会话管理、批处理等的基础。
如果遵循本文的原则,您可以避免诸如时间漂移、边界错误和性能下降等常见问题。