我正在开发一部分代码,我必须使用日历 API 使用现有的 API,而我使用的是全新的 API。在转换中出现了一些奇怪的行为,请参见以下示例:
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
String date1 = "0000-01-01T00:00:00Z";
Calendar calendar = Calendar.getInstance();
calendar.setTime(df.parse(date1));
Instant instant = calendar.toInstant();
ZonedDateTime zonedDateTime = instant.atZone(calendar.getTimeZone().toZoneId());
System.out.println(calendar.getTime() + " " +calendar.getTimeZone().getDisplayName());
System.out.println(instant.toString());
System.out.println(zonedDateTime.toString());
String date2 = "0000-01-01T00:00:00+01:00";
calendar.setTime(df.parse(date2));
instant = calendar.toInstant();
zonedDateTime = instant.atZone(calendar.getTimeZone().toZoneId());
System.out.println(calendar.getTime() + " " +calendar.getTimeZone().getDisplayName());
System.out.println(instant.toString());
System.out.println(zonedDateTime.toString());
我得到的输出如下:
Thu Jan 01 01:00:00 CET 1 Central European Standard Time
-0001-12-30T00:00:00Z
-0001-12-30T00:09:21+00:09:21[Europe/Paris]
Thu Jan 01 00:00:00 CET 1 Central European Standard Time
-0001-12-29T23:00:00Z
-0001-12-29T23:09:21+00:09:21[Europe/Paris]
因此,公历的第一行对于这两种情况都是正确的:
- 在 case1 中,我们在 +01:00 区域获得 1 月 1 日凌晨 1:00
- 在 case2 中,我们在 +01:00 区域获得 1 月 1 日上午 0:00
在从Calendar
to转换之后,Instant
我们已经看到了日期的问题,因为我们现在突然:
- 12 月 30 日(48 小时前)在案例 1 中
- 在案例 2 中的 12 月 29 日(72 小时前)... 还发现在转换过程中引入了几百毫秒的小随机误差,你在这里看不到
现在,当我们转换 next from 时Instant
,ZonedDateTime
我们现在
- 9 分 21 秒后,因为时区 Europe/Paris 传递到
instant.atZone()
导致 +00:09:21 的奇怪时区
我对它进行了更多测试,并且通常在 1583 年之前的日期之间进行转换Calendar
并且Instant
变得非常不可靠,而由于 1911 年之前的日期的时区问题,Instant
to变得不可靠。Local/ZonedDateTime
现在我知道几乎没有人为 1911 年之前的日期存储/转换时间(但我仍然可以想象这样的用例),但是,嘿!让我们看看克里斯托弗·哥伦布何时出发去发现美洲!:
1492-08-03
1492-08-11T23:00:00Z
1492-08-11
结果,我还发现,在 1911 年之前的早期,从两个 api 获取 epoch millis 会为相同的 ISO 日期产生不同的结果(问题似乎出在Calendar实现中):
System.out.println(Instant.parse("1911-01-01T00:00:00Z").toEpochMilli());
calendar.setTime(df.parse("1911-01-01T00:00:00Z"));
System.out.println(calendar.getTimeInMillis());
什么是正确的转换方法,所以它会“正常工作”(tm)?
注意:到目前为止,我认为最安全的方法是先转换为 ISO 日期字符串。有没有更好的解决方案?