MySQL数据库中存储的日期时间数据,经常需要根据业务需求进行格式转换。特别是当应用系统需要对接不同数据源,或者报表需要特定格式展示时,字符串与日期类型之间的转换就成为高频操作。我在金融行业的数据仓库项目中,就遇到过十几个系统传来的日期格式不统一的问题,这时候DATE_FORMAT()和STR_TO_DATE()这两个函数就成了救命稻草。
日期格式转换看似简单,但实际业务中会遇到各种坑。比如:
这些格式混乱的情况如果处理不当,轻则导致报表数据错误,重则引发业务逻辑问题。接下来我就结合12种典型场景,详细拆解MySQL中的日期格式转换技巧。
DATE_FORMAT()是将日期类型格式化为字符串的核心函数,其基本语法为:
sql复制DATE_FORMAT(date, format)
其中format参数支持30多种格式符,这里列举最常用的10个:
| 格式符 | 说明 | 示例输出 |
|---|---|---|
| %Y | 四位年份 | 2023 |
| %y | 两位年份 | 23 |
| %m | 两位月份(01-12) | 07 |
| %c | 月份(1-12) | 7 |
| %d | 两位日期(01-31) | 05 |
| %e | 日期(1-31) | 5 |
| %H | 24小时制(00-23) | 14 |
| %h | 12小时制(01-12) | 02 |
| %i | 分钟(00-59) | 45 |
| %s | 秒(00-59) | 30 |
实际案例:将当前日期格式化为中文年月日
sql复制SELECT DATE_FORMAT(NOW(), '%Y年%m月%d日');
-- 输出:2023年07月05日
STR_TO_DATE()是DATE_FORMAT()的逆操作,用于将字符串转为日期类型:
sql复制STR_TO_DATE(str, format)
这个函数对格式要求非常严格,常见问题包括:
典型示例:处理美国格式日期
sql复制SELECT STR_TO_DATE('07/04/2023', '%m/%d/%Y');
-- 输出:2023-07-04
重要提示:STR_TO_DATE()在严格模式下(STRICT_TRANS_TABLES)会对非法日期值报错,建议先用SELECT测试再用于UPDATE
sql复制-- 字符串转日期
SELECT STR_TO_DATE('2023-07-05', '%Y-%m-%d');
-- 日期转字符串
SELECT DATE_FORMAT('2023-07-05', '%m/%d/%Y');
从UNIX时间戳转换:
sql复制SELECT
FROM_UNIXTIME(1688544000) AS datetime,
DATE_FORMAT(FROM_UNIXTIME(1688544000), '%Y-%m-%d %H:%i:%s') AS formatted;
sql复制SELECT
STR_TO_DATE('2023年07月05日', '%Y年%m月%d日'),
DATE_FORMAT(NOW(), '%Y年%m月%d日 %H时%i分');
sql复制-- MM/DD/YYYY 转标准格式
SELECT STR_TO_DATE('07/04/2023', '%m/%d/%Y');
-- 反向转换
SELECT DATE_FORMAT('2023-07-04', '%m/%d/%Y');
sql复制-- 处理'Apr-15-2023'格式
SELECT STR_TO_DATE('Apr-15-2023', '%b-%d-%Y');
-- 生成英文月份缩写
SELECT DATE_FORMAT(NOW(), '%b %d, %Y');
sql复制-- 24小时制转12小时制
SELECT DATE_FORMAT('2023-07-05 14:30:00', '%Y-%m-%d %h:%i:%s %p');
-- 12小时制转24小时制
SELECT STR_TO_DATE('07/05/2023 02:30 PM', '%m/%d/%Y %h:%i %p');
sql复制-- 获取季度
SELECT
CONCAT('Q', QUARTER('2023-07-05')) AS quarter,
DATE_FORMAT('2023-07-05', '%Y-Q%q') AS quarter_format;
sql复制-- 获取周数并格式化
SELECT
WEEK('2023-07-05') AS week_number,
DATE_FORMAT('2023-07-05', '%Y-第%u周') AS week_format;
sql复制-- ISO格式转换
SELECT
DATE_FORMAT('2023-07-05', '%Y-%m-%dT%H:%i:%sZ') AS iso_format,
STR_TO_DATE('2023-07-05T14:30:00Z', '%Y-%m-%dT%H:%i:%sZ');
sql复制-- 处理非常规分隔符
SELECT
STR_TO_DATE('2023|07|05', '%Y|%m|%d'),
DATE_FORMAT(NOW(), '%Y.%m.%d');
sql复制-- 日期时间拆分
SELECT
DATE_FORMAT(NOW(), '%Y') AS year,
DATE_FORMAT(NOW(), '%m') AS month;
-- 组合成日期
SELECT STR_TO_DATE(CONCAT('2023','07','05'), '%Y%m%d');
sql复制-- 时区转换(需确保时区表已加载)
SET time_zone = '+00:00';
SELECT NOW() AS utc_time;
SET time_zone = '+08:00';
SELECT NOW() AS beijing_time;
在WHERE条件中使用日期函数会导致索引失效:
sql复制-- 错误示范(索引失效)
SELECT * FROM orders WHERE DATE_FORMAT(create_time,'%Y-%m')='2023-07';
-- 正确做法(使用范围查询)
SELECT * FROM orders
WHERE create_time BETWEEN '2023-07-01' AND '2023-07-31';
处理大量数据时,避免逐行转换:
sql复制-- 低效做法
UPDATE table SET date_col = STR_TO_DATE(str_col, '%Y-%m-%d') WHERE id > 0;
-- 高效做法(先创建临时列)
ALTER TABLE table ADD COLUMN temp_date DATE;
UPDATE table SET temp_date = STR_TO_DATE(str_col, '%Y-%m-%d');
ALTER TABLE table DROP COLUMN str_col;
ALTER TABLE table CHANGE COLUMN temp_date str_col DATE;
跨时区系统要特别注意:
sql复制-- 查看时区设置
SELECT @@global.time_zone, @@session.time_zone;
-- 设置会话时区
SET time_zone = '+08:00';
诊断方法:
sql复制-- 检查字符串原始值
SELECT HEX(str_date) FROM table WHERE id=123;
-- 测试转换
SELECT STR_TO_DATE('input_string', 'format');
当发现日期转换SQL执行缓慢时:
使用存储过程处理不确定格式:
sql复制DELIMITER //
CREATE PROCEDURE parse_date(IN date_str VARCHAR(50))
BEGIN
DECLARE formatted_date DATE;
-- 尝试常见格式
SET formatted_date = STR_TO_DATE(date_str, '%Y-%m-%d');
IF formatted_date IS NULL THEN
SET formatted_date = STR_TO_DATE(date_str, '%m/%d/%Y');
END IF;
SELECT formatted_date AS result;
END //
DELIMITER ;
创建常用格式转换函数:
sql复制CREATE FUNCTION format_chinese_date(d DATE)
RETURNS VARCHAR(50)
DETERMINISTIC
BEGIN
RETURN DATE_FORMAT(d, '%Y年%m月%d日');
END;
何时在MySQL中做格式转换:
何时在应用层处理:
在金融项目里,我总结的经验法则是:ETL过程在数据库层处理,展示逻辑在应用层处理。