Why GregorianCalendar randomly add "Z" at the end of date and sometimes don't

1k Views Asked by At

I had to get the current date, add 20 years, and transferred it in an XML object.

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "XMLCommande", propOrder = {
    ...
    "dateLivSouhaitee",
   ...
})
public class XMLCommande {
   ...
   @XmlElement(name = "date_liv_souhaitee", required = true)
   @XmlSchemaType(name = "date")
   protected XMLGregorianCalendar dateLivSouhaitee;
   ...
}

No date format is specified, it's all by default :

XMLCommande xmlMessage = new XMLCommande(); 

GregorianCalendar gregorianCalendar = new GregorianCalendar();
gregorianCalendar.add(Calendar.YEAR, 20);
ligne.setDateLivSouhaitee(DatatypeFactory.newInstance().newXMLGregorianCalendar(gregorianCalendar2));

The problem, is, for some unknown reason, that's sometimes I have a "Z" at the end of the date, but sometimes don't :

<date_liv_souhaitee>2041-05-26Z</date_liv_souhaitee>
<date_liv_souhaitee>2041-05-26+02:00</date_liv_souhaitee>

It's the same server, why sometimes I have the Z with "+02:00" and sometimes don't ? How can I force the format to be always :

<date_liv_souhaitee>2041-05-26+02:00</date_liv_souhaitee>
2

There are 2 best solutions below

1
On BEST ANSWER

Why do I sometimes get the Z and sometimes +02:00?

If both come from creating a GregorianCalendar using the no-arg constructor and converting it to XMLGregorianCalendar, then the best explanation is that someone is modifying the default time zone of your JVM. A part of your own program may do that or some other program running in the same JVM. To demonstrate:

    TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("Europe/Paris")));
    GregorianCalendar gc = new GregorianCalendar();
    System.out.println(DatatypeFactory.newInstance().newXMLGregorianCalendar(gc));
    
    TimeZone.setDefault(TimeZone.getTimeZone(ZoneOffset.UTC));
    gc = new GregorianCalendar();
    System.out.println(DatatypeFactory.newInstance().newXMLGregorianCalendar(gc));

Output from these code lines was:

2021-05-26T19:41:29.744+02:00
2021-05-26T17:41:29.776Z

new GregorianCalendar() creates a GregorianCalendar that has the default time zone of the JVM at the time of creation. As Arvind Kumar Avinash already explained, offset 0 from UTC is rendered as Z in accordance with the ISO 8601 standard.

How can I force +02:00 always?

I recommend that you use java.time, the modern Java date and time API, for your date work. The OffsetDateTime class represents a date and time with a UTC offset, so just set the offset to +2.

    OffsetDateTime now = OffsetDateTime.now(ZoneOffset.ofHours(2));
    OffsetDateTime in20Years = now.plusYears(20);
    String dateStringWithOffset0200 = in20Years.format(DateTimeFormatter.ISO_OFFSET_DATE);
    System.out.println(dateStringWithOffset0200);

2041-05-26+02:00

If you do need an XMLGregorianCalendar, build one from the string we just got:

    XMLGregorianCalendar xmlgc = DatatypeFactory.newInstance()
            .newXMLGregorianCalendar(dateStringWithOffset0200);
    System.out.println(xmlgc);

2041-05-26+02:00

Links

2
On

The Z in the date-time string is the timezone designator for zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).

In the other case, you have +02:00 timezone offset added in the date-time string i.e. the corresponding date-time in UTC will be the given date-time minus 2 hours. You can convert the UTC date-time into date-time with +02:00 timezone offset e.g.

import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.GregorianCalendar;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

public class Main {
    public static void main(String[] args) throws DatatypeConfigurationException {
        ZonedDateTime zdtUtc = LocalDate.of(2041, 5, 26).atStartOfDay(ZoneOffset.UTC);
        System.out.println(zdtUtc);
        ZonedDateTime zdtOffsetTwoHrs = zdtUtc.withZoneSameInstant(ZoneOffset.of("+02:00"));
        System.out.println(zdtOffsetTwoHrs);
        GregorianCalendar gregorianCalendar = GregorianCalendar.from(zdtOffsetTwoHrs);
        XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance()
                .newXMLGregorianCalendar(gregorianCalendar);
        System.out.println(xmlGregorianCalendar);
    }
}

Output:

2041-05-26T00:00Z
2041-05-26T02:00+02:00
2041-05-26T02:00:00.000+02:00