You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL刷新令牌表触发器致DATETIME值丢失问题排查

MySQL触发器转换Unix时间戳失败的原因及解决办法

问题背景

搭建MySQL认证服务器时,使用以下表存储刷新令牌:

CREATE TABLE IF NOT EXISTS REFRESH_TOKEN (
    TOKEN VARCHAR(512) NOT NULL,
    EXPIRATION DATETIME
) ENGINE=INNODB;

为自动转换Unix时间戳为DATETIME格式,创建了触发器:

DROP TRIGGER IF EXISTS TG_RT_EXP;
DELIMITER //
CREATE TRIGGER TG_RT_EXP BEFORE INSERT ON REFRESH_TOKEN
FOR EACH ROW 
BEGIN
    SET NEW.EXPIRATION = FROM_UNIXTIME(NEW.EXPIRATION);
END;//
DELIMITER ;

执行以下插入语句时出现1265截断警告,EXPIRATION值为1969-12-31 16:00:00(预期为Unix时间戳1796239022对应的日期):

INSERT INTO REFRESH_TOKEN
(TOKEN, EXPIRATION)
VALUES (
    'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
    1796239022
);

移除触发器后,EXPIRATION变为0000-00-00 00:00:00;但直接在插入语句中使用FROM_UNIXTIME(1796239022)却能正确存储。

根本原因

1. MySQL自动类型转换优先级高于触发器

向DATETIME列传入数字1796239022时,MySQL会在触发器执行前自动尝试将该数字转换为DATETIME类型。MySQL对数字转DATETIME的规则是把数字解析为YYYYMMDDHHMMSS格式的整数(例如20240520123000代表2024-05-20 12:30:00),而1796239022不符合这个格式(拆解后年份为17、月份为96,均为无效值),因此转换结果为0000-00-00 00:00:00。

此时触发器中NEW.EXPIRATION已经是0000-00-00 00:00:00,调用FROM_UNIXTIME()时会把这个无效日期对应的数值(0)转换为Unix时间戳起始点的本地时间(即1969-12-31 16:00:00,对应UTC-8时区)。

2. 直接使用FROM_UNIXTIME()的区别

插入语句中直接写FROM_UNIXTIME(1796239022)时,函数会先将数字Unix时间戳转换为合法的DATETIME字符串,再传入列中,MySQL不需要做自动类型转换,因此能正确存储。

解决办法

方法一:修改列类型存储原始Unix时间戳

将EXPIRATION列改为INT UNSIGNED类型,直接存储Unix时间戳,查询时再做转换:

-- 修改列类型
ALTER TABLE REFRESH_TOKEN MODIFY COLUMN EXPIRATION INT UNSIGNED;

-- 插入语句(无需触发器)
INSERT INTO REFRESH_TOKEN
(TOKEN, EXPIRATION)
VALUES (
    'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
    1796239022
);

-- 查询时转换为DATETIME
SELECT TOKEN, FROM_UNIXTIME(EXPIRATION) AS EXPIRATION FROM REFRESH_TOKEN;

方法二:调整触发器逻辑,接收字符串形式的时间戳

如果要保留DATETIME列,插入时将时间戳作为字符串传入,触发器中先转换为整数再处理:

-- 更新触发器
DROP TRIGGER IF EXISTS TG_RT_EXP;
DELIMITER //
CREATE TRIGGER TG_RT_EXP BEFORE INSERT ON REFRESH_TOKEN
FOR EACH ROW 
BEGIN
    SET NEW.EXPIRATION = FROM_UNIXTIME(CAST(NEW.EXPIRATION AS UNSIGNED));
END;//
DELIMITER ;

-- 插入时将时间戳用引号括为字符串
INSERT INTO REFRESH_TOKEN
(TOKEN, EXPIRATION)
VALUES (
    'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
    '1796239022'
);

方法三:插入时显式转换(最直接)

直接在插入语句中使用FROM_UNIXTIME()转换时间戳,无需触发器:

INSERT INTO REFRESH_TOKEN
(TOKEN, EXPIRATION)
VALUES (
    'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
    FROM_UNIXTIME(1796239022)
);

内容的提问来源于stack exchange,提问作者MarCordero385

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 04:47:04