PostgreSQL 的格式化函数提供了一套极其强大的数据转换工具,用于在各种内置数据类型(日期时间、整数、浮点数、高精度数值)与格式化字符串之间进行双向解析与转换。 Table 9.26 列出了这些函数。 这些函数都遵循一个通用的调用约定:第一个参数是要被格式化的值, 第二个参数是一个定义输出或输入格式的模板。
Table 9.26. 格式化函数
函数 说明 示例 |
|---|
按照给定的格式把时间戳转换为字符串。
|
按照给定的格式把 interval 转换为字符串。
|
按照给定的格式把数字转换为字符串;
可用于
|
|
按照给定的格式把字符串转换为日期。
|
按照给定的格式把字符串转换为数字。
|
按照给定的格式把字符串转换为时间戳。
(另请参见
Table 9.33
中的
|
to_timestamp 和
to_date
的存在是为了处理简单转换无法处理的输入格式。
对于大多数标准的日期/时间格式,
只要把源字符串转换为所需的数据类型就可以了,
而且容易得多。
类似地,
对于标准的数字表示,
也用不着
to_number。
在一个
to_char
输出模板字符串中,
某些模式会被识别,
并根据给定的值被替换为相应格式的数据。
任何不是模板模式的文本都会被原样复制。
类似地,
在一个输入模板字符串中
(针对其他函数),
模板模式标识要由输入数据字符串提供的值。
如果模板字符串中存在不是模板模式的字符,
那么输入数据字符串中对应的字符会被简单地跳过
(无论它们是否等于模板字符串中的字符)。
Table 9.27 展示了可用于日期和时间值格式化的模板模式。
Table 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 or pm | 上下午指示符(不带句点) |
A.M., a.m.,
P.M. or 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 or ad | 纪元指示符(不带句点) |
B.C., b.c.,
A.D. or 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 天是第一个 ISO 周的星期一) |
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 日以来的整数天数;参见 Section B.7) |
Q | 季度 |
RM | 大写罗马数字的月份(I–XII;I=一月) |
rm | 小写罗马数字的月份(i–xii;i=一月) |
TZ | 大写的时区缩写 |
tz | 小写的时区缩写 |
TZH | 时区小时 |
TZM | 时区分钟 |
OF | 相对于 UTC 的时区偏移(HH 或 HH:MM) |
修饰符可以应用于任何模板模式以改变其行为。
例如,
FMMonth
就是带有
FM
修饰符的
Month
模式。
Table 9.28
展示了日期/时间格式化的修饰符模式。
Table 9.28. 日期/时间格式化的模板模式修饰符
| 修饰符 | 说明 | 示例 |
|---|---|---|
FM 前缀 | 填充模式(抑制前导零和填充空白) | FMMonth |
TH 后缀 | 大写的序数后缀 | DDTH, e.g., 12TH |
th 后缀 | 小写的序数后缀 | DDth, e.g., 12th |
FX 前缀 | 固定格式全局选项(参见使用注意事项) | FX Month DD Day |
TM 前缀 | 翻译模式(基于 lc_time 使用本地化的星期和月份名称) | TMMonth |
SP 后缀 | 拼写模式(未实现) | DDSP |
日期/时间格式化的使用注意事项:
FM
会抑制原本会被添加以使某个模式的输出为固定宽度的前导零和尾部
空白。
在
PostgreSQL 中,
FM
只修饰下一个说明;
而在 Oracle 中,
FM
影响所有后续的说明,
并且重复的
FM
修饰符会来回切换填充模式的开启与关闭。
无论是否指定了
FM,
TM
都会抑制尾部的空白。
to_timestamp 和
to_date
忽略输入中的字母大小写;
因此例如
MON、
Mon
和 mon
都接受相同的字符串。
在使用
TM
修饰符时,
大小写折叠按照函数输入排序规则的规则完成
(参见
Section 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
必须被指定为模板中的第一项。
在
to_timestamp 和
to_date
的模板字符串中,
一个分隔符
(空格或非字母/非数字字符)
会匹配输入字符串中的任何单个分隔符,
或者被跳过,
除非使用了
FX
选项。
例如,
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)。
如果你同时写下一个负年份和一个显式的
BC
字段,
那么又会得到公元(AD)。
年份为零的输入被视为公元前 1 年。
在
to_timestamp 和
to_date 中,
YYYY
转换在处理多于 4
位数字的年份时有一个限制。
你必须在
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
的东西;
那样会在临近年初的时候产生令人意外的结果。
(更多信息请参见
Section 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)
把 HH 和
HH12
按照 12
小时制时钟的方式格式化,
例如零小时和 36
小时都输出为
12,
而
HH24
则输出完整的小时值,
在一个
interval
值中它可能超过 23。
Table 9.29 展示了可用于数字值格式化的模板模式。
Table 9.29. 数值格式化的模板模式
| 模式 | 说明 |
|---|---|
9 | 数字位置(如果不重要则可以去掉) |
0 | 数字位置(不会被去掉,即使它不重要) |
. (period) | 小数点 |
, (comma) | 分组(千位)分隔符 |
PR | 尖括号中的负值 |
S | 锚定到数字的正负号(使用区域设置) |
L | 货币符号(使用区域设置) |
D | 小数点(使用区域设置) |
G | 分组分隔符(使用区域设置) |
MI | 指定位置上的负号(如果数字小于 0) |
PL | 指定位置上的加号(如果数字大于 0) |
SG | 指定位置上的加/减号 |
RN or rn | 罗马数字(介于 1 与 3999 之间的值) |
TH or 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
会跳过两个非数据字符。
在
to_char 中,
V
会把输入值乘以
10^,
其中
nn
是跟在
V
后面的数字的个数。
在
to_number 中,
V
以类似的方式做除法。
V
可以被看作标记输入或输出字符串中一个隐式小数点的位置。
to_char 和
to_number
不支持把
V
与小数点结合使用
(例如不允许
99.9V99)。
EEEE
(科学计数法)
不能与除了数字和小数点模式之外的任何其他格式化模式或修饰符
结合使用,
并且必须位于格式字符串的末尾
(例如
9.99EEEE
是一个有效的模式)。
在
to_number() 中,
RN
模式把罗马数字
(标准形式)
转换为数字。
输入不区分大小写,
因此
RN 和
rn
是等价的。
RN
不能与除
FM
之外的任何其他格式化模式或修饰符结合使用;
FM
只适用于
to_char(),
在
to_number()
中会被忽略。
某些修饰符可以应用于任何模板模式以改变其行为。
例如,
FM99.99
就是带有
FM
修饰符的
99.99
模式。
Table 9.30
展示了数字格式化的修饰符模式。
Table 9.30. 数值格式化的模板模式修饰符
| 修饰符 | 说明 | 示例 |
|---|---|---|
FM 前缀 | 填充模式(抑制尾部零和填充空白) | FM99.99 |
TH 后缀 | 大写的序数后缀 | 999TH |
th 后缀 | 小写的序数后缀 | 999th |
Table 9.31
展示了
to_char
函数使用的一些例子。
Table 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',ISO 8601 扩展格式 |
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' |