常用的格式化(format)标识符:
yyyy:四位年份
mm:月份,不足两位补0
dd:日期,不足两位补0
hh24:小时,24小时制,不足两位补0
hh12:小时,12小时制,不足两位补0,后面带 am pm
mi:分钟,不足两位补0
ss:秒,不足两位补0
示例:2019/11/21 05:00:00 pm -- 格式 'yyyy/mm/dd hh12:mi:ss am'
2019-11-21 17:00:00 -- 格式 'yyyy-mm-dd hh24:mi:ss'
本章节每一行代码后都有运算说明和执行结果样例,例如 - - 返回当前系统日期 yyyy-mm-dd, 【2023-04-08】
select
current_date -- 返回当前系统日期,格式为yyyy-mm-dd 【2023-04-08】
,current_time -- 返回当前系统时间,格式为hh24:mi:ss 【14:48:32】
,current_timestamp -- 返回当前系统时间戳,格式为yyyy-mm-dd hh24:mi:ss 【2023-04-08 14:48:32】
,now() -- 返回当前系统时间戳,格式为yyyy-mm-dd hh24:mi:ss 【2023-04-08 14:48:32】
,localtimestamp -- 返回当前系统时间戳,格式为yyyy-mm-dd hh24:mi:ss 【2023-04-08 14:48:32】
,localtime -- 返回当前系统时间戳,格式为yyyy-mm-dd hh24:mi:ss 【14:48:32】
;
to_char()
和to_timestamp()
函数的使用对于日期的结果变换和统一非常重要,使用频率非常高的函数。
select
to_char(current_date,'yyyymmdd') -- 返回当前系统日期,格式为yyyy-mm-dd 【20230408】
,to_date('2023-04-26', 'yyyy-mm-dd') -- 将字符串转换成 date 类型的值 【2023-04-26】
,date('2023-04-26') -- 将字符串转换成 date 类型的值 【2023-04-26】看字符类型
,to_timestamp('2023-04-26', 'yyyy-mm-dd') -- 将字符串转换成 timestamp 类型的值 【2023-04-26 00:00:00】
,to_timestamp('2023-04-26 12:30:45','yyyy-mm-dd hh24:mi:ss') -- 将字符串转换成 timestamp 类型的值 【2023-04-26 12:30:45】
,to_timestamp('20230426123045','yyyymmddhh24miss') -- 将字符串转换成 timestamp 类型的值 【2023-04-26 12:30:45】
,to_timestamp('2023042612'||'3045','yyyymmddhh24miss') -- 将字符串转换成 timestamp 类型的值 【2023-04-26 12:30:45】
,'2023-04-26 16:30:45'::date -- 将字符串转换成 date 类型的值 【2023-04-26】
,'2023-04-26 16:30:45'::time -- 将字符串转换成 time 类型的值 【16:30:45】
,'2023-04-26 16:30:45'::timestamp -- 将字符串转换成 timestamp 类型的值 【2023-04-26 16:30:45】
;
转换函数的嵌套使用,这种嵌套在实际工作中非常常用且很实用。
select
to_char(to_date('2023-04-26','yyyy-mm-dd'), 'yyyy年mm月dd日') -- 将日期/时间类型的值转换成字符串类型 【2023年04月26日】
,to_char(to_timestamp('2023-04-26 16:30:45','yyyy-mm-dd hh24:mi:ss'),'yyyy/mm/dd hh24时mi分ss秒 ') -- 将字符串转换成 TIMESTAMP 类型的值 【2023/04/26 16时30分45秒】
,to_char(to_timestamp('2023-04-26 16:30:45','yyyy-mm-dd hh24:mi:ss'),'hh12miss am') -- 将字符串转换成 TIMESTAMP 类型的值 【043045 pm】
;
GreenPlum和PostgreSQL中常用的日期间隔计算函数为age()
,或者两个日期直接相减(第2行代码),age()
的计算结果是 interval 格式,两个日期直接相减是间隔天数。
select
-- 减法
interval '2 years 3 months 10 days' -- 创建一个时间间隔 【2 years 3 mons 10 days】
,'2023-04-26'::date - '2021-04-26'::date -- 两个日期直接相减计算天数 【730】
,age('2023-04-26', '2021-12-25') -- 计算两个日期之间的时间差 【1 year 4 mons 1 day】
,age('2023-04-26'::date,'2021-12-25'::date) -- 计算两个日期之间的时间差 【1 year 4 mons 1 day】
,age(timestamp '2023-04-26', timestamp '1950-08-01') -- 计算两个日期之间的时间差 【72 years 8 mons 25 days】
,age(timestamp '1950-08-01') -- 从当前系统日期current_date(2023-04-08) 减去参数后的结果 【72 years 8 mons 7 days】
-- 加法
,'2023-01-19 09:12:00'::timestamp + interval '1 day' -- 【2023-01-20 09:12:00】
,'2023-01-20 09:12:00'::timestamp + interval '4 week' -- 【2023-02-17 09:12:00】
,'2023-01-20 09:12:00'::date + interval '3 month' -- 【2023-04-20 00:00:00】
,'2023-01-20 09:12:00'::date + interval '1 year' -- 【2024-01-20 00:00:00】
,'2023-01-20 09:12:00'::date + interval '100 year' -- 【2123-01-20 00:00:00】
;
函数1:date_part() 从日期/时间字符串中取出指定部分的值
本人比较习惯于使用date_part()函数做日期时间指定值的提取,简单齐全。
select
date_part('year', date '2023-04-26') -- 【2023】
,date_part('month', '2023-04-26'::date) -- 【4】
,date_part('week', '2023-04-26'::date) -- 【17】
,date_part('day', '2023-04-26 19:20:21'::timestamp) -- 【26】
,date_part('month', interval '2 years 3 months 10 days') -- 【3】
,date_part('year', interval '2 years 3 months 10 days')::text
||(case when length(date_part('month', interval '2 years 3 months 10 days')::text)=2
then date_part('month', interval '2 years 3 months 10 days')::text
else '0'||date_part('month', interval '2 years 3 months 10 days')::text
end)
||(case when length(date_part('day', interval '2 years 3 months 10 days')::text)=2
then date_part('day', interval '2 years 3 months 10 days')::text
else '0'||date_part('day', interval '2 years 3 months 10 days')::text
end) -- 【20310】 跟DB2的计算一样,这里用五位数字代表间隔的年月日
;
函数2:extract() 从日期/时间字符串中抽取指定部分的值,与date_part()效果一样
select
extract(year from timestamp '2023-04-26 19:20:21') -- 【2023】
,extract(quarter from '2023-04-26 19:20:21'::timestamp) -- 【2】
,extract(month from '2023-04-26 19:20:21'::timestamp) -- 【4】
,extract(week from '2023-04-26 19:20:21'::timestamp) -- 【17】
,extract(day from '2023-04-26 19:20:21'::timestamp) --【26】
,extract(hour from '2023-04-26 19:20:21'::timestamp) -- 【19】
,extract(minute from '2023-04-26 19:20:21'::timestamp) -- 【20】
,extract(second from '2023-04-26 19:20:21'::timestamp) -- 【21】
-- interval 时间间隔
,extract(month from interval '2 years 3 months 10 days') -- 【3】
,extract(year from interval '2 years 3 months 10 days')::text
||(case when length(extract(month from interval '2 years 3 months 10 days')::text)=2
then extract(month from interval '2 years 3 months 10 days')::text
else '0'||extract(month from interval '2 years 3 months 10 days')::text
end)
||(case when length(extract(day from interval '2 years 3 months 10 days')::text)=2
then extract(day from interval '2 years 3 months 10 days')::text
else '0'||extract(day from interval '2 years 3 months 10 days')::text
end) -- 【20310】 跟DB2的计算一样,这里用五位数字代表间隔的年月日
;
函数3:date_trunc() 截取日期到指定的时间单位,date_trunc()函数的返回可以看到还是个日期时间格式,这是与date_part()和extract()差异所在,计算后直接返回日期时间
select
date_trunc('day',date '2023-04-26') -- 【2023-04-26 00:00:00】
,date_trunc('week', '2023-04-26 19:20:21'::timestamp) -- 【2023-04-24 00:00:00】
,date_trunc('month', '2023-04-26'::date) -- 【2023-04-01 00:00:00】
,date_trunc('year', '2023-04-26'::date) -- 【2023-01-01 00:00:00】
,date_trunc('hour', '2023-04-26 19:20:21'::timestamp) -- 【2023-04-26 19:00:00】
,date_trunc('minute', '2023-04-26 19:20:21'::timestamp) -- 【2023-04-26 19:20:00】
,date_trunc('second', '2023-04-26 19:20:21'::timestamp) -- 【2023-04-26 19:20:21】
;
两个日期直接相减,得到的结果是日期间的间隔天数,如下1和2行代码执行结果;
两个日期通过age函数相减,得到结果是 interval 间隔格式,如下3和4行代码执行结果;
3和4行代码执行结果,可以经过 || 拼接加工为类似于DB2的日期相减格式来使用,如五位数字的10407,即1年4个月7天。
select
'2023-04-26'::date - (date_trunc('week','2021-12-26'::date) - interval '1 day') -- 【493 days】
,date_part('day','2023-04-26'::date - (date_trunc('week','2021-12-26'::date) - interval '1 day')) --【493.0】
,age('2023-04-26', '2021-12-26') --【1 year 4 mons】
,age('2023-04-26', (date_trunc('week','2021-12-26'::date) - interval '1 day')) --【1 year 4 mons 7 days】
,age('2023-04-26', '2021-12-20'::date - interval '1 day') -- 【1 year 4 mons 7 days】
,date_part('year', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text
||(case when length(date_part('month', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text)=2
then date_part('month', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text
else '0'||date_part('month', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text
end)
||(case when length(date_part('day', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text)=2
then date_part('day', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text
else '0'||date_part('day', age('2023-04-26', '2021-12-20'::date - interval '1 day'))::text
end) -- 【10407】
;
第一步,构建表结构。因为某系统经常用到昨日、上周末、上旬末、上月末、上季末、上年同期的日期,每次调用都计算就很麻烦,所以开发了一张日期时间表,每次调用表即可。具体内容如下:
-- 存储表建表
CREATE TABLE public.date_information (
dtstatdate date NOT NULL, -- 数据日期
period_code bpchar(3) NOT NULL, -- 内容编码
period_date date NOT NULL, -- 内容日期
period_name varchar(10) NOT NULL, -- 内容名称
period_type int4 NOT NULL, -- 内容格式
interval_days int4 NOT NULL -- 内容日期与数据日期间隔天数
);
COMMENT ON TABLE public.date_information IS '日期时间表';
-- Column comments
COMMENT ON COLUMN public.date_information.dtstatdate IS '数据日期';
COMMENT ON COLUMN public.date_information.period_code IS '内容编码';
COMMENT ON COLUMN public.date_information.period_date IS '内容日期';
COMMENT ON COLUMN public.date_information.period_name IS '内容名称';
COMMENT ON COLUMN public.date_information.period_type IS '内容格式';
COMMENT ON COLUMN public.date_information.interval_days IS '内容日期与数据日期间隔天数';
第二步,编写存储过程,存储过程较长,完整脚本放在了文章最后;
第三步,调用存储过程生成对应日期,并插入到目标表中。
-- 存储过程调用
select date_inf((current_date - interval '1 day')::date);
select date_inf(('2023-01-21'::date - interval '1 day')::date);
第四步,结果验证
select * from public.date_information;
日期时间间隔计算的完整存储过程如下所示
CREATE OR REPLACE FUNCTION date_inf(dt date)
RETURNS integer
LANGUAGE plpgsql
AS $function$
DECLARE V_STEP VARCHAR(10) default '0';
begin
V_STEP :='1'; -- 删除历史数据
delete from date_information where dtstatdate = dt;
V_STEP :='2'; -- 插入select数据
insert into date_information(dtstatdate,
period_code,
period_date,
period_name,
period_type,
interval_days
)
select dtstatdate,
period_code,
period_date::date period_date,
period_name,
period_type,
interval_days
from
(select dt as dtstatdate,
'CUR' period_code,
to_char(dt,'yyyy-mm-dd') as period_date,
'本期' period_name,
1 period_type,
0 interval_days
union all
select dt as dtstatdate,
'PDE' period_code,
to_char(dt- interval '1 day','yyyy-mm-dd') as period_date,
'昨日' period_name,
1 period_type,
1 interval_days
union all
select dt as dtstatdate,
'PWE' period_code,
to_char(date_trunc('week',dt) - interval '1 day','yyyy-mm-dd') as period_date,
'上周末' period_name,
1 period_type,
date_part('day',dt - (date_trunc('week',dt) - interval '1 day')) interval_days
union all
select dt as dtstatdate,
'PTE' period_code,
case when substr(dt::text,9,10)::int < 11
then to_char(concat(substr(dt::text,1,8),'01')::date - interval '1 day','yyyy-mm-dd')
when substr(dt::text,9,10)::int < 21
then to_char(concat(substr(dt::text,1,8),'10')::date,'yyyy-mm-dd')
when substr(dt::text,9,10)::int >= 21
then to_char(concat(substr(dt::text,1,8),'20')::date,'yyyy-mm-dd')
end as period_date,
'上旬末' period_name,
1 period_type,
date_part('day',dt - (case when substr(dt::text,9,10)::int < 11
then concat(substr(dt::text,1,8),'01')::date - interval '1 day'
when substr(dt::text,9,10)::int < 21
then concat(substr(dt::text,1,8),'10')::date
when substr(dt::text,9,10)::int >= 21
then concat(substr(dt::text,1,8),'20')::date
end)) interval_days
union all
select dt as dtstatdate,
'PME' period_code,
to_char(date_trunc('month',dt) - interval '1 day','yyyy-mm-dd') as period_date,
'上月末' period_name,
1 period_type,
date_part('day',dt - (date_trunc('month',dt) - interval '1 day')) interval_days
union all
select dt as dtstatdate,
'PQE' period_code,
to_char(date_trunc('quarter',dt) - interval '1 day','yyyy-mm-dd') as period_date,
'上季末' period_name,
1 period_type,
date_part('day',dt-(date_trunc('quarter',dt) - interval '1 day')) interval_days
union all
select dt as dtstatdate,
'PYE' period_code,
to_char(date_trunc('year',dt) - interval '1 day','yyyy-mm-dd') as period_date,
'上年末' period_name,
1 period_type,
date_part('day',dt-(date_trunc('year',dt) - interval '1 day')) interval_days
union all
select dt as dtstatdate,
'PYS' period_code,
to_char(dt - interval '1 year','yyyy-mm-dd') as period_date,
'上年同期' period_name,
1 period_type,
date_part('day',dt-(dt - interval '1 year')) interval_days
) a
;
return 0;
END;
$function$
;
声明:本文所载信息不保证准确性和完整性。文中所述内容和意见仅供参考,不构成实际商业建议,如有雷同纯属巧合。