Phoenix日期时间问题

Phoenix中跟时间相关的类型有TIMESTAMP,DATE和TIME,这些类型对于时区的处理逻辑是相同的,所以这里就以TIMESTAMP类型为例来说明Phoenix关于时区的处理方式。首先,我们先来看下Phoenix文档中对于TIMESTAMP类型的描述:

The timestamp data type. The format is yyyy-MM-dd hh:mm:ss[.nnnnnnnnn]. Mapped to java.sql.Timestamp with an internal representation of the number of nanos from the epoch. The binary representation is 12 bytes: an 8 byte long for the epoch time plus a 4 byte integer for the nanos. Note that the internal representation is based on a number of milliseconds since the epoch (which is based on a time in GMT), while java.sql.Timestamp will format timestamps based on the client's local time zone.

意思明确指出TIMESTAMP类型在处理时是基于GMT时区的毫秒值(默认的基准都是"1970-01-01 00:00:00.000"),而java.sql.Timestamp使用的是客户端的本地时区。下面我们通过一个例子来说明这个设定在实际使用中,容易遇到的问题。

创建一个测试表

CREATE TABLE IF NOT EXISTS liqin_test(
id VARCHAR NOT NULL primary key, 
name VARCHAR,
age VARCHAR,
date1 Date,
date2 TIMESTAMP
);

upsert into liqin_test values('1001', '李钦钦','1001', '2019-3-19 23:35:00', '2019-3-19 23:35:00')
PreparedStatement pstmt = conn.prepareStatement("upsert into liqin_test values(?, ?, ?, ?, ?)");
        pstmt.setString(1, "0001");
        pstmt.setString(2, "prepareStatement_Timestamp");
        pstmt.setString(3, "0001");

        pstmt.setTimestamp(4,Timestamp.valueOf("2018-11-11 10:00:00.000"));
        pstmt.setTimestamp(5, Timestamp.valueOf("2018-11-11 10:00:00.000"));
        pstmt.executeUpdate();
        conn.commit();

执行查询语句

-----query----0001  存储时间为:2018-11-11 10:00:00.000
字段类型:date             date type getDate=2018-11-11  date1 getstring=2018-11-11 02:00:00.000
字段类型:Timestamp  Timestamp  type getTimestamp=2018-11-11 10:00:00.0  date2 getstring=2018-11-11 02:00:00.000
-----query----1001  存储时间为:2019-3-19 23:35:00
字段类型:date             date type getDate=2019-03-20  date1 getstring=2019-03-19 23:35:00.000
字段类型:Timestamp  Timestamp  type getTimestamp=2019-03-20 07:35:00.0  date2 getstring=2019-03-19 23:35:00.000

表象:用string写入用getTimestamp/getDate读取时时间戳多了8个小时;而用setTimestamp写入,用getString读出时间戳则少了8个小时。

原因:java.sql.Timestamp类型是带时区的,默认是本地时区,且不能通过函数参数设置。Phoenix在做String和Timestamp转换时使用的是GMT时区,也可以认为不带时区。比如对于"1970-01-01 08:00:00.000",Phoenix存储的数值是28800000,而Java的Timestamp.valueOf("1970-01-01 08:00:00.000").getTime()得到的数值则是0,两者混用就会出现偏差。这个逻辑也是造成程序测试结果的根本原因。

 

解决方法:对于读和写,字符串拼SQL和 PreparedStatement 设置日期对象,只选择一种处理方法。

 

Phoenix对于thin-client的处理方式也不一样,我样

-----query----0001  存储时间为:2018-11-11 10:00:00.000
字段类型:date             date type getDate=2018-11-11  date1 getstring=2018-11-11
字段类型:Timestamp  Timestamp  type getTimestamp=2018-11-11 02:00:00.0  date2 getstring=2018-11-11 02:00:00
-----query----1001  存储时间为:2019-3-19 23:35:00
字段类型:date             date type getDate=2019-03-19  date1 getstring=2019-03-19
字段类型:Timestamp  Timestamp  type getTimestamp=2019-03-19 23:35:00.0  date2 getstring=2019-03-19 23:35:00

表象:通过setDate/setTimestamp的值,getString和getTimestamp的结果是一样的(早于原值)

原理 :社区版轻客户端在实现getTimestamp的时候,在构造Timestamp对象之前先把得到的毫秒数值减去了时区。

解决方法:在获取的时间上增加8小时(转为北京时间)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值