引子

最近使用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

java 1900年_JDK与1900年01月01日相关推荐

  1. 如何避免贫穷和忙碌,在2018年你需要这样提升自己 2018年01月07日 00:00:00 2099 热文导读 | 点击标题阅读 Java和Android架构2017年总结:文章精选 吊炸天!74

    如何避免贫穷和忙碌,在2018年你需要这样提升自己 2018年01月07日 00:00:00 2099 热文导读 | 点击标题阅读 Java和Android架构2017年总结:文章精选 吊炸天!74款 ...

  2. Python 是怎么火起来的? 转载 2018年01月12日 00:00:00 133 图:Python 之父 Guido 正在设计 Python 语言,结果家里突然潜入一条大蟒蛇,一番激烈斗争,大

    Python 是怎么火起来的? 转载 2018年01月12日 00:00:00 133 图:Python 之父 Guido 正在设计 Python 语言,结果家里突然潜入一条大蟒蛇,一番激烈斗争,大蟒 ...

  3. java为什么计算时间从1970年1月1日开始

    今天在看Python  API 时,看到 time 模块 : The epoch is the point where the time starts. On January 1st of that  ...

  4. 关于通过Date.getTime()得到1970年01月1日0点零分问题验证

    我的博客:通常认为Date.getTime()可以得到得到1970年01月1日0点零分以来的毫秒数,经过实践证明是错误的 实际上通过Date.getTime()的到的是1970年01月01日8点中以来 ...

  5. 01月26日【Python3 基础知识】

    01月26日[Python3 基础知识] 5.1 九宫格 5.2 函数入门 5.3 判断某天为某年的第几天 5.1 九宫格 import random x = 0 l = [1,2,3,4,5,6,7 ...

  6. 爬虫项目(四)---采集从01月22日以来全国各省疫情数据

    采集从03月02日以来全国各省疫情数据 当然,数据来源仍然是丁香园新型冠状病毒肺炎疫情实时动态首页 url:https://ncov.dxy.cn/ncovh5/view/pneumonia 分析 确 ...

  7. 乐视生态世界发布会官方图文直播(2016年01月12日 15:00)

    2016年01月12日 13:10 生态世界即将开启!乐视将再度引领潮流,让更多人感受到生态的魅力.再一次颠覆,你准备好了吗?彻底焕新,我们来了!1月12日15:00,乐视生态世界发布会全程直播,敬请 ...

  8. Pycharm Professional Edition 激活码(license),有效期至2018年01月30日

    Pycharm Professional Edition 激活码(license)(Yep, 请复制以下全部内容) BIG3CLIK6F-eyJsaWNlbnNlSWQiOiJCSUczQ0xJSzZ ...

  9. 【财经期刊FM-Radio|2021年01月19日】

    title: [财经期刊FM-Radio|2021年01月19日] 微信公众号: 张良信息咨询服务工作室 [今日热点新闻一览↓↓] 美股美债休市,欧股走出逾一周低谷,雪铁龙并购后新公司登陆欧股首日涨超 ...

  10. 当前你所在的服务器更新维护尚未完成,我们将于01月16日09:00-13:00对所有服务器进行更...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 我们将于01月16日09:00-13:00对所有服务器进行更新维护,在此期间无法登录游戏,给大家带来的不便我们深表歉意. - 新武器 炼狱双刃(密码箱/活 ...

最新文章

  1. 说人话,搜代码,Facebook发布神经代码搜索数据集+benchmark
  2. nginx在CDN加速或使用SLB代理后,获取真实IP,做并发访问限制的方法
  3. JNI实现源码分析【二 数据结构】
  4. securecrt自动发送空格防止session卡死
  5. MySQL 面试,必须掌握的 8 大核心点
  6. python做界面用什么软件好_pyqt | 做一个好用的图形界面软件
  7. 360浏览器或chrome谷歌浏览器 打不开HTTPS网站,显示您的连接不是私密连接解决办法
  8. python情感分析模型_Python有趣|中文文本情感分析
  9. 【渝粤教育】国家开放大学2018年春季 3819-21T燃气安全管理 参考试题
  10. POJ 1579 Function Run Fun
  11. about cisco DNA
  12. 网站服务器登录很慢,网站打开速度慢如何解决?有何技巧?
  13. 登陆邮箱的方法有哪些?解析mail163邮箱如何误删恢复?
  14. vatic标注工具安装步骤(非docker安装)以及错误解决办法
  15. 魔百盒CM201-1、CM211-1朝歌ZG_支持UWE5621WiFi驱动_免拆卡刷固件包
  16. C语言——计算某日是该年的第几天
  17. 计算机如果没有什么 就无法启动,电脑开机没有任何反应
  18. vue之原生上传图片并压缩图片大小(1)
  19. Linux 系统USB设备检测
  20. IPV6----升级点,地址分类及部分协议配置

热门文章

  1. C/C++程序计时函数
  2. branca.colormap自带的json文件对颜色的定义
  3. 少壮不努力,老大写程序(kobewry) 经典话语啊
  4. 光学分辨率光声显微镜中基于深度学习的运动校正算法
  5. 百度巨变的2019年,都经历了什么鬼?
  6. 前端学习(2451):表单数据的绑定
  7. 谷歌地图启用全新卫星图:细节更清晰,色彩更丰富
  8. 加油站APP开发定制功能
  9. 深度优先搜索是什么?
  10. Laravel Database——查询构造器与语法编译器源码分析 (上)