如何避免夏令时(DST)对Date类型数据的影响

  SQL报错
内容纲要

概要描述


夏时令(Daylight Saving Time:DST),又称“日光节约时制”和“夏令时间”,是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。各地实行夏令时的时间不一致,具体做法是会在约定的时间将时钟拨快一小时。

详细说明


  • 环境准备:TDH6.2.1

现象描述


1. 基础查询异常

  • 由于夏令时(1)的原因,Date类型的数据有可能出现自动补一小时的情况
0: jdbc:hive2://172.22.23.6:10000> select cast('19910414' as date) from system.dual;
+----------------------+
|         _c0          |
+----------------------+
| 1991-04-14 01:00:00  |
+----------------------+
1 row selected (0.141 seconds)

2. torc表中的date类型数据异常

0: jdbc:hive2://172.22.23.6:10000> create table t_orc(id int, birth_date date) CLUSTERED BY (id) INTO 2 BUCKETS stored as orc TBLPROPERTIES('transactional'='true');
No rows affected (0.315 seconds)
0: jdbc:hive2://172.22.23.6:10000> insert into t_orc select 8,'1991-04-14 12:12:12' from system.dual;
1 row affected (1.322 seconds)
0: jdbc:hive2://172.22.23.6:10000> select * from t_orc;
+-----+----------------------+
| id  |      birth_date      |
+-----+----------------------+
| 8   | 1991-04-14 01:00:00  |
+-----+----------------------+
1 row selected (0.58 seconds)

3. 日期计算函数结果异常

0: jdbc:hive2://172.22.23.6:10000> select datediff(TDH_TODATE('19910419'),TDH_TODATE('19910414')) from system.dual;
+------+
| _c0  |
+------+
| 4    |
+------+
1 row selected (0.129 seconds)

故障原因


我们系统的date(HiveDate)类型规定当时间部分为零的时候,输出不带有时间部分,如果时间部分不为零则输出带有时间部分。

torc表在处理日期类型的的时候只存日期部分,将时间部分直接舍去。所以对于日期:’1991-04-14 12:12:12’,就只保留日期部分,系统中会将该日期识别为’1991-04-14′,但是由于夏令时的影响,会直接加上一个小时的偏移量,使得日期的时间部分不为0,这样时间部分就会被输出。当然本来就不带有时间部分的日期值也会因为夏令时被加上一个小时的偏移量输出。

查询夏令时的方法

1. linux查看系统时区
[root@kevin001 ~]# ll /etc/localtime
lrwxrwxrwx. 1 root root 35 4月  29 2019 /etc/localtime -> ../usr/share/zoneinfo/Asia/Shanghai
2. 每个国家实现的夏令时的规则不同,具体夏令时时间需要查看localtime文件
3. 查看所有实现夏令时的时间

本地时区:

[root@kevin001 ~]# zdump -v /etc/localtime | grep isdst=1
/etc/localtime  Sun Jun  2 16:00:00 1940 UTC = Mon Jun  3 01:00:00 1940 CDT isdst=1 gmtoff=32400
/etc/localtime  Mon Sep 30 14:59:59 1940 UTC = Mon Sep 30 23:59:59 1940 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat Mar 15 16:00:00 1941 UTC = Sun Mar 16 01:00:00 1941 CDT isdst=1 gmtoff=32400
/etc/localtime  Tue Sep 30 14:59:59 1941 UTC = Tue Sep 30 23:59:59 1941 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat May  3 16:00:00 1986 UTC = Sun May  4 01:00:00 1986 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat Sep 13 14:59:59 1986 UTC = Sat Sep 13 23:59:59 1986 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat Apr 11 16:00:00 1987 UTC = Sun Apr 12 01:00:00 1987 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat Sep 12 14:59:59 1987 UTC = Sat Sep 12 23:59:59 1987 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat Apr  9 16:00:00 1988 UTC = Sun Apr 10 01:00:00 1988 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat Sep 10 14:59:59 1988 UTC = Sat Sep 10 23:59:59 1988 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat Apr 15 16:00:00 1989 UTC = Sun Apr 16 01:00:00 1989 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat Sep 16 14:59:59 1989 UTC = Sat Sep 16 23:59:59 1989 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat Apr 14 16:00:00 1990 UTC = Sun Apr 15 01:00:00 1990 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat Sep 15 14:59:59 1990 UTC = Sat Sep 15 23:59:59 1990 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat Apr 13 16:00:00 1991 UTC = Sun Apr 14 01:00:00 1991 CDT isdst=1 gmtoff=32400
/etc/localtime  Sat Sep 14 14:59:59 1991 UTC = Sat Sep 14 23:59:59 1991 CDT isdst=1 gmtoff=32400

某个指定时区:

[root@kevin001 ~]# zdump -v /usr/share/zoneinfo/Australia/ACT | grep isdst=1 

解决办法


解决方案一:修改jvm启动参数

着重说明下:

1)修改宿主机的时区、无法影响服务pod内的时区,即修改宿主机的时区并且重启平台服务后、pod内的时区并不会随之改变;
2)修改服务pod的时区、【有可能】对pod内java进程的jvm时区并不生效,即修改镜像的时区并且重启平台服务后、pod内java进程的jvm时区并不会随之改变。

数据的流转是在jvm中进行的,因此我们必须有效地修正相关服务的java进程jvm时区为Etc/GMT-8, 才能解决夏令时的问题。

可以通过manager上服务的config界面修改,EXTRA_DRIVER_OPTSEXTRA_EXECUTOR_OPTSEXTRA_METASTORE_OPTS添加配置-Duser.timezone=Etc/GMT-8,然后配置重启服务即可。

file

重启服务后,通过命令sudo -u hive jcmd <pid> VM.system_properties | grep timezone查看进程的jvm时区,以quark server为例:

file

解决方案二:使用string类型替代Date类型

如果并非坚决要用Date类型来装数据,可以使用string类型来存放日期数据,使用string就不会出现上述的问题。

结果验证


1. 基础查询正常

0: jdbc:hive2://172.22.23.6:10000> select cast('19910414' as date) from system.dual;
+-------------+
|     _c0     |
+-------------+
| 1991-04-14  |
+-------------+
1 row selected (0.165 seconds)

2. torc表中的date类型数据正常

0: jdbc:hive2://172.22.23.6:10000> select * from t_orc;
+-----+-------------+
| id  | birth_date  |
+-----+-------------+
| 8   | 1991-04-14  |
+-----+-------------+
1 row selected (0.601 seconds)

3. 日期计算函数结果正常

0: jdbc:hive2://172.22.23.6:10000>  select datediff(TDH_TODATE('19910419'),TDH_TODATE('19910414')) from system.dual;
+------+
| _c0  |
+------+
| 5    |
+------+
1 row selected (0.191 seconds)

这篇文章对您有帮助吗?

平均评分 0 / 5. 次数: 0

尚无评价,您可以第一个评哦!

非常抱歉,这篇文章对您没有帮助.

烦请您告诉我们您的建议与意见,以便我们改进,谢谢您。