带有DatatypeFactory的神奇00:09时区

时间:2018-09-17 14:34:57

标签: java xml calendar

在Java中,您可以使用.navbar__box { position: relative; transition: all 0.3s ease-in-out; } .fixed { background: $aliceblue; box-shadow: 0 1px 7px $black; position: fixed; top: 0; padding-top: 10px; z-index: 1299; } 来导入和导出xml日期,如下所示。

javax.xml.datatype.DatatypeFactory

在这种简单情况下, String xmlDateIn = "1900-01-01T12:00:00"; DatatypeFactory df = DatatypeFactory.newInstance(); XMLGregorianCalendar xmlCalendar = df.newXMLGregorianCalendar(xmlDateIn); String xmlDateOut = xmlCalendar.toXMLFormat(); 等于xmlDateIn,这与预期的一样。但是,如果我希望将其作为xmlDateOut,事情就会变得有趣。

java.lang.Date

乍一看仍然可以正常工作,但实际上内部似乎有些问题。使用我的IDE,我可以看到 GregorianCalendar gregorianCalendar = xmlCalendar.toGregorianCalendar(); Date dts = gregorianCalendar.getTime(); System.out.println(dts); // prints Mon Jan 01 12:00:00 CET 1900 对象内部的情况。 (如果您想知道,我住在CET时区。)看看这个奇怪的时区。

Timezone is broken

当我尝试将其转换回XML时,实际上也会打印9分钟时区。因此,这不仅仅是内部的事情。

Date

为解决此问题,如果我手动设置时区,情况会变得非常糟糕。看看这个神奇的时刻:

  DatatypeFactory df2 = DatatypeFactory.newInstance();
  GregorianCalendar gc2 = new GregorianCalendar();
  gc2.setTime(dts);
  XMLGregorianCalendar xc2 = df2.newXMLGregorianCalendar(gc2);
  System.out.println(xc2.toXMLFormat()); // prints 1900-01-01T12:00:00.000+00:09 

Unable to fix.

实际上,我对特定程序有一种解决方法:现在对我有用的是,导入XML时我没有设置时区。 String xmlDateIn = "1900-01-01T12:00:00"; DatatypeFactory df = DatatypeFactory.newInstance(); XMLGregorianCalendar xmlCalendar = df.newXMLGregorianCalendar(xmlDateIn); xmlCalendar.setTimezone(0); // <--- ONLY CHANGE GregorianCalendar gregorianCalendar = xmlCalendar.toGregorianCalendar(); Date dts = gregorianCalendar.getTime(); 会在内部携带错误的时区,即9分钟。然后,当我最终想将Date导出回xml时,我确实将xml格里高利日历上的时区设置为0,这神奇地修复了它并再次导出了正确的xml格式。

但是,实际上,我想知道这种疯狂行为是否有任何好的解释。

2 个答案:

答案 0 :(得分:2)

我对日历历史记录和官方计时并不了解很多,所以我首先确定使用的是时区进行了测试:

int offset = (int) TimeUnit.HOURS.toMillis(1);
String[] ids = TimeZone.getAvailableIDs(offset);
TimeZone cet = Arrays.stream(ids).map(TimeZone::getTimeZone)
    .filter(tz -> tz.getDisplayName(false, TimeZone.SHORT).equals("CET"))
    .findFirst().orElseThrow(
        () -> new RuntimeException("No CET timezone found"));

TimeZone.setDefault(cet);

然后,我检查了该时区的一些内部工作原理。特别是,我打印了其历史时间转换:

System.out.println("Transitions:");
cet.toZoneId().getRules().getTransitions().forEach(
    t -> System.out.println("  " + t));

前两个这样的过渡打印为:

Transition[Overlap at 1891-03-15T00:01+00:12:12 to +00:09:21]
Transition[Overlap at 1911-03-11T00:00+00:09:21 to Z]

接着是Zulu(Z)和UTC + 01:00之间的各种“手动”过渡。

所以,1900年的午夜实际上比1912年相应的午夜晚了9分21秒。

实际上,如果将年份更改为1912,则不会出现9分钟的差异:

String xmlDateIn = "1912-01-01T12:00:00";

我无法找到12:12或9:21过渡的历史原因。我认为随着天文测量变得更加准确,这只是科学上的追赶。

答案 1 :(得分:0)

多亏了VGR的回答,我设法确定了XMLGregorianCalendarImpl内部的错误。

看看构造函数:

public XMLGregorianCalendarImpl(GregorianCalendar cal) {

    int year = cal.get(Calendar.YEAR);
    if (cal.get(Calendar.ERA) == GregorianCalendar.BC) {
        year = -year;
    }
    this.setYear(year);

    // Calendar.MONTH is zero based, XSD Date datatype's month field starts
    // with JANUARY as 1.
    this.setMonth(cal.get(Calendar.MONTH) + 1);
    this.setDay(cal.get(Calendar.DAY_OF_MONTH));
    this.setTime(
            cal.get(Calendar.HOUR_OF_DAY),
            cal.get(Calendar.MINUTE),
            cal.get(Calendar.SECOND),
            cal.get(Calendar.MILLISECOND));

    // Calendar ZONE_OFFSET and DST_OFFSET fields are in milliseconds.
    int offsetInMinutes = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET)) / (60 * 1000);
    this.setTimezone(offsetInMinutes);
}

基本上,它是将GregorianCalendar的所有内部字段映射到内部字段。例如年,月,日,时,分,秒,毫秒。

最后,它映射了时区。请注意,GregorianCalendar对象以毫秒为单位存储其时区,而XMLGregorianCalendarImpl对象以分钟为单位存储其时区。 在此转换过程中,它只剩下剩余的秒数和毫秒。这就是问题所在,日历实际上可以有一个带有秒数的时区。

这使我们进入了ECT时区下一个日期"1900-01-01T12:00:00"的示例。它实际上具有561000毫秒的区域偏移。即9分21秒。但是xml gregorian只是忽略了21秒。

如果时区为毫秒,最好输出不带时区的日期。

private String toXml(Date dts) {

  DatatypeFactory df = DatatypeFactory.newInstance();
  GregorianCalendar gc = new GregorianCalendar();
  gc.setTime(dts);
  XMLGregorianCalendar xc = df.newXMLGregorianCalendar(gc2);

  int zoneOffsetInMillis = gc.get(Calendar.ZONE_OFFSET);
  boolean zoneHasMillis zoneOffsetInMillis % (60 * 1000) != 0;
  if (zoneHasMillis) xc.setTimezone(0);

  return xc.toXMLFormat();
}

示例:

1900-01-01T12:00:00+02:00变为1900-01-01T10:09:21.000

编辑:

我实际上发现了这9分21秒的时间变化的历史原因:

  

在英国,“铁路时间”是在1840年代引入的,目的是使当地时钟与铁路时间表保持同步,并在1880年由格林尼治标准时间(GMT)取代。法国在1891年将巴黎平均时间作为其标准的国家时间。火车站内的时钟和火车时刻表都推迟了五分钟,以防止乘客错过火车。

     

1911年,巴黎平均时间更改了 9分钟21秒,以与格林威治标准时间同步。它仍然被称为巴黎平均时间,避免使用“格林威治”一词。

来源:https://vanessafrance.wordpress.com/2012/03/25/a-brief-history-of-french-time/