使用Java8的LocalDateTime时,我有点困惑。
LocalDateTime ldate2 = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
LocalDateTime ldate = LocalDateTime.ofInstant(date.toInstant(), ZoneId.of("CET"));
System.out.println("Date : " + date);
System.out.println("LocalDateTime CET : " + ldate);
System.out.println("LocalDateTime " + ZoneId.systemDefault() + " : " + ldate2);
Date : Sun Dec 31 23:30:00 CET 1889
LocalDateTime CET : 1889-12-31T23:30
LocalDateTime Europe/Paris : 1889-12-31T22:39:21
我期望的最终结果是 23:30 ,这对ZoneId.of("CET")
工作正常,但我想避免指定时区来使用系统{{1} }。在这种情况下 Europe / Paris 。但后来我得到了 22:39 。我可以很容易地理解1小时的偏移,但这对我来说听起来很奇怪。
在原始日期使用ZoneId.systemDefault()
工作正常,但代码可能会在多线程环境中运行,所以我需要一个线程安全的解决方案。
有人能解释一下这种行为吗?这是预期的还是我做错了什么(或者我不理解明显的东西:))? 在此先感谢帮助我。
编辑:
这个问题与年份(1889年)有关,这一年很久,但它是Excel中用于时间表示的年份。 要查看问题的代码示例:http://ideone.com/kaHaAz
答案 0 :(得分:3)
我还没有找到这方面的来源,但看起来1889年巴黎的偏差可能确实是+00:09:21:
System.out.println(LocalDateTime.parse("1889-12-03T10:15:30").atZone(ZoneId.of("Europe/Paris")).getOffset());
// +00:09:21
答案 1 :(得分:2)
@shmosel的答案正确地提到了1889年巴黎不同的历史tz偏移。A good original source来证实这一点:
Zone Europe/Paris 0:09:21 - LMT 1891 Mar 15 0:01 0:09:21 - PMT 1911 Mar 11 0:01 # Paris MT 0:00 France WE%sT 1940 Jun 14 23:00
但这只是整个事实的一小部分。 了解Java-8中引入的java.util.TimeZone
和ZoneId
之间的差异非常重要。
新类ZoneId
解释底层tzdb-repository的规则,以便查询包括LMT-lines在内的所有条目。 LMT表示"本地平均时间&#34 ;当没有确切的历史时区偏移可用时,它是TZDB维护者的任意发明。那些LMT条目仅表示相关城市/区域的基于经度的偏移计算,并不代表任何真实的历史时区偏移。所以请小心处理这些补偿:通常不是当时人们如何看待当地时间。还要记住,大多数人(或所有人)在古代都没有足够精确的时间测量方法。 因此,在过去的时间里使用时区计算的一般方法值得怀疑。
摘要:
ZoneId
使用+00:09:21作为1889年的抵消。
但是,您说您从java.util.Date
类型的对象开始。由于此类型不代表本地日历日期或墙上时间,而是表示时刻,因此无法避免时区计算以获得所需的本地时间戳。现在的问题是使用哪种类型的时区。如果是java.util.Date
,则自然和最可能的选择不是ZoneId
,而是旧班java.util.TimeZone
。特别是如果您通过其他传统API(如Apache POI(允许访问Excel文件的库))获得Date
- 实例。
您可能认为java.util.TimeZone
和ZoneId
的内部规则对于给定的tz标识符应该相同,例如" Europe / Paris"。但不,它不一样。 传统的类TimeZone
会在1900年之前切断所有偏移转换,并使用当前的偏移量。
java.util.TimeZone
的偏移量为+01:00(当前
2017年冬季时间)。 因此,我建议您放弃课程ZoneId
并使用课程TimeZone
,以便保留每个相关课程的原始时区计算。实际上,您使用与构造Date
- 实例时相同的tz规则。 解决方案示例:
String input = "Sun Dec 31 23:30:00 CET 1889";
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH);
TimeZone tz = TimeZone.getTimeZone("Europe/Paris");
sdf.setTimeZone(tz);
Date d = sdf.parse(input);
ZoneOffset offset = ZoneOffset.ofTotalSeconds(tz.getOffset(d.getTime()) / 1000);
System.out.println(d); // Tue Dec 31 23:30:00 CET 1889
LocalDateTime ldt = LocalDateTime.ofInstant(d.toInstant(), offset);
System.out.println("ldt=" + ldt); // 1889-12-31T23:30
关于你1889年的另一个主题:
1889年对我来说是惊人的。据我所知(我的怀疑),Excel使用大约十年后的日期作为参考点,即1899-12-31。因此,没有任何日期的时钟时间将相对于1899年末的这个时代进行建模。请记住,Excel模型的日期和时间与Excel时期相比只有两倍。
另一个细节表明1899而不是1889:1889-12-31的工作日是星期二,但1899-12-31的工作日是星期天,所以你的输入"太阳12月31日23:30:00 CET 1889"不一致。请尝试检查您获取Date
- 实例的准确程度,并检查您是否在此处输入了打字错误。