calcite-avatica数据传输时对日期类型压缩,导致客户端展示数据异常问题排查

发布时间 2023-06-27 18:15:03作者: 黑水滴
一、背景
用户使用如下sql来获取周开始和结束时间,直连presto查询该sql,得到的week_start=2019-12-30,而通过calcite-avatica查询出结果为week_start=2019-12-29.
日期相差一天。
with week_array as (
select 
sequence(cast('2019-12-30' as date), current_date, interval '7' day) as date_array
)
select 
week_start
, date_add('day', 6, week_start) as week_end
from week_array
cross join unnest(date_array) as t1 (week_start)
order by 1
 
二、问题排查
driver和server端,底层是通过http方式进行交互,查到底层是继承extends Meta.MetaResultSet的方法,对数据做压缩时,日期数据做了压缩。
case Types.DATE:
      final Date aDate = resultSet.getDate(j + 1, calendar);
      /*
       * USql改源码--日期类型下面逻辑做了数据压缩,但是没考虑到时区问题
       *  如用户查询日期类型2019-12-30转换成时间戳是1577635200000
       *  下面逻辑对日期做了处理1577635200000/86400000 = 18259.666666666666667强转了整形得到18259
       *  客户端那获取时用18259*86400000得到1577577600000 对应日期为2019-12-29 08:00:00
       *  所以展示给用户是2019-12-29,展示错误。
       * 解决方法
       *  转换日期前加一个时区偏差8小时
       *  1577635200000+28800000=1577664000000  压缩后1577664000000/86400000=18260
       *  客户端拿到18260 * 86400000 = 1577664000000 对应日期 2019-12-30
       */
      return aDate == null
          ? null
          : (int) ((aDate.getTime()+28800000) / DateTimeUtils.MILLIS_PER_DAY);
 
(1)排查数据转化错误问题
18259 * 86400000
18259转为日期是2019-12-29
18260转为日期是2019-12-30
(2)数据获取到后判断date类型,是日期类型格式是2019-12-30
转成时间戳:1577635200000
(3)发送数据时对数据做了优化,日期类型/86400000相当于数据压缩后再做转发
1577635200000/86400000=18259.666666666666667
(4)取了个整,导致精度缺失,发送给客户端的数据是18259
(int) (aDate.getTime() / DateTimeUtils.MILLIS_PER_DAY)
(5)客户端获取时是18259
18259*86400000=1577577600000
转日期2019-12-29 08:00:00
(6)主要原因是服务端日期类型除86400000余数直接忽略导致
(7)2021-05-31 08:00:00 和 2021-05-31 00:00:00相差时间戳
2021-05-31 08:00:00 1622419200000
2021-05-31 00:00:00 1622390400000
 
三、解决方法
获取的时间戳需要加上8小时时间差,对应毫秒数28800000,然后再转日期
 
 
关注我一起学习成长,免费ChatGPT,本人开发的程序员工具箱可以提高开发效率欢迎您来体验:www.robots2.com