PostgreSQL 的格式化函数提供了一套强大的工具,用于将各种数据类型(日期/时间、整数、浮点数、数值)转换为格式化的字符串,以及从格式化的字符串转换为特定的数据类型。 表 9.26 列出了这些函数。所有这些函数都遵循一个通用的调用约定:第一个参数是要格式化的值,第二个参数是定义输出或输入格式的模板。
表 9.26. 格式化函数
| 函数 描述 示例 | 
|---|
|   
 根据给定的格式将时间戳转换为字符串。 
 | 
| 
 根据给定的格式将间隔转换为字符串。 
 | 
| 
 根据给定的格式将数字转换为字符串;适用于  
 
 
 | 
| 根据给定的格式将字符串转换为日期。 
 | 
|   根据给定的格式将字符串转换为数值。 
 | 
|   根据给定的格式将字符串转换为时间戳。(另请参见 表 9.33 中的  
 | 
to_timestamp 和 to_date 函数用于处理无法通过简单类型转换的输入格式。对于大多数标准的日期/时间格式,只需将源字符串强制转换为所需的数据类型即可,这样更简单。同样,对于标准数值表示,to_number 函数是不必要的。
在 to_char 的输出模板字符串中,某些模式会被识别并根据给定值进行相应格式化替换。任何非模板模式的文本都会被原样复制。同样,在输入模板字符串(用于其他函数)中,模板模式标识输入数据字符串要提供的值。如果模板字符串中包含非模板模式的字符,则输入数据字符串中的相应字符将被跳过(无论它们是否与模板字符串中的字符相等)。
表 9.27 显示了用于格式化日期和时间值的模板模式。
表 9.27. 日期/时间格式化模板模式
| 模式 | 描述 | 
|---|---|
| HH | 一天中的小时 (01–12) | 
| HH12 | 一天中的小时 (01–12) | 
| HH24 | 一天中的小时 (00–23) | 
| MI | 分钟 (00–59) | 
| SS | 秒 (00–59) | 
| MS | 毫秒 (000–999) | 
| US | 微秒 (000000–999999) | 
| FF1 | 十分之一秒 (0–9) | 
| FF2 | 百分之一秒 (00–99) | 
| FF3 | 毫秒 (000–999) | 
| FF4 | 千分之一毫秒 (0000–9999) | 
| FF5 | 万分之一毫秒 (00000–99999) | 
| FF6 | 微秒 (000000–999999) | 
| SSSS,SSSSS | 午夜后的秒数 (0–86399) | 
| AM,am,PM或pm | 上午/下午指示器(无句点) | 
| A.M.,a.m.,P.M.或p.m. | 上午/下午指示器(有句点) | 
| Y,YYY | 年份(4 位或更多数字),带逗号 | 
| YYYY | 年份(4 位或更多数字) | 
| YYY | 年份的后 3 位数字 | 
| YY | 年份的后 2 位数字 | 
| Y | 年份的最后 1 位数字 | 
| IYYY | ISO 8601 编号年份(4 位或更多数字) | 
| IYY | ISO 8601 编号年份的后 3 位数字 | 
| IY | ISO 8601 编号年份的后 2 位数字 | 
| I | ISO 8601 编号年份的最后 1 位数字 | 
| BC,bc,AD或ad | 纪元指示器(无句点) | 
| B.C.,b.c.,A.D.或a.d. | 纪元指示器(有句点) | 
| MONTH | 大写月份全称(用空格填充至 9 个字符) | 
| Month | 首字母大写的月份全称(用空格填充至 9 个字符) | 
| month | 小写月份全称(用空格填充至 9 个字符) | 
| MON | 大写月份缩写(英文为 3 个字符,本地化长度不同) | 
| Mon | 首字母大写的月份缩写(英文为 3 个字符,本地化长度不同) | 
| mon | 小写月份缩写(英文为 3 个字符,本地化长度不同) | 
| MM | 月份编号 (01–12) | 
| DAY | 大写星期全称(用空格填充至 9 个字符) | 
| Day | 首字母大写的星期全称(用空格填充至 9 个字符) | 
| day | 小写星期全称(用空格填充至 9 个字符) | 
| DY | 大写星期缩写(英文为 3 个字符,本地化长度不同) | 
| Dy | 首字母大写的星期缩写(英文为 3 个字符,本地化长度不同) | 
| dy | 小写星期缩写(英文为 3 个字符,本地化长度不同) | 
| DDD | 一年中的第几天 (001–366) | 
| IDDD | ISO 8601 编号年份中的第几天 (001–371;一年中的第 1 天是第一周的星期一) | 
| DD | 月份中的第几天 (01–31) | 
| D | 星期中的第几天,星期日 ( 1) 到星期六 (7) | 
| ID | ISO 8601 星期中的第几天,星期一 ( 1) 到星期日 (7) | 
| W | 月份中的第几周 (1–5)(第一周从月份的第一天开始) | 
| WW | 一年中的第几周 (1–53)(第一周从一年中的第一天开始) | 
| IW | ISO 8601 编号年份中的第几周 (01–53;一年中的第一个星期四在第 1 周) | 
| CC | 世纪(2 位数字)(二十一世纪始于 2001-01-01) | 
| J | 儒略日(从公元前 4714 年 11 月 24 日当地午夜开始的整数天数;参见 B.7 节) | 
| Q | 季度 | 
| RM | 大写罗马数字月份 (I–XII;I=一月) | 
| rm | 小写罗马数字月份 (i–xii;i=一月) | 
| TZ | 大写时区缩写 | 
| tz | 小写时区缩写 | 
| TZH | 时区小时 | 
| TZM | 时区分钟 | 
| OF | 与 UTC 的时区偏移( HH或HH:MM) | 
修饰符可应用于任何模板模式以更改其行为。例如,FMMonth 是带有 FM 修饰符的 Month 模式。 表 9.28 显示了日期/时间格式化的修饰符模式。
表 9.28. 日期/时间格式化模板模式修饰符
| 修饰符 | 描述 | 示例: | 
|---|---|---|
| FM前缀 | 填充模式(抑制前导零和填充空格) | FMMonth | 
| TH后缀 | 大写序数后缀 | DDTH,例如12TH | 
| th后缀 | 小写序数后缀 | DDth,例如12th | 
| FX前缀 | 固定格式全局选项(参见用法说明) | FX Month DD Day | 
| TM前缀 | 翻译模式(使用基于 lc_time 的本地化日期和月份名称) | TMMonth | 
| SP后缀 | 拼写模式(未实现) | DDSP | 
日期/时间格式化用法说明
FM 抑制了否则将添加以使模式输出固定宽度的前导零和尾随空格。在 PostgreSQL 中,FM 只修改下一个规范,而在 Oracle 中,FM 影响所有后续规范,重复的 FM 修饰符会切换填充模式的开启和关闭。
TM 抑制尾随空格,无论是否指定了 FM。
to_timestamp 和 to_date 忽略输入的字母大小写;因此,例如 MON、Mon 和 mon 都接受相同的字符串。使用 TM 修饰符时,根据函数输入排序规则(参见 23.2 节)进行大小写折叠。
to_timestamp 和 to_date 会跳过输入字符串开头以及日期和时间值周围的多个空格,除非使用了 FX 选项。例如,to_timestamp(' 2000    JUN', 'YYYY MON') 和 to_timestamp('2000 - JUN', 'YYYY-MON') 可以正常工作,但 to_timestamp('2000    JUN', 'FXYYYY MON') 会返回错误,因为 to_timestamp 只期望单个空格。 FX 必须作为模板中的第一个项指定。
除非使用了 FX 选项,否则 to_timestamp 和 to_date 的模板字符串中的分隔符(空格或非字母/非数字字符)会匹配输入字符串中的任何单个分隔符或被跳过。例如,to_timestamp('2000JUN', 'YYYY///MON') 和 to_timestamp('2000/JUN', 'YYYY MON') 可以正常工作,但 to_timestamp('2000//JUN', 'YYYY/MON') 会返回错误,因为输入字符串中的分隔符数量超过了模板中的分隔符数量。
如果指定了 FX,则模板字符串中的分隔符会精确匹配输入字符串中的一个字符。但请注意,输入字符串字符不一定需要与模板字符串中的分隔符相同。例如,to_timestamp('2000/JUN', 'FXYYYY MON') 可以正常工作,但 to_timestamp('2000/JUN', 'FXYYYY  MON') 会返回错误,因为模板字符串中的第二个空格会消耗输入字符串中的字母 J。
TZH 模板模式可以匹配一个带符号的数字。在没有 FX 选项的情况下,负号可能存在歧义,并可能被解释为分隔符。这种歧义的解决方式如下:如果模板字符串中 TZH 前面的分隔符数量少于输入字符串中负号前面的分隔符数量,则负号被解释为 TZH 的一部分。否则,负号被视为值之间的分隔符。例如,to_timestamp('2000 -10', 'YYYY TZH') 将 -10 匹配到 TZH,而 to_timestamp('2000 -10', 'YYYY  TZH') 将 10 匹配到 TZH。
to_char 模板中允许使用普通文本,并且会按字面输出。您可以使用双引号括起来的子字符串来强制将其解释为字面文本,即使它包含模板模式。例如,在 '"Hello Year "YYYY' 中,YYYY 将被年份数据替换,但 Year 中的单个 Y 不会被替换。在 to_date、to_number 和 to_timestamp 中,字面文本和双引号字符串会导致跳过字符串中包含的字符数;例如,"XX" 跳过两个输入字符(无论它们是否是 XX)。
在 PostgreSQL 12 之前,可以使用非字母或非数字字符跳过输入字符串中的任意文本。例如,to_timestamp('2000y6m1d', 'yyyy-MM-DD') 过去可以正常工作。现在,您只能使用字母字符来实现此目的。例如,to_timestamp('2000y6m1d', 'yyyytMMtDDt') 和 to_timestamp('2000y6m1d', 'yyyy"y"MM"m"DD"d"') 会跳过 y、m 和 d。
如果您想在输出中包含双引号,则必须在其前面加上反斜杠,例如 '\"YYYY Month\"'。在双引号字符串之外,反斜杠没有其他特殊作用。在双引号字符串中,反斜杠会使下一个字符被按字面意思处理,无论它是什么(但这没有特殊效果,除非下一个字符是双引号或另一个反斜杠)。
在 to_timestamp 和 to_date 中,如果年份格式规范的位数少于四位,例如 YYY,并且提供的年份位数少于四位,则年份将调整为最接近 2020 年,例如 95 变为 1995。
在 to_timestamp 和 to_date 中,负年份表示公元前。如果您同时写了负年份和明确的 BC 字段,则会再次得到公元。年份零被视为公元前 1 年。
在 to_timestamp 和 to_date 中,当处理超过 4 位数的年份时,YYYY 转换存在一个限制。您必须在 YYYY 之后使用非数字字符或模板,否则年份始终被解释为 4 位数。例如(以年份 20000 为例):to_date('200001130', 'YYYYMMDD') 将被解释为 4 位数年份;请改用年份后的非数字分隔符,例如 to_date('20000-1130', 'YYYY-MMDD') 或 to_date('20000Nov30', 'YYYYMonDD')。
在 to_timestamp 和 to_date 中,如果存在 YYY、YYYY 或 Y,YYY 字段,则会接受 CC(世纪)字段,但会忽略它。如果 CC 与 YY 或 Y 一起使用,则结果计算为指定世纪中的该年份。如果指定了世纪但未指定年份,则假定为该世纪的第一年。
在 to_timestamp 和 to_date 中,星期名称或数字(DAY、D 和相关字段类型)会被接受,但用于计算结果的目的会被忽略。季度(Q)字段也是如此。
在 to_timestamp 和 to_date 中,ISO 8601 编号日期(与公历日期相对)可以通过以下两种方式之一指定:
年份、周数和星期几:例如 to_date('2006-42-4', 'IYYY-IW-ID') 返回日期 2006-10-19。如果省略星期几,则假定为 1(星期一)。
年份和一年中的第几天:例如 to_date('2006-291', 'IYYY-IDDD') 也返回 2006-10-19。
尝试使用 ISO 8601 编号日期字段和公历日期字段的混合输入日期是无意义的,并会导致错误。在 ISO 8601 编号年份的上下文中,“月份”或“月份中的第几天”的概念没有意义。在公历年份的上下文中,ISO 周也没有意义。
虽然 to_date 会拒绝公历和 ISO 编号日期字段的混合输入,但 to_char 不会,因为像 YYYY-MM-DD (IYYY-IDDD) 这样的输出格式规范可能很有用。但要避免编写类似 IYYY-MM-DD 的内容;这会在年初附近产生令人惊讶的结果。(有关更多信息,请参见 9.9.1 节。)
在 to_timestamp 中,毫秒(MS)或微秒(US)字段用作小数点后的秒数。例如 to_timestamp('12.3', 'SS.MS') 不是 3 毫秒,而是 300 毫秒,因为转换将其视为 12 + 0.3 秒。因此,对于格式 SS.MS,输入值 12.3、12.30 和 12.300 指定的毫秒数相同。要得到三毫秒,必须写 12.003,转换将其视为 12 + 0.003 = 12.003 秒。
这是一个更复杂的例子:to_timestamp('15:12:02.020.001230', 'HH24:MI:SS.MS.US') 表示 15 小时、12 分钟和 2 秒 + 20 毫秒 + 1230 微秒 = 2.021230 秒。
to_char(..., 'ID') 的星期编号与 extract(isodow from ...) 函数匹配,但 to_char(..., 'D') 的编号不匹配 extract(dow from ...) 的编号。
to_char(interval) 以 12 小时制格式化 HH 和 HH12,例如零小时和 36 小时都输出为 12,而 HH24 输出完整的小时值,该值可以超过 interval 值中的 23。
表 9.29 显示了用于格式化数值的模板模式。
表 9.29. 数值格式化模板模式
| 模式 | 描述 | 
|---|---|
| 9 | 数字位置(如果无符号则可省略) | 
| 0 | 数字位置(即使无符号也保留) | 
| .(句点) | 小数点 | 
| ,(逗号) | 分组(千位)分隔符 | 
| PR | 用尖括号表示的负值 | 
| S | 符号固定在数字上(使用区域设置) | 
| L | 货币符号(使用区域设置) | 
| D | 小数点(使用区域设置) | 
| G | 分组分隔符(使用区域设置) | 
| MI | 在指定位置的负号(如果数字 < 0) | 
| PL | 在指定位置的正号(如果数字 > 0) | 
| SG | 在指定位置的正负号 | 
| RN或rn | 罗马数字(1 到 3999 之间的值) | 
| TH或th | 序数后缀 | 
| V | 移动指定位数的数字(参见说明) | 
| EEEE | 科学计数法的指数 | 
数值格式化用法说明
0 指定一个始终会打印的数字位置,即使它包含前导/尾随零。 9 也指定一个数字位置,但如果是前导零,则会用空格替换,如果是尾随零且指定了填充模式,则会被删除。(对于 to_number(),这两个模式字符是等效的。)
如果格式提供的分数位数少于要格式化的数字,to_char() 会将数字四舍五入到指定的分数位数。
模式字符 S、L、D 和 G 代表当前区域设置定义的符号、货币符号、小数点和千位分隔符(参见 lc_monetary 和 lc_numeric)。模式字符句点和逗号代表那些确切的字符,分别具有小数点和千位分隔符的含义,与区域设置无关。
如果在 to_char() 的模式中没有明确提供符号,则会保留一个列用于符号,并将其锚定在数字的左侧(紧邻数字)。如果 S 出现在一些 9 的左侧,它也会锚定在数字上。
使用 SG、PL 或 MI 格式化的符号不会锚定在数字上;例如,to_char(-12, 'MI9999') 生成 '-  12',而 to_char(-12, 'S9999') 生成 '  -12'。(Oracle 实现不允许在 9 前使用 MI,而是要求 9 在 MI 前面。)
TH 不转换小于零的值,也不转换分数。
PL、SG 和 TH 是 PostgreSQL 的扩展。
在 to_number 中,如果使用了非数据模板模式(如 L 或 TH),则会跳过相应数量的输入字符,无论它们是否匹配模板模式,除非它们是数据字符(即数字、符号、小数点或逗号)。例如,TH 会跳过两个非数据字符。
V 与 to_char 结合使用时,会将输入值乘以 10^,其中 nn 是 V 后面的数字位数。 V 与 to_number 结合使用时,会以类似的方式进行除法。 V 可以被视为标记输入或输出字符串中隐式小数点的位置。to_char 和 to_number 不支持将 V 与小数点组合使用(例如,不允许 99.9V99)。
EEEE(科学计数法)不能与除数字和小数点模式之外的任何其他格式模式或修饰符组合使用,并且必须位于格式字符串的末尾(例如,9.99EEEE 是有效模式)。
在 to_number() 中,RN 模式将罗马数字(标准形式)转换为数字。输入不区分大小写,因此 RN 和 rn 是等效的。RN 不能与任何其他格式模式或修饰符组合使用,除了 FM,后者仅适用于 to_char(),在 to_number() 中被忽略。
某些修饰符可应用于任何模板模式以更改其行为。例如,FM99.99 是带有 FM 修饰符的 99.99 模式。 表 9.30 显示了数值格式化的修饰符模式。
表 9.30. 数值格式化模板模式修饰符
| 修饰符 | 描述 | 示例: | 
|---|---|---|
| FM前缀 | 填充模式(抑制尾随零和填充空格) | FM99.99 | 
| TH后缀 | 大写序数后缀 | 999TH | 
| th后缀 | 小写序数后缀 | 999th | 
表 9.31 展示了 to_char 函数的一些使用示例。
表 9.31. to_char 示例
| 表达式 | 结果 | 
|---|---|
| to_char(current_timestamp, 'Day, DD  HH12:MI:SS') | 'Tuesday  , 06  05:39:18' | 
| to_char(current_timestamp, 'FMDay, FMDD  HH12:MI:SS') | 'Tuesday, 6  05:39:18' | 
| to_char(current_timestamp AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS"Z"') | '2022-12-06T05:39:18Z', ISO8601 扩展格式 | 
| to_char(-0.1, '99.99') | '  -.10' | 
| to_char(-0.1, 'FM9.99') | '-.1' | 
| to_char(-0.1, 'FM90.99') | '-0.1' | 
| to_char(0.1, '0.9') | ' 0.1' | 
| to_char(12, '9990999.9') | '    0012.0' | 
| to_char(12, 'FM9990999.9') | '0012.' | 
| to_char(485, '999') | ' 485' | 
| to_char(-485, '999') | '-485' | 
| to_char(485, '9 9 9') | ' 4 8 5' | 
| to_char(1485, '9,999') | ' 1,485' | 
| to_char(1485, '9G999') | ' 1 485' | 
| to_char(148.5, '999.999') | ' 148.500' | 
| to_char(148.5, 'FM999.999') | '148.5' | 
| to_char(148.5, 'FM999.990') | '148.500' | 
| to_char(148.5, '999D999') | ' 148,500' | 
| to_char(3148.5, '9G999D999') | ' 3 148,500' | 
| to_char(-485, '999S') | '485-' | 
| to_char(-485, '999MI') | '485-' | 
| to_char(485, '999MI') | '485 ' | 
| to_char(485, 'FM999MI') | '485' | 
| to_char(485, 'PL999') | '+485' | 
| to_char(485, 'SG999') | '+485' | 
| to_char(-485, 'SG999') | '-485' | 
| to_char(-485, '9SG99') | '4-85' | 
| to_char(-485, '999PR') | '<485>' | 
| to_char(485, 'L999') | 'DM 485' | 
| to_char(485, 'RN') | '        CDLXXXV' | 
| to_char(485, 'FMRN') | 'CDLXXXV' | 
| to_char(5.2, 'FMRN') | 'V' | 
| to_char(482, '999th') | ' 482nd' | 
| to_char(485, '"Good number:"999') | 'Good number: 485' | 
| to_char(485.8, '"Pre:"999" Post:" .999') | 'Pre: 485 Post: .800' | 
| to_char(12, '99V999') | ' 12000' | 
| to_char(12.4, '99V999') | ' 12400' | 
| to_char(12.45, '99V9') | ' 125' | 
| to_char(0.0004859, '9.99EEEE') | ' 4.86e-04' | 
如果您在文档中发现任何不正确、不符合您对特定功能的使用经验或需要进一步阐明的内容,请使用 此表单 报告文档问题。