为什么这个转换后的 Jackson 时间与预期的 Unix 时间不同?

Why is this converted Jackson time different to expected Unix time?

编辑: 原来不是杰克逊的问题,而是1920年4月1日泰国时间调整的问题

com.fasterxml.jackson.databind.ObjectMapper 是如何运作的?我以为它使用了 Unix 时间戳。

我尝试用 mapper.writeValueAsString() 转换 java.util.Date。

当我使用 mapper.readerFor(Date.class).readValue() 将字符串转换回 Date 时,结果是正确的。

但是,当我尝试删除最后 3 位数字并将相同的字符串放入某些转换器网站时,结果会关闭几分钟和几秒钟。

请看下面的代码。

Date wayBack = new SimpleDateFormat("yyyy-MM-dd").parse("1900-01-31");
System.out.println(wayBack); // Wed Jan 31 00:00:00 ICT 1900
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(wayBack)); // -2206420924000
Date deserialised = mapper.readerFor(Date.class).readValue(mapper.writeValueAsString(wayBack));
System.out.println(deserialised); // Wed Jan 31 00:00:00 ICT 1900

下面是来自http://www.onlineconversion.com/unix_time.htm

的截图

请注意,由于我所在的时区,预计会有 7 小时的休息时间,但我不明白 17:56 分钟的不同。

编辑 - 这是我试图提供比第一个更好的答案的尝试。

背景

在查看问题中的代码之前,一些背景说明:

曼谷 1900 年 1 月 31 日午夜的纪元值(以秒为单位)为 -2206420924:

LocalDateTime localDateTime = LocalDateTime.parse("1900-01-31T00:00:00");
ZoneId z = ZoneId.of("Asia/Bangkok");
ZonedDateTime ict_1 = ZonedDateTime.of(localDateTime, z);
System.out.println("Epoch seconds: " + ict_1.toEpochSecond());
System.out.println("ICT datetime : " + ict_1);

上面打印了这个:

Epoch seconds: -2206420924
ICT datetime : 1900-01-31T00:00+06:42:04[Asia/Bangkok]

同一日期 UTC 午夜的纪元值(以秒为单位)为 -1570060800:

ZonedDateTime utcDateTime = ZonedDateTime.parse("1900-01-31T00:00:00Z");
System.out.println("Epoch seconds: " + utcDateTime.toEpochSecond());
System.out.println("UTC datetime : " + utcDateTime);

上面打印了这个:

Epoch seconds: -2206396800
UTC datetime : 1900-01-31T00:00Z

1900 年 1 月 31 日曼谷的午夜时间比英国格林威治的午夜时间(本初子午线 - 或 UTC)晚 24,124 秒。

也就是说,那天曼谷比 UTC 时间(或我认为当时称为 GMT - 因为当时 UTC 尚未建立)早 6 小时 42 分 4 秒。

问题中的具体代码

首先,我更改了默认时区以匹配问题中使用的时区:

System.setProperty("user.timezone", "Asia/Bangkok");

问题中的以下行执行以下操作:

(1)SimpleDateFormat构造函数,其中日期格式字符串没有指定语言环境,使用默认语言环境。

(2) 然后parse()方法创建Date对象:

Date wayBack = new SimpleDateFormat("yyyy-MM-dd").parse("1900-01-31");

此时我们可以检查日期对象:

System.out.println(wayBack);
System.out.println(wayBack.getTime());

这将打印以下内容:

Wed Jan 31 00:00:00 ICT 1900
-2206420924000 // epoch milliseconds

这与我们之前在背景部分看到的相符。

当您使用问题中提到的在线工具时,您会看到上述毫秒值报告为以下 GMT (UTC) 日期时间:

GMT: Tuesday, January 30, 1900 5:17:56 PM

对于上面的输出,我使用了 this tool

同样,这正是我们所期望的 - 当曼谷的午夜时分,它仍然是前一天在英国格林威治的下午。

其余代码(包括 Jackson 对象映射器转换)都受制于您的 Date 对象的初始设置。

对于问题:“com.fasterxml.jackson.databind.ObjectMapper 是如何工作的?我认为它使用了 Unix 时间戳。”它显示了与核心 Java 相同的行为日期对象。我相信你的假设是正确的。

关于异常偏移

关于上图+06:42:04的ICT抵消:

1920 年 4 月 1 日,对当地的 ICT(印度支那时间)进行了调整,使其与 UTC 时间保持一致(如您所述,偏移 +7 小时)。本地时钟 向前 设置了 17 分 56 秒,以将 UTC (GMT) 偏移四舍五入为 7 小时。

有关 17 分 56 秒更改的具体参考,请参阅 this link

这就是为什么从 1920 年 4 月开始您将看不到异常偏移的原因。

更多链接

请参阅 关于较新的 java.time 类 应该使用它而不是 java.util.Date

请参阅此 question and its answers 以深入了解历史时区调整主题。