Java中使用LocalDate与旧Date类计算2020-01-01的epoch毫秒值出现1小时差值的原因
两种时间计算存在1小时差值的根本原因
核心差异就是两个API在把“年月日”这种人类可读的日期值转成epoch毫秒时间戳时,用的时区基准完全不一样,不存在谁算错,只是隐式的时区处理逻辑不统一。
LocalDate计算逻辑的时区基准
你用的Java 8新API的计算逻辑:
long millis1 = LocalDate.of(2020, Month.JANUARY, 1).toEpochDay() * millisecondsInTheDay;
LocalDate本身是完全无时区属性的纯日期类,它的toEpochDay()方法写死了计算规则:从1970-01-01 UTC零点开始,累计跨了多少个自然日。- 整个计算链路没有插入任何时区转换逻辑,最终算出的
millis1就是UTC时区下2020年1月1日零点的epoch毫秒值1577836800000,这个结果是固定的,和你跑代码的运行环境没有关系。
老式Date构造方法的隐式时区坑
你用传统Date类的计算逻辑:
long millis2 = new Date(2020 - 1900, 0, 1).toInstant().toEpochMilli();
问题完全出在new Date(int year, int month, int date)这个JDK 1.0时代就有的老构造方法上:
- 这个方法从设计之初就没留时区参数,它把传入的年、月、日参数转换成内部存的epoch毫秒值时,会偷偷用当前JVM的默认时区来解释入参:构造出来的Date对象,对应的是「JVM默认时区下2020年1月1日零点」这个时间点的毫秒值。
- 后面调用的
toInstant().toEpochMilli()只是把Date内部存的毫秒值直接读出来,本身没有任何转换或计算,不存在精度损失。
为什么差值刚好是1小时
这个1小时的差值完全能匹配你当前运行环境的JVM默认时区设置:
- 你跑代码的JVM默认时区是UTC+1:这个时区下的2020年1月1日零点,对应的UTC时间是2019年12月31日23点,比UTC基准下的2020年1月1日零点整整早1小时。
- 等于两个结果虽然都对应“2020年1月1日零点”的概念,但一个是UTC的零点,一个是UTC+1时区的零点,换算成统一的epoch毫秒值时刚好差了3600_000毫秒,也就是你观察到的1小时差值。
常见误区澄清:很多人以为
Date本身存了时区信息,实际上Date只是对epoch毫秒数的薄封装,本身和时区毫无关系。所有的时区偏差都发生在「把人能看懂的年月日转成内部毫秒值」的环节——老Date的构造方法偷偷绑了默认时区,Java 8的LocalDate计算完全基于UTC基准,这就是差异的根源。额外提醒:
new Date(年,月,日)这个构造方法早就在JDK里被标记为废弃了,官方明确不推荐使用,核心原因就是它这种隐式依赖默认时区的设计,换个运行环境就会出计算偏差,排查问题极麻烦。
内容的提问来源于stack exchange,提问作者Trismegistos
相关产品推荐
相关产品推荐

