TO_CHAR是Oracle中将DATE或TIMESTAMP转为字符串的唯一可靠方式,需显式指定格式掩码(如'YYYY-MM-DD HH24:MI:SS'),注意大小写、时区处理、NLS参数及常见错误规避。

TO_CHAR 日期格式化的基本用法
Oracle 的 TO_CHAR 函数是把 DATE 或 TIMESTAMP 转成字符串的唯一可靠方式,直接拼接或隐式转换(比如用 || 连接字符串)会依赖 NLS_DATE_FORMAT,极易出错且不可控。
基本写法就是:TO_CHAR(date_value, 'format_mask'),其中 format_mask 是关键。常见掩码如 'YYYY-MM-DD HH24:MI:SS'、'DD-MON-RRRR' 都区分大小写,MM 是月份,mi 是分钟(不是 mm),HH24 表示24小时制。
-
'YYYY'返回4位年份;'YY'返回2位,跨世纪时可能歧义(比如 2025 和 1925 都显示为'25') - 月份缩写用
'MON'(大写),值受会话NLS_DATE_LANGUAGE影响;想固定输出英文就加参数:TO_CHAR(d, 'MON', 'NLS_DATE_LANGUAGE=AMERICAN') - 中文星期几要用
'DAY',但默认带空格补足到9字符(因 Oracle 中DAY是固定宽度字段),可加'fm'修饰符去除:'fmDAY'
处理时区和 TIMESTAMP 类型的坑
如果输入是 TIMESTAMP WITH TIME ZONE,只用普通格式串会丢掉时区信息,且可能触发隐式转换错误。必须显式指定时区上下文。
- 想输出带时区偏移的字符串,用
'TZH:TZM'或'TZR':TO_CHAR(ts_tz, 'YYYY-MM-DD HH24:MI:SS TZR') - 若需转成特定时区再格式化,先用
AT TIME ZONE转换,再TO_CHAR:TO_CHAR(ts_tz AT TIME ZONE 'Asia/Shanghai', 'YYYY-MM-DD HH24:MI:SS') - 对纯
TIMESTAMP(无时区),TO_CHAR行为与DATE一致;但若列实际存的是TIMESTAMP却用DATE函数操作,可能截断纳秒精度
常见报错及规避方法
最常遇到的是 ORA-01821: date format not supported,通常因为格式串里混入了未被识别的字符,或者大小写/拼写错误(比如把 'HH24' 写成 'hh24' 或 'HH24' 中漏了数字)。
- 避免在格式串中使用中文标点或全角字符——哪怕看着像单引号,实际可能是 Unicode 引号,导致解析失败
- 动态拼接格式串时(例如从参数传入),务必验证格式合法性;Oracle 不提供运行时校验函数,只能靠测试覆盖或提前白名单过滤
- 当格式串含文字内容(如
'今天是fmDAY'),文字部分需用双引号包裹:'"今天是"fmDAY',否则今天是会被当作非法格式符 - 使用
fm(fill mode)前缀能抑制前导空格和补零,但不要滥用——比如'fmMM'在月份为1时输出'1'而非'01',下游系统若依赖固定宽度可能出问题
性能与 NLS 设置的实际影响
TO_CHAR 本身开销不大,但格式串越复杂、涉及语言/时区转换越多,CPU 消耗越明显;尤其在聚合查询或大表 SELECT 中高频调用时,差异可观。
- 同一个 SQL 在不同会话中结果不一致?大概率是
NLS_DATE_LANGUAGE或NLS_TERRITORY设置不同,建议在 SQL 中显式指定而非依赖会话级设置 - 想全局统一行为,可在连接后执行:
ALTER SESSION SET NLS_DATE_LANGUAGE = 'AMERICAN';,但更稳妥的做法仍是格式串里写死语言参数 - 如果只是做简单年月日提取(如
'YYYY-MM'),考虑用EXTRACT(YEAR FROM d)+ 字符串拼接,有时比TO_CHAR更快,尤其在分区裁剪场景下
格式串里的每个字符都参与解析,多一个空格、少一个引号、大小写错一位,都可能导致运行时报错或静默错误。别图省事跳过测试——哪怕只是改了个 'DD' 到 'Dd'。


















