xsd:dateTime 到 Java 偏移日期时间

xsd:dateTime to Java OffsetDateTime

为了妥善处理xs:dateTime with JAXB, I have to write my own converter from String->java.time.OffsetDateTime.

如 XML 架构定义中所述,dateTime 的灵感来自 ISO 8601。我使用 OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME) 来解析 xs:dateTime,它适用于例如

"2007-12-03T10:15:30+01:00" //or
"2007-12-03T10:15:30Z"

可悲的是,在xs:dateTime偏移部分is declared optional,所以解析有效

"2016-03-02T17:09:55"

抛出 DateTimeParseException.

是否有 OffsetDateTime 的 DateTimeFormatter,它也处理未分区的 xs:dateTimes(可能有默认时区)?

我认为没有内置的,但您可以在 DateTimeFormatterBuilder class.

的帮助下制作自己的

您可以在方括号中指定一个可选的偏移量,即 [XXXXX](以匹配 "+HH:MM:ss"),然后,您可以在这种情况下提供默认偏移量(parseDefaulting)它不存在的地方。如果想默认UTC,可以设置0指定不偏移;如果您想默认为 VM 的当前偏移量,您可以使用 OffsetDateTime.now().getLong(ChronoField.OFFSET_SECONDS).

获取它
public static void main(String[] args) {
    String[] dates = {
        "2007-12-03T10:15:30+01:00",
        "2007-12-03T10:15:30Z",
        "2016-03-02T17:09:55",
        "2016-03-02T17:09:55Z"
    };
    DateTimeFormatter formatter =
        new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd'T'HH:mm:ss[XXXXX]")
                                      .parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
                                      // or OffsetDateTime.now().getLong(ChronoField.OFFSET_SECONDS)
                                      .toFormatter();
    for (String date : dates) {
        System.out.println(OffsetDateTime.parse(date, formatter));
    }
}

只是为了展示我当前的解决方案,该解决方案将未分区格式解析为系统默认 offset 在当前解析 dateTime.

public static OffsetDateTime parseDateTime(String s) {
    if (s == null) {
        return null;
    }
    try {
        return OffsetDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
    } catch (DateTimeParseException e) {
        try { // try to handle zoneless xml dateTime
            LocalDateTime localDateTime = LocalDateTime.parse(s, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
            ZoneOffset offset = ZoneId.systemDefault().getRules().getOffset(localDateTime);
            return OffsetDateTime.of(localDateTime.toLocalDate(), localDateTime.toLocalTime(), offset);
        } catch (Exception fallbackTryException) {
            throw e;
        }
    }
}

jTextTime 库解决了这个问题。这是 Maven Central 提供的最小 no-dep 库。它处理缺少时区偏移量的情况。

该库包含预构建 XmlAdapters,因此您只需使用以下示例中的那些注释您的 classes:

public class Customer {

    @XmlElement
    @XmlJavaTypeAdapter(OffsetDateTimeXmlAdapter.class)
    @XmlSchemaType(name="dateTime")
    public OffsetDateTime getLastOrderTime() {
        ....
    }

    @XmlElement
    @XmlJavaTypeAdapter(OffsetDateXmlAdapter.class)
    @XmlSchemaType(name="date")
    public OffsetDateTime getDateOfBirth() {   // returns a date-only value
        ....
    }
}

或者,您可以在包级别进行注释,这样您就不必为每个 class 和每个属性都进行注释。

如果您对库对缺少时区偏移情况的默认处理不满意,那么您可以 customize

完全披露:我是 jTextTime 的作者。

正如我在评论中提到的那样添加到@Tunaki 的回答中,因为xs:dateTime 不允许基于秒的区域偏移。根据 appendPattern:

的文档,正确的模式应该只包含 [XXX]
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .appendPattern("yyyy-MM-dd'T'HH:mm:ss[XXX]")
    .parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
    .toFormatter();

其中列出了以下 table 时区格式:

  Pattern  Count  Equivalent builder methods
  -------  -----  --------------------------
    O       1      appendLocalizedOffset(TextStyle.SHORT)
    OOOO    4      appendLocalizedOffset(TextStyle.FULL)
    X       1      appendOffset("+HHmm","Z")
    XX      2      appendOffset("+HHMM","Z")
    XXX     3      appendOffset("+HH:MM","Z")
    XXXX    4      appendOffset("+HHMMss","Z")
    XXXXX   5      appendOffset("+HH:MM:ss","Z")
    x       1      appendOffset("+HHmm","+00")
    xx      2      appendOffset("+HHMM","+0000")
    xxx     3      appendOffset("+HH:MM","+00:00")
    xxxx    4      appendOffset("+HHMMss","+0000")
    xxxxx   5      appendOffset("+HH:MM:ss","+00:00")
    Z       1      appendOffset("+HHMM","+0000")
    ZZ      2      appendOffset("+HHMM","+0000")
    ZZZ     3      appendOffset("+HHMM","+0000")
    ZZZZ    4      appendLocalizedOffset(TextStyle.FULL)
    ZZZZZ   5      appendOffset("+HH:MM:ss","Z")