ZonedDateTime与XMLGregorianCalendar互转后对象不匹配的问题咨询
ZonedDateTime与XMLGregorianCalendar互转后对象不匹配的问题咨询
我最近写了个Java时间类互转的测试用例,结果最后一个断言总是失败,实在搞不懂原因,来请教下大家!
先贴我的测试代码:
@Test public void test_calendar() throws DatatypeConfigurationException { ZonedDateTime zdt = ZonedDateTime.now(); GregorianCalendar gc = GregorianCalendar.from(zdt); XMLGregorianCalendar xgc = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc); assertThat(gc.toZonedDateTime(), comparesEqualTo(zdt)); assertThat(xgc.toGregorianCalendar(), comparesEqualTo(gc)); assertThat(xgc.toGregorianCalendar().toZonedDateTime(), comparesEqualTo(zdt)); // <== 这里失败 }
失败时的错误提示是:
Expected: a value equal to <2025-06-19T18:13:44.885-05:00[America/Chicago]>
but: <2025-06-19T18:13:44.885-05:00[GMT-05:00]> was greater than <2025-06-19T18:13:44.885-05:00[America/Chicago]>
我能看出来是时区不匹配导致的,但有点懵:为什么gc.toZonedDateTime()和xgc.toGregorianCalendar()这两步的断言都能通过,偏偏最后一步转回来就不行了?
这问题我之前也踩过坑,其实是XMLGregorianCalendar的时区存储机制在搞事情:
- 首先,
GregorianCalendar.from(zdt)转出来的gc是带着完整时区ID(比如America/Chicago)的,所以gc.toZonedDateTime()能完美还原出和原zdt一致的对象,这一步断言自然没问题。 - 但当你把
gc转成XMLGregorianCalendar时,它并不会存储完整的时区ID,只会记录时区的偏移量(比如GMT-05:00),不会保留America/Chicago这种包含夏令时规则的时区标识。 - 那为什么
xgc.toGregorianCalendar()和原gc的断言能通过?因为GregorianCalendar的equals方法比较宽松,只要时间戳、偏移量、日历字段都一致,哪怕时区ID不一样也会返回true。 - 而
ZonedDateTime的equals方法就严格多了,它不仅要比对时间和偏移量,还要求时区ID完全匹配,所以最后一步转出来的ZonedDateTime用的是偏移量时区,和原zdt的时区ID不匹配,断言就失败了。
如果要保证转回来的对象完全匹配,给你个小技巧:在把XMLGregorianCalendar转回GregorianCalendar之后,手动设置回原来的时区ID:
// 先保存原时区 ZoneId originalZone = zdt.getZone(); // 从XMLGregorianCalendar转回GregorianCalendar GregorianCalendar convertedGc = xgc.toGregorianCalendar(); // 手动设置回原时区ID convertedGc.setTimeZone(TimeZone.getTimeZone(originalZone)); // 这时候再转ZonedDateTime就和原对象一致了 assertThat(convertedGc.toZonedDateTime(), comparesEqualTo(zdt));
内容来源于stack exchange
相关产品推荐
相关产品推荐

