java 1900年_JDK与1900年01月01日

部署运行你感兴趣的模型镜像

引子

最近使用Spark SQL读取Date类型(精度为天)的数据时发现一个很有意思的问题:

0: jdbc:hive2://0.0.0.0:10000> select cast('1900-01-01' as Date);

+-------------+--+| _c0 |

+-------------+--+| 1899-12-31 |

+-------------+--+1 row selected (0.038 seconds)

根据Spark的处理逻辑(Spark 1.6.1),上面的数据类型转换可以等效为下面的代码:

import java.util.Calendar;

import java.util.Date;

import java.util.TimeZone;

public class DateDemo {

public static void main(String[] args) {

// 1900-01-01 Date date = new Date(0, Calendar.JANUARY, 1);

long secondsPerDay = 24 * 3600 * 1000;

// Spark SQL内部使用EpochDays,即距离1970-01-01 00:00:00 GMT的天数,存储Date类型 long days = (date.getTime() + TimeZone.getDefault().getOffset(date.getTime())) / secondsPerDay;

// 将EpochDays还原为Date long millsOfDays = days * secondsPerDay;

Date newDate = new Date(millsOfDays - TimeZone.getDefault().getOffset(millsOfDays));

// 输出:Sun Dec 31 23:54:17 CST 1899 System.out.println(newDate);

}

}

分析

先来理解一下这个方法:java.util.TimeZone#getOffset(long)。

时区和零时区的时间差不是固定的么?为什么需要时间参数?

来看看java.util.TimeZone#getOffset(long)的Javadoc。

Returns the offset of this time zone from UTC at the specified date. If Daylight Saving Time is in effect at the specified date, the offset value is adjusted with the amount of daylight saving.

This method returns a historically correct offset value if an underlying TimeZone implementation subclass supports historical Daylight Saving Time schedule and GMT offset changes.

也就是说,时差还要考虑DST和历史上的时区变更!

通过stackoverlfow,我找到一个神奇的网站,记录了世界上主要城市的时区变更历史。与这个问题相关的时区(Asia/Shanghai)在1900年附近有如下变更:

在当地时间1901-01-01 00:00:00,上海时区由LMT(Local Mean Time)切换为CST (China Standard Time),与GMT的时差由+8:05:43调整为+8:00:00。

但JDK中的java.util.TimeZone#getOffset(long)对这一时区变更的处理似乎有些问题:

// 1900-01-01Date date1 = new Date(0, Calendar.JANUARY, 1);

// 输出:28800000,即+8:00:00System.out.println(TimeZone.getDefault().getOffset(date1.getTime()));

Date date2 = new Date(0, Calendar.JANUARY, 2);

// 输出:29143000,即+8:05:43System.out.println(TimeZone.getDefault().getOffset(date2.getTime()));

通过debug,发现上海时间1900-01-01 08:05:43(即1900-01-01 00:00:00 GMT)之前的时差被JDK错误计算成了8小时。

OpenJDK的bug跟踪系统里在2005年就记录了这个bug,至今未修复。。。

在DateDemo的代码中,millsOfDays的时间是1900-01-01 00:00:00 GMT,TimeZone.getDefault().getOffset(millsOfDays)返回的时差是+8:05:43。

但是因为JDK的bug,输出newDate对应的当地时间时,又是按照8小时的时差计算的,产生了5:43秒的误差。

总结

知识点:一个时区与零时区的时差在各个历史时期是不同的。

此外,Spark 2.4改进了Spark 1.6.1计算时差的逻辑,规避了JDK的bug:https://github.com/apache/spark/blob/7955b3962ac46b89564e0613db7bea98a1478bf2/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala#L1085​github.com

您可能感兴趣的与本文相关的镜像

PyTorch 2.6

PyTorch 2.6

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值