一篇文章掌握Postgresql中对于日期类数据的计算和处理

在使用postgresql中的SQL语句时,往往会遇到时间和日期的处理方法,本文会对日期进行一个总结,方便更多人来使用。
对格式化字符串的介绍:

模式描述
HH一天的小时数(01-12)
HH12一天的小时数(01-12)
HH24一天的小时数(00-23)
MI分钟(00-59)
ss秒(00-59)
MS毫秒(000-999)
US微秒(000000-999999)
AM或A.M.正午标识(大写)
pm或p.m.下午标识(小写)
Y,YYY带逗号的年(4和更多位)
YYYY年(4和更多位)
YYY年的后三位
YY年的后两位
Y年的最后一位
BC或B.C. 或 AD 或 A.D.纪元标识(大写)
CC世纪(2位)
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)
DD一个月里的日子(01-31)
D一周里的日子(1-7;周日是1)
W一个月里的周数(1-5)第一周从该月第一天开始)
WW一年里的周数(1-53)(第一周从该年第一天开始算)
IWISO一年里的咒术(第一个星期四在第一周里)
Q季度
RM罗马数字的月份(I-XII ;I=1月)(大写)
rm罗马数字的月份(I-XII ;I=1月)(小写)
TZ时区名(大写)
tz时区名(小写)

对时间加减类的介绍:

操作符例子结果
+date ‘2001-09-28’ + integer ‘7’date ‘2001-10-05’
+date ‘2001-09-28’ + interval ‘1 hour’timestamp ‘2001-09-28 01:00:00’
+date ‘2001-09-28’ + time ‘03:00’timestamp ‘2001-09-28 03:00:00’
+interval ‘1 day’ + interval ‘1 hour’interval ‘1 day 01:00:00’
+timestamp ‘2001-09-28 01:00’ + interval ‘23 hours’timestamp ‘2001-09-29 00:00:00’
+time ‘01:00’ + interval ‘3 hours’time ‘04:00:00’
-- interval ‘23 hours’interval ‘-23:00:00’
-date ‘2001-10-01’ - date ‘2001-09-28’integer ‘3’ (days)
-date ‘2001-10-01’ - integer ‘7’date ‘2001-09-24’
-date ‘2001-09-28’ - interval ‘1 hour’timestamp ‘2001-09-27 23:00:00’
-time ‘05:00’ - time ‘03:00’interval ‘02:00:00’
-time ‘05:00’ - interval ‘2 hours’time ‘03:00:00’
-timestamp ‘2001-09-28 23:00’ - interval ‘23 hours’timestamp ‘2001-09-28 00:00:00’
-interval ‘1 day’ - interval ‘1 hour’interval ‘1 day -01:00:00’
-timestamp ‘2001-09-29 03:00’ - timestamp ‘2001-09-27 12:00’interval ‘1 day 15:00:00’
*900 * interval ‘1 second’interval ‘00:15:00’
*21 * interval ‘1 day’interval ‘21 days’
*double precision ‘3.5’ * interval ‘1 hour’interval ‘03:30:00’
/interval ‘1 hour’ / double precision ‘1.5’interval ‘00:40:00’

1.字符串转日期、时间戳类型

--将字符串类型转为日期类型
select to_date('2020-06-14','yyyy-mm-dd');
select to_date('14 Jun 2022','DD Mon yyyy');

--将字符串类型转为时间戳
select to_timestamp(1655211000);
select to_timestamp('2020-06-04', 'yyyy-mm-dd');

2.时间戳、日期类型转字符串

--将时间戳转为字符串(年月日)
select to_char(now(),'yyyy-mm-dd');
--将时间戳转为字符串(年月日 时分秒)
select to_char(now(),'yyyy-mm-dd HH24:MI:SS');
--将时间间隔转为字符串
select to_char(interval '1 year 1 mon 2day 3h 4m 5s','yy-mm-dd HH24:MI:SS');
--将日期转为字符串
select cast(current_date as VARCHAR);
select to_char(CURRENT_DATE,'yy-mm-dd');

3.日期与时间戳之间的互相转换

-- 带时区(推荐使用)
select EXTRACT(epoch FROM CAST(CURRENT_DATE AS TIMESTAMPTZ));
--不带时区(会多出8小时,时间会变为08:00:00)
select EXTRACT(epoch from CURRENT_DATE);
select EXTRACT(epoch FROM CAST(CURRENT_DATE AS TIMESTAMP));

4.时间、日期筛选

为方便进行演示,在这里进行构造数据。

DROP TABLE IF EXISTS "test_table";
CREATE TABLE "test_table" (
  "depart" varchar(15),
  "name" varchar(15),
  "create_date" date,
  "last_login_date" varchar(50),
  "update_time" timestamp,
  "birth_time" varchar(50),
  "salary" int8
)
;
INSERT INTO test_table (depart, name, create_date,last_login_date,update_time,birth_time,salary)
WITH test_table AS (
  SELECT UNNEST( ARRAY [ '财务', '行政', '销售', '财务', '行政', '行政' ] ) AS depart,
    UNNEST ( ARRAY [ 'A', 'B', 'A', 'C', 'D', 'C' ] ) AS NAME,
    UNNEST ( ARRAY [ cast('2021-12-21' as date), cast('2020-01-03' as date), cast('2022-01-01' as date), cast('2021-05-31' as date), cast('2020-02-28' as date), cast('2019-01-31' as date) ] ) AS create_date,
        UNNEST ( ARRAY [ '2022-06-21', '2022-06-03', '2022-06-01', '2021-06-02', '2022-06-28', '2022-05-31' ] ) AS last_login_date,
    UNNEST ( ARRAY [to_timestamp('2022-05-21 12:20:30','yyyy-mm-dd HH24:MI:SS'), to_timestamp('2022-01-03 23:59:58','yyyy-mm-dd HH24:MI:SS'), to_timestamp('2022-01-04 01:01:39','yyyy-mm-dd HH24:MI:SS'), to_timestamp('2022-01-01 11:32:36','yyyy-mm-dd HH24:MI:SS'), to_timestamp('2021-12-31 09:18:27','yyyy-mm-dd HH24:MI:SS'), to_timestamp('2021-12-30 17:12:29','yyyy-mm-dd HH24:MI:SS')] ) AS update_time,
    UNNEST ( ARRAY [ '1998-02-21 07:20:36', '1994-01-03 22:59:51', '1994-01-04 18:09:14', '1994-01-01 04:12:43', '1993-12-31 15:12:57', '1993-12-30 10:12:49']) AS birth_time,  
    UNNEST ( ARRAY [ 200, 100, 50, 30, 200, 100 ] ) AS donate
  )  SELECT
  *
FROM
  test_table ; 

数据截图:
演示数据
数据类型截图:
数据类型
以下基于上面的数据进行日期筛选,为了降低阅读成本,这里会对一些常用的写法进行整理。

-- 对字符串格式/日期格式进行筛选
--获取last_login_date大于等于2022-06-21日的
select * from test_table where last_login_date>='2022-06-21';
--获取create_date大于等于2021-05-21日的
select * from test_table where create_date>='2021-05-21';
--获取last_login_date等于2022-06-21日的
select * from test_table where last_login_date>='2022-06-21' and last_login_date <='2022-06-21';
select * from test_table where last_login_date between '2022-06-21' and '2022-06-21';
--获取last_login_date大于等于2021-05-21日并且小于等于2021-12-10日的
select * from test_table where create_date>='2021-05-21' and create_date<='2021-12-10';
select * from test_table where create_date between '2021-05-21' and '2021-12-10';

--基于字符串格式/时间戳格式的时间筛选
--对字符串格式数据进行筛选:
--获取出生日期大于等于1994-01-03的数据。
select * from test_table where birth_time>='1994-01-03';
--获取出生日期在1994-01-03和1994-01-04日期之间的数据,含1994-01-03和1994-01-04当天的数据。
--特别注意:以下写法会遗漏1994-01-04当天的数据。
--select * from test_table where birth_time between '1994-01-03' and '1994-01-04';
--需要对末尾日期加1天,等同于:'1994-01-03 00:00:00' and'1994-01-05 00:00:00'   
select * from test_table where birth_time between '1994-01-03' and '1994-01-05';
--需要对日期进行转换
select * from test_table where cast(birth_time as date) between '1994-01-03' and '1994-01-04';
--需要加上时分秒
select * from test_table where birth_time >='1994-01-03 00:00:00' and birth_time <='1994-01-04 23:59:59';
select * from test_table where birth_time between '1994-01-03 00:00:00' and '1994-01-04 23:59:59';
--基于时间戳格式的日期筛选与字符串格式的筛选方法类似,这里不多加赘述。

5.时间截断

--按年截断,结果为2022-01-01 00:00:00
SELECT date_trunc('year', TIMESTAMP '2022-06-14 20:24:30');
--按月截断,结果为2022-06-01 00:00:00
SELECT date_trunc('month', TIMESTAMP '2022-06-14 20:24:30');
--按天截断,结果为2022-06-14 00:00:00+08
SELECT date_trunc('day', TIMESTAMP WITH TIME ZONE '2022-06-14 20:24:30+08');
--按天和时区截断,结果为2022-06-14 22:00:00+08
SELECT date_trunc('day', TIMESTAMP WITH TIME ZONE '2022-06-14 20:24:30+00', 'Australia/Sydney');
--按小时截断,结果为2022-06-14 20:00:00
SELECT date_trunc('hour', TIMESTAMP '2022-06-14 20:24:30');
--按小时对时间间隔进行截断,结果为2 days 02:00:00
SELECT date_trunc('hour', INTERVAL '2 days 02:30:58');

6.计算时间差

with temp as (select now() now,now() -INTERVAL '2 year 2 month 1 day 1h 3m 33s' last_date)
select now,
last_date,
EXTRACT(year from age(now,last_date)) years,   --年
EXTRACT(year from age(now,last_date))*12+EXTRACT(month from age(now,last_date)) months,  --月份
EXTRACT(day from (now-last_date)) days,   --日
now::date-last_date::date days_2, --日
EXTRACT(epoch from (now-last_date)/3600) hours,   --时
round(EXTRACT(epoch from (now-last_date))::numeric/3600,2) hour_2fs,   --时 四舍五入形式
EXTRACT(epoch from (now-last_date))::numeric/60 minutes,   --分
trunc(EXTRACT(epoch from (now-last_date))::numeric/60,1) minutes_1fs,   --分 截断形式
round(EXTRACT(epoch from (now-last_date))::numeric/60,1) minutes_1fs_another,   --分 四舍五入形式
EXTRACT(epoch from (now-last_date)) epoch  --秒数 
from temp;

7.其他

7.1 生成日期序列
-- 按天生成自2000-01-01至2100-12-31的时间序列
SELECT generate_series(DATE '2000-01-01',
                       DATE '2100-12-31',
                       INTERVAL '1 day')::DATE
AS frequency;
  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

theskylife

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值