1

I have an instance of LocalDateTime.

I need to map it XMLGregorianCalendar (using JAXB here) and in the end XML, i would like the time to look like following in XML document: 2020-03-04T19:45:00.000 + 1:00 (1 hour is the offset from UTC).

I have tried to convert the LocalDateTime to a String using DateTimeFormatter and then map it to XMLGregorianCalender.

I have two questions now:

  1. I have not been able to find any formatter in DateTimeFormatter which formats the time with offset to UTC? Does something like this exist or I need to define my formatter pattern?

    Secondly, if I'm able to format the LocalDateTime in String format I need, is it enough if I just create a XMLGregorianCalendar from string represenation?

Nitesh
  • 193
  • 1
  • 2
  • 17
  • 1
    Why should the offset be `+1:00`? Is that the default time zone of the JVM? – Andreas Mar 04 '20 at 20:48
  • Similar (not identical): [Convert between LocalDate and XMLGregorianCalendar](https://stackoverflow.com/questions/29767084/convert-between-localdate-and-xmlgregoriancalendar). I bet that there are many more. – Ole V.V. Mar 04 '20 at 22:01
  • it can be anything depending on time zone of server and UTC – Nitesh Mar 04 '20 at 22:01

1 Answers1

2

If the time zone offset is to be derived from the default time zone of the JVM, then code it like this:

LocalDateTime localDateTime = LocalDateTime.now();
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault()); // <== default
OffsetDateTime offsetDateTime = zonedDateTime.toOffsetDateTime();
XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance()
        .newXMLGregorianCalendar(offsetDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));

System.out.println(localDateTime);        // 2020-03-04T15:58:09.604171800
System.out.println(zonedDateTime);        // 2020-03-04T15:58:09.604171800-05:00[America/New_York]
System.out.println(offsetDateTime);       // 2020-03-04T15:58:09.604171800-05:00
System.out.println(xmlGregorianCalendar); // 2020-03-04T15:58:09.604171800-05:00

If you want to hardcode an offset of +01:00, then do it like this:

LocalDateTime localDateTime = LocalDateTime.now();
OffsetDateTime offsetDateTime = localDateTime.atOffset(ZoneOffset.ofHours(1)); // <== hardcoded
XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance()
        .newXMLGregorianCalendar(offsetDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));

System.out.println(localDateTime);        // 2020-03-04T16:00:04.437550500
System.out.println(offsetDateTime);       // 2020-03-04T16:00:04.437550500+01:00
System.out.println(xmlGregorianCalendar); // 2020-03-04T16:00:04.437550500+01:00

Or like this:

LocalDateTime localDateTime = LocalDateTime.now();
XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance()
        .newXMLGregorianCalendar(localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
xmlGregorianCalendar.setTimezone(60); // <== hardcoded

System.out.println(localDateTime);        // 2020-03-04T16:03:09.032191
System.out.println(xmlGregorianCalendar); // 2020-03-04T16:03:09.032191+01:00
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • This seems to work. In some cases, I’m creating LocalDateTime object without seconds or settings it to zero second, in this case, xmlgregoriancalendar throws an exception. Sample code to reproduce the exception: LocalDateTime paymentStatusTime = LocalDateTime.of(2020, 1, 1, 9, 30, 0); ZonedDateTime gmtTime = ZonedDateTime.of(localDateTime, ZoneId.of("CET")); OffsetDateTime offsetDateTime = gmtTime.toOffsetDateTime(); System.out.println("Offset time is " + offsetDateTime); return DatatypeFactory.newInstance().newXMLGregorianCalendar(offsetDateTime.toString()); – Nitesh Mar 05 '20 at 10:32
  • @Nitesh Replace `offsetDateTime.toString()` with `offsetDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)` – Andreas Mar 05 '20 at 11:02
  • Thanks I tried the same myself too and indeed it does the job – Nitesh Mar 05 '20 at 12:00
  • Another weird behaviour on the same topics and I believe there is no simple solution? When the time offset is zero, xmlgregoriancalendar replaces the offset by Z in xml? Is there any way to prevent it? – Nitesh Mar 05 '20 at 14:58
  • 1
    @Nitesh That is not weird behavior, the use of `Z` is well-defined for the [`dateTime`](https://www.w3.org/TR/2012/REC-xmlschema11-2-20120405/datatypes.html#dateTime) XML schema data type, so any XML parser should support it, and it is the spec that `XMLGregorianCalendar` adhere to. --- The `Z` is also specified in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Time_zone_designators), the basis for the XML schema-defined formats. --- While `+00:00` is allowed by the *lexical* representations for `dateTime`, the *canonical* representation specifies that `Z` MUST be used for the zero offset. – Andreas Mar 05 '20 at 16:07
  • I understand your point, but the specifications are out of our hand. I suppose bathe only solution here is to use custom data type for this field in XML? Probably a custo type extending the xmlgregoriancalendar – Nitesh Mar 05 '20 at 16:25
  • 1
    @Nitesh If someone don't support the `Z` time zone designation, then *they* are not following the XML schema standard / ISO-8601 specfication. *Shame on them!* --- Instead of "custo type", why not just use [`OffsetDateTime`](https://docs.oracle.com/javase/8/docs/api/java/time/OffsetDateTime.html) with a [`DateTimeFormatter`](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html) that won't use `Z` for the offset? E.g. if you just need millisecond precision, pattern `uuuu-MM-dd'T'HH:mm:ss.SSSxxx` will do. – Andreas Mar 05 '20 at 18:31
  • I have not tried it yet. I'll give it a try and see if it works. But I tried something similar. I replaced 'Z' in lexical formatted offset date time with '+00:00', but XMLGregorianCalendar adds back the 'Z' Code: ``` String timeWithoutZ = offsetDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME).replace('Z', '+00:00') ``` – Nitesh Mar 05 '20 at 21:06