gpt4 book ai didi

java - 将 XMLGregorianCalendar 转换为 LocalDateTime 时出现时区不一致

转载 作者:行者123 更新时间:2023-11-30 01:56:27 29 4
gpt4 key购买 nike

所以我有一个带有日期/时间字段的 XML Soap 响应,其表示如下:

<BusStopTime>
<BusStopId>1023</BusStopId>
<Order>1</Order>
<PassingTime>1899-12-30T07:20:00</PassingTime>
</BusStopTime>

我对日期不感兴趣(因为这是我无法控制的一些遗留表示),而是时间。该字段由 WS 工具转换为 XMLGregorianCalendar,我的目标是进行转换。

var date = DatatypeFactory.newInstance()
.newXMLGregorianCalendar("1899-12-30T07:20:00")
.toGregorianCalendar().toInstant()

转换为LocalDateTime是siLocalimple。我明确设置 TimeZone 以避免位置冲突

LocalDateTime.ofInstant(date, ZoneId.of("Europe/Warsaw"))

结果为1899-12-30T07:44

LocalDateTime.ofInstant(date, ZoneId.of("Europe/Berlin"))

给我不同的输出1899-12-30T07:20

当日期从现代开始时(1900 年之后及之后)- 一切正常。所以问题是:十九世纪初柏林和华沙之间究竟发生了什么?或者说得更清楚一些——为什么时间的变化如此奇怪

我在 JDK8 和 JDK11 上运行(观察到相同的行为)

{ ~ }  » java -version                                                                                                                                              
openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment 18.9 (build 11.0.1+13)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.1+13, mixed mode)

java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

最佳答案

LocalDateTime.parse()

如果您可以轻松地从 XML 中获取字符串,为了获得可预测的结果,请使用

    LocalDateTime.parse("1899-12-30T07:20:00")

编辑:如果不直接访问字符串,我建议解决方案是在 XMLGregorianCalendar 上设置 GMT/UTC 的偏移量,以避免对 JVM 默认值的任何依赖时区:

    XMLGregorianCalendar xgc = DatatypeFactory.newInstance()
.newXMLGregorianCalendar("1899-12-30T07:20:00");
xgc.setTimezone(0);
LocalTime time = xgc.toGregorianCalendar()
.toZonedDateTime()
.toLocalTime();
System.out.println(time);

由于 XMLGregorianCalendar 的所谓“时区”实际上只是一个固定偏移量,因此我们设置哪个值并不重要。此代码片段的输出始终为:

07:20

我已经测试了九个不同的默认时区,包括欧洲/华沙。

既然您说您只对一天中的时间感兴趣,而不是日期,我已转换为LocalTime。如果您想要问题中的 LocalDateTime ,只需使用 toLocalDateTime 而不是 toLocalTime

或者,这是您评论中的简单解决方案:

    LocalDateTime.parse(xmllGregoriaCalendar.toXMLFormat​())

toXMLFormat​() 从创建 XMLGregorianCalendar 对象的 XML 中重新创建字符串(文档保证您会得到相同的字符串)。所以这种方式也避免了所有时区问题。

编辑:新旧类之间的分歧

在我看来,问题的核心在于旧的和过时的 TimeZone 类和现代的 ZoneId 类对于 GMT/UTC 的历史偏移量不一致。

我做了几个实验。让我们首先尝试一下似乎工作正常的时区,柏林。从 1894 年到 1915 年,柏林的偏移量为 +01:00。Java 知道:

    LocalDate baseDate = LocalDate.of(1899, Month.DECEMBER, 30);

ZoneId berlin = ZoneId.of("Europe/Berlin");
TimeZone tzb = TimeZone.getTimeZone(berlin);
GregorianCalendar gcb = new GregorianCalendar(tzb);
gcb.set(1899, Calendar.DECEMBER, 30);
ZonedDateTime zdtb = baseDate.atStartOfDay(berlin);
System.out.println("" + berlin + ' ' + tzb.getOffset(gcb.getTimeInMillis())
+ ' ' + berlin.getRules().getOffset(zdtb.toInstant())
+ ' ' + berlin.getRules().getOffset(zdtb.toInstant()).getTotalSeconds());

此代码段的输出是:

Europe/Berlin 3600000 +01:00 3600

1899 年 12 月 30 日的偏移量正确给出为 +01:00。 TimeZone 类表示 3 600 000 毫秒,ZoneId 表示 3600 秒,因此他们同意。

问题出在华沙。直到 1915 年,华沙一直位于 GMT 偏移 +01:24。让我们看看 Java 是否可以找到答案:

    ZoneId warsaw = ZoneId.of("Europe/Warsaw");
TimeZone tzw = TimeZone.getTimeZone(warsaw);
GregorianCalendar gcw = new GregorianCalendar(tzw);
gcw.set(1899, Calendar.DECEMBER, 30);
ZonedDateTime zdtw = baseDate.atStartOfDay(warsaw);
System.out.println("" + warsaw + ' ' + tzw.getOffset(gcw.getTimeInMillis())
+ ' ' + warsaw.getRules().getOffset(zdtw.toInstant())
+ ' ' + warsaw.getRules().getOffset(zdtw.toInstant()).getTotalSeconds());

Europe/Warsaw 3600000 +01:24 5040

ZoneId 正确地表示 +01:24 或 5040 秒,但这里 TimeZone 表示 3 600 000 毫秒,与柏林的情况相同。这是不正确的。

旧的 GregorianCalendar 类依赖于旧的 TimeZone 类,因此在使用欧洲/华沙时区(明确或默认)时会产生错误的结果。特别是,您从 Calendar.toInstant() 得到了错误的 Instant。正是因为 LocalDateTime.ofInstant 使用现代 ZoneId,错误才会被带入您的 LocalDateTime

此外,从欧洲/都柏林、欧洲/巴黎、欧洲/莫斯科和亚洲/加尔各答时区,我得到了矛盾的结果。

我在 Java 1.8.0_131、Java 9.0.4 和 Java 11 上运行了我的代码片段。所有版本的结果都是相同的。

链接

关于java - 将 XMLGregorianCalendar 转换为 LocalDateTime 时出现时区不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54326076/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com