Postgresql 数据库时区(timezone)设置,以及TIMESTAMPTZ和TIMESTAMP数据类型的选择
timestamp和timestamptz都占用8个字节,在存储时间时并没有本质的区别,都不携带时区信息。只是在insert保存数据和select给数据库客户端返回数据时处理方式不同。
下边以具体示例解释这两种数据类型的差别,以及他们与数据库链接时区(session对应的时区)和postgresql数据库时区之间的关系。下边例子使用的数据库时区是Etc/UTC (GMT + 0),首先创建表,然后做相应操作:
test_db=> CREATE TABLE test_table (id SERIAL NOT NULL PRIMARY KEY,lable TEXT NOT NULL,
timestamp_col TIMESTAMPTZ NOT NULL,timestamptz_col TIMESTAMPTZ NOT NULL);
CREATE TABLE
test_db=> insert into test_table(lable, timestamp_col, timestamptz_col) values ('haha',
'2022-04-13 01:15:55','2022-04-13 01:15:55');
INSERT 0 1
test_db=> select * from test_table;
id | lable | timestamp_col | timestamptz_col
----+-------+---------------------+------------------------
1 | haha | 2022-04-13 01:15:55 | 2022-04-13 01:15:55+00
(1 row)
注:因为当前session时区和数据库时区都是Etc/UTC,所以数据库保存的时间,timestamp_col
和timestamptz_col都是一样的。select显示结果也是一样的。
test_db=> show timezone;
TimeZone
----------
Etc/UTC
(1 row)
test_db=> SET TIMEZONE='Asia/Shanghai';
SET
test_db=> show timezone;
TimeZone
---------------
Asia/Shanghai
(1 row)
test_db=> insert into test_table(lable, timestamp_col, timestamptz_col) values ('hello',
'2022-04-13 01:15:55','2022-04-13 01:15:55');
INSERT 0 1
test_db=> select * from test_table;
id | lable | timestamp_col | timestamptz_col
----+-------+---------------------+------------------------
1 | haha | 2022-04-13 01:15:55 | 2022-04-13 09:15:55+08
2 | hello | 2022-04-13 01:15:55 | 2022-04-13 01:15:55+08
(2 rows)
注:因为当前session时区是Asia/Shanghai, 而数据库时区都是Etc/UTC,所以数据库保存的时间,timesttamp_col保存
的时间是2022-04-13 01:15:55,而timestamptz_col保存的时间是2022-04-12 17:15:55。当执行selects时,timestamp
类型字段不做任何转换,而timestamptz类型的字段,需要转换成当前session对应的时区的时间,所以针对第二行看起来
timesttamp_col和timestamptz_col的结果还是一样的,而第一行,结果就不一样了。
test_db=> SET TIMEZONE='Asia/Tokyo';
SET
test_db=> show timezone;
TimeZone
------------
Asia/Tokyo
(1 row)
test_db=> select * from test_table;
id | lable | timestamp_col | timestamptz_col
----+-------+---------------------+------------------------
1 | haha | 2022-04-13 01:15:55 | 2022-04-13 10:15:55+09
2 | hello | 2022-04-13 01:15:55 | 2022-04-13 02:15:55+09
(2 rows)
注:因为当前session时区是Asia/Tokyo(GMT+9), 而数据库时区都是Etc/UTC,当执行selects时,
timestamp类型字段不做任何转换,而timestamptz类型的字段,需要转换成当前session对应的时区的时间,
所以看起来timesttamp_col和timestamptz_col的结果还是一样的。
从上边的输出可以看出,随着数据库连接的时区的改变,同样的数据显示的不一样。对于timestamptz_col列,他会随着数据库链接session对应的时区的改变,而改变。而timestamp_col列,则不改变,只是把保存在数据库中的原始数据原封不动的返回给数据库链接客户端。
在插入(保存)数据时,不管是timestamp还是timestamptz,都不会保存时区信息,这也是为什么这两种类型的数据都占用8个字节,保存的时间精度也都是一样的。但是针对timestamp_col字段,只是把insert语句的时间原封不动的保存到数据库中,select的时候原封不动的返回,不会去考虑时区转换问题。而timestamptz, insert时会转换成数据库对应的时区的时间,select时,再转换成session对应的时区的时间。
因为上边我们设置的时区都是针对当前所在数据库链接的(所谓的session),当新建一个链接之后,我们之前对应之前链接设置的session信息就会丢失。
$ psql -h localhost -U test_user -p 6432 -d test_db;
Password for user test_user:
psql (14.2, server 14.1 (Debian 14.1-1.pgdg110+1))
Type "help" for help.
test_db=> select * from test_table;
id | lable | timestamp_col | timestamptz_col
----+-------+---------------------+------------------------
1 | haha | 2022-04-13 01:15:55 | 2022-04-13 01:15:55+00
2 | hello | 2022-04-13 01:15:55 | 2022-04-12 17:15:55+00
(2 rows)
test_db=> show timezone;
TimeZone
----------
Etc/UTC
(1 row)
如上所示,新建一个链接之后,当前session所在的链接又变回了数据库的默认时区。
如果要修改数据库的时区,而非session的时区,则需要修改postgresql.conf文件,
Find postgresql.conf file.
# find / -name postgresql.conf
/var/lib/pgsql/12/data/postgresql.conf
Change timezone in the postgresql.conf file
# vi /var/lib/pgsql/12/data/postgresql.conf
...
log_timezone = 'Asia/Shanghai'
...
timezone = 'Asia/Shanghai'
:wq
注意不同版本的postgresql,对应的配置文件路径可能不一样。修改完之后,需要重启。
总结:
1. timestamp和timestamptz类型的数据虽然都不保存时区信息,但是timestamptz却通过结合数据库设置的时区,session对应的时区以及timestamptz本身保存的数据,将正确的时间返回给客户端。也就是说从宏观上,timestamptz看起来确实像是保存了时区信息的timestamp数据,只不过这个时区是整个数据库设置的时区。
2. 什么时候使用timestamptz类型,什么时候使用timestamp?
简单说针对简单应用,只有单一客户端(单体应用)或同类型多个应用访问数据库(比如多个微服务),如果数据库保存的时间是由客户端应用程序保证时区正确性,这时就可以使用timestamp。因为这时,不管数据库的时区是Etc/UTC还是'Asia/Shanghai',数据库都不会对客户端传过来的数据做处理,只是原封不动的保存。select的时候再原封不动的返回。至于应用程序的客户端,比如一个新闻web应用的客户端是浏览器,如果希望根据浏览器所在区域的时区正确显示从数据库返回的时间,这是需要新闻web应用做转换,或者在时间上加上时区信息一起返回给浏览器客户端,由客户端做时间转换。
而如果有多种不类型客户端需要访问同一数据库,这个时候可以考虑使用timestamptz, 这样不管哪个客户端都可以根据当前session的时区正确获取对应的时间。
以上只是个人理解,可能有不准确的地方还望指正。
最后,需要说明的是,针对整个数据库的时区设置,个人认为作用不大。唯一的用处也许是使用postgresql命令行客户端psql连接数据库时,使session默认的时区设置成跟整个数据库时区一致,其他作用并不明显。因为如果你是使用例如DBeaver这种图形化数据库客户端工具,默认他会帮你把session的时区设置成DBeaver当前所在主机的时区,这样他显示timestamptz类型的数据时,也是完全没有问题的。
更多推荐
所有评论(0)