在Java中解析XML dateTime的最佳方法是什么?

47

在Java中解析XML dateTime的最佳方法是什么?合法的dateTime值包括2002-10-10T12:00:00-05:00和2002-10-10T17:00:00Z。

是否有一个好的开源库可以使用,或者应该使用SimpleDateFormat或类似工具自己实现?

9个回答

74

3
谢谢!我发现这是最简单的方法,而不必添加其他库。 - mlaccetti
1
这很好,但它允许没有时间戳的“dateTime”值和带有时间戳的“dateTimeStamp”值。有没有办法要求其中之一? - Rich
3
一旦升级到Java 11,就无法立即使用,因为JDK/JRE中的javax.xml.bind包已被删除。 - Manuel

41

我认为你需要使用ISODateTimeFormat.dateTimeNoMillis(),它来源于Joda Time。一般来说,我强烈建议你远离Java中内置的Date/Calendar类。Joda Time设计更好、更注重不可变性(尤其是格式化器是不可变的和线程安全的),并且是Java 7新日期/时间API的基础。

示例代码:

import org.joda.time.*;
import org.joda.time.format.*;

class Test
{   
    public static void main(String[] args)
    {
        parse("2002-10-10T12:00:00-05:00");
        parse("2002-10-10T17:00:00Z");
    }

    private static final DateTimeFormatter XML_DATE_TIME_FORMAT =
        ISODateTimeFormat.dateTimeNoMillis();

    private static final DateTimeFormatter CHECKING_FORMAT =
        ISODateTimeFormat.dateTime().withZone(DateTimeZone.UTC);

    static void parse(String text)
    {
        System.out.println("Parsing: " + text);
        DateTime dt = XML_DATE_TIME_FORMAT.parseDateTime(text);
        System.out.println("Parsed to: " + CHECKING_FORMAT.print(dt));
    }
}

输出:

Parsing: 2002-10-10T12:00:00-05:00
Parsed to: 2002-10-10T17:00:00.000Z
Parsing: 2002-10-10T17:00:00Z
Parsed to: 2002-10-10T17:00:00.000Z

(请注意,在输出中,两者最终都变成了相同的UTC时间。输出格式使用UTC是因为我们使用withZone调用要求这样做。)


1
这非常有帮助!为了保持原始的时区偏移量,我使用了DateTimeFormatter XML_DATE_TIME_FORMAT = ISODateTimeFormat.dateTimeNoMillis().withOffsetParsed() - Diego Medaglia

8

简而言之

Instant instant = Instant.parse( "2002-10-10T17:00:00Z" );
OffsetDateTime odt = OffsetDateTime.parse( "2002-10-10T12:00:00-05:00" );

详情

其他答案是正确的,但现在已经过时。它们使用麻烦的旧类,现在已被java.time框架取代。

“XML dateTime”不存在。XML没有定义除文本之外的任何数据类型。

使用java.time

输入字符串恰好符合ISO 8601标准格式。因此,在解析/生成字符串时,无需指定格式模式,因为java.time类默认使用ISO 8601。

Instant

第二个输入字符串以Z结尾,表示UTC

Instant类表示时间轴上的一个瞬间,以UTC为参考,精度为纳秒

String input = "2002-10-10T17:00:00Z":
Instant instant = Instant.parse( input );

OffsetDateTime

第一个输入字符串包含与UTC的偏移量,因此我们将其解析为OffsetDateTime

String input = "2002-10-10T12:00:00-05:00" ;
OffsetDateTime odt = OffsetDateTime.parse( input );

ZonedDateTime

如果您有特定的时区而不仅仅是UTC偏移量,请使用该时区。使用格式为continent/region正确时区名称。永远不要使用那些不是真正的时区、没有标准化、甚至不是唯一的3-4个字母缩写。请注意保留HTML标签。
ZoneId zoneId = ZoneId.of( "America/Cancun" );
ZonedDateTime zdt = odt.atZone( zoneId );

enter image description here

关于java.time

java.time框架内置于Java 8及更高版本中。这些类取代了旧的麻烦日期时间类,如java.util.Date.Calendarjava.text.SimpleDateFormat

Joda-Time项目现在处于维护模式,建议迁移到java.time。

要了解更多信息,请参见Oracle教程。并在Stack Overflow上搜索许多示例和解释。

许多java.time功能在ThreeTen-Backport中向Java 6和7进行了回溯,并在ThreeTenABP中进一步适用于Android

ThreeTen-Extra项目通过添加额外的类扩展了java.time。该项目是java.time可能未来添加的新功能的试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuarter等。


“XML dateTime”并不存在。XML没有定义除文本之外的任何数据类型。虽然如此,这个问题几乎肯定是在询问“xsd:dateTime”,也就是“XML Schema dateTime”。请参阅https://www.w3.org/TR/xmlschema-2/#dateTime。 - Laurence Gonsalves

6
StaxMan 是完全正确的。为了使用 SimpleDateFormat,您需要在每个 SimpleDateFormat 中关闭宽松解析,并迭代多个 SimpleDateFormat 格式,直到找到一个可以解析日期而不抛出异常的格式。如果保留宽松解析,您可能会在不想要匹配时得到匹配,并且XSD:DateTime 的词汇空间 在格式上具有 SimpleDateFormat 无法在单个表达式中处理的某些灵活性。
XML Schema 1.0 确实使用 ISO 8601,正如 Jon Skeet 建议的那样,Joda Time 实现了这一点,因此这是一个有效的选项。
如果您想将所有内容保留在本机 Java 包中,则还可以使用 XMLGregorianCalendarDatatypeFactory 结合使用来解析和创建 XSD:Datetime 字符串。
请参见 DatatypeFactory.newXMLGregorianCalendar 和 XMLGregorianCalendar.toXMLFormat。

2

查看解析和格式化dateTime值,不过:

  • 它将“GMT”作为默认时区。
  • 如果存在无法解析的尾随部分,则不会发出警告。
  • 它不考虑当时区设置错误的“GMT + xxxx”情况下,TimeZone默认为“GMT”的问题。

1

1
你还可以在javax.xml.datatype.DatatypeFactory中使用newXMLGregorianCalendar,这将使你拥有详细的控制权,包括检测时区是否已指定。

一个人在哪里可以找到javax.xml.datatype.DatatypeFactory的实现?我唯一找到的是在xerces2中...我以为xerces2已经过时了...(我离开Java有一段时间了) - denishaskin
你不需要自己找一个 -- 使用 DatatypeFactory.newInstance(),你的 JVM 将会搜索已经安装在 Java 中的 DatatypeFactory。 - Rich
没错,我应该在弄清楚之后删除我的问题。 - denishaskin

1
在XML Beans v2中,它将是XmlDateTime.Factory.parse(dateTimeString),但这很笨拙,因为它期望一个带有开始和结束标记的元素,如<mytime>2011-10-20T15:07:14.112-07:00</mytime> 更简单的方法是调用(new org.apache.xmlbeans.GDate(dateTimeString)).getDate()

0
理想情况下,处理 XML 数据时应该使用支持模式(或可用作基础)的程序包,并提供对已分配类型内容的访问器。我知道有一个(http://woodstox.codehaus.org/),但它(尚)不提供对日期/时间等更简单的类型(数字、数组、QNames 等)的访问。有一个请求支持 javax.xml.datatype.XMLGregorianCalendar。
然而,实际上并不多。但是,如果您正在使用特定的程序包(如 XOM 或 JDOM 等),在他们的用户列表中询问此问题可能并不是一个坏主意。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接