【问题标题】:xsd:dateTime to Java OffsetDateTimexsd:dateTime 到 Java OffsetDateTime
【发布时间】:2016-03-02 18:02:16
【问题描述】:

为了使用 JAXB 正确处理 xs:dateTime,我必须从 String->java.time.OffsetDateTime 编写自己的转换器。

正如 XML Schema Definition 中提到的,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(可能带有默认时区)?

【问题讨论】:

    标签: xml datetime xsd jaxb java-8


    【解决方案1】:

    我认为没有内置的,但您可以在 DateTimeFormatterBuilder 类的帮助下自己制作。

    您可以指定用方括号括起来的可选偏移量,即[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));
        }
    }
    

    【讨论】:

    • 好答案。但是我们都必须这样做不是很奇怪吗?为什么DateTimeFormatter 没有xs:date 的标准模式。 xs:dateTimexs:time ?
    • 实际上,我认为xs:dateTime 的正确模式是[XXX](3 Xs 而不是5 Xs),因为xs:dateTime 不允许基于秒的区域偏移。
    • @b4hand: 或 6,因为 +01:00 是 6 个字符;-)
    • 不,从文档中引用,您应该只使用 3 个 X。 XXX 等价于 appendOffset("+HH:MM","Z")。见docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/…
    • 请注意,使用本地时区偏移量会导致 DST 出现问题。如果字符串中的时间是 DST 而当前时间不是 DST,反之亦然,则该偏移量会减少一个小时(通常)。例如,如果您在中欧并且当前是夏令时,则 OffsetDateTime.now() 的偏移量为 2 小时,而对于夏令时以外的日期应该是 1 小时。 Stefan K 的回答没有这个问题。
    【解决方案2】:

    只是为了展示我当前的解决方案,它将未分区格式解析为系统默认的当前解析日期时间的偏移量

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

    【讨论】:

    • 啊,所以当偏移量丢失时,您可以解释 JVM 时区中的日期时间。我想我会使用localDateTIme.atZone(ZoneId.systemDefault()).toOffsetDateTime()。是不是更容易阅读?
    【解决方案3】:

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

    该库包含预构建 XmlAdapters,因此您可以使用以下示例中的类注释您的类:

    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
            ....
        }
    }
    

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

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

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

    【讨论】:

      【解决方案4】:

      添加到@Tunaki 的答案,正如我在评论中提到的那样,因为xs:dateTime 不允许基于秒的区域偏移。根据appendPattern的文档,正确的模式应该只包含[XXX]

      DateTimeFormatter formatter = new DateTimeFormatterBuilder()
          .appendPattern("yyyy-MM-dd'T'HH:mm:ss[XXX]")
          .parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
          .toFormatter();
      

      下表列出了时区格式:

        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")
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-22
        • 1970-01-01
        • 1970-01-01
        • 2019-03-09
        • 1970-01-01
        • 1970-01-01
        • 2016-04-09
        • 1970-01-01
        相关资源
        最近更新 更多