Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 神奇的00:09时区和DatatypeFactory_Java_Xml_Calendar - Fatal编程技术网

Java 神奇的00:09时区和DatatypeFactory

Java 神奇的00:09时区和DatatypeFactory,java,xml,calendar,Java,Xml,Calendar,在java中,有一个javax.xml.datatype.DatatypeFactory,可用于导入和导出xml日期,如下所示 String xmlDateIn = "1900-01-01T12:00:00"; DatatypeFactory df = DatatypeFactory.newInstance(); XMLGregorianCalendar xmlCalendar = df.newXMLGregorianCalendar(xmlDateIn); String xml

在java中,有一个
javax.xml.datatype.DatatypeFactory
,可用于导入和导出xml日期,如下所示

  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
,事情就会变得有趣

  GregorianCalendar gregorianCalendar = xmlCalendar.toGregorianCalendar();
  Date dts = gregorianCalendar.getTime();
  System.out.println(dts);  // prints Mon Jan 01 12:00:00 CET 1900
乍一看,它仍然可以正常工作,但实际上内部似乎有些东西坏了。通过我的IDE,我可以看到
Date
对象内部发生了什么。(如果你想知道,我住在CET时区。)看看这个奇怪的时区

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();
}

当我尝试将其转换回XML时,9分钟的时区实际上也会被打印出来。所以,这不仅仅是一个内在的东西

  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 
为了解决这个问题,如果我手动设置时区,事情会变得非常糟糕。看看这个神奇的时刻:

  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();
String xmlDateIn=“1900-01-01T12:00:00”;
DatatypeFactory df=DatatypeFactory.newInstance();
XMLGregorianCalendar xmlCalendar=df.newXMLGregorianCalendar(xmlDateIn);

xmlCalendar.setTimezone(0);// 我对日历历史和官方计时了解不多,所以我首先测试了一下,确定我使用的是您的时区:

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转换的历史原因。我想这只是一个科学追赶的问题,因为天文测量变得更加精确。

多亏了VGR的答案,我成功地确定了
xmlgoriancalendarimpl内部的错误

看看构造函数:

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);
}
它的基本功能是将
gregorianalendar
的所有内部字段映射到内部字段。e、 年、月、日、时、分、秒、毫秒

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

这就引出了以下例子:ECT时区中的日期为1900-01-01T12:00:00。它实际上有一个561000毫秒的区域偏移量。i、 e.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秒时间变化的历史原因:

在英国,“铁路时间”于19世纪40年代引入,以使当地时钟与铁路时刻表同步,1880年被单一时间GMT取代。1891年,法国采用巴黎平均时间作为标准国家时间。火车站内的时钟和火车时刻表被设置为晚点五分钟,以防止乘客错过火车

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


来源:

我无法使用Java 10.0.2重现您的结果。您使用的是哪个版本的Java?@VGR运行的是1.8.0 e u版本它似乎是该时区特有的。我在EST/EDT上,但当我将默认时区更改为CET时,我看到了您在Java 1.8.0_161和Java 10.0.2中描述的行为。有一件事困扰我进行更多的调试,那就是即使我在
Date
Date(long)
构造函数中设置了一个断点,
cdate
字段被神奇地实例化。我找不到实例化的点。这可能是某种JVM优化吗?很好的分析(+1)。请注意,CET实际上是许多地区共享一个共同的缩写,大约每个中欧国家一个。哪一个表现出你的行为我不知道,但肯定不是全部。我更喜欢使用现代的
ZoneId
ZoneRules
类,而不是过时的
TimeZone
(尽管它有更吸引人的名字)。现代的也更可靠。你的分析很出色。-这当然解释了魔法数字的来源。@OleV.V。我认为每年这个时候CET与GMT+2相当。但在冬天它变成了GMT+1。然后,欧洲人换表一小时。所以,在某种程度上,你可以说它可以是2个时区中的1个,但一次只能是1个。-这确实意味着多个欧洲国家都在效仿。e、 法国、荷兰、德国、意大利……和比利时(我居住的地方)的地图:@bvdb偏移量1900-01-01T12:00:巴黎+00:09:21,阿姆斯特丹+00:19:32,柏林+01:00,罗马相同,布鲁塞尔+00:00。它们不是同一时区。