Converting from Instant to XMLGregorianCalendar and vice-versa without losing precision



Using, java.time.Instant and javax.xml.datatype.XMLGregorianCalendar, I’m trying to convert from one to the other without losing precision.

Why is this test not passing and how to fix it?

class FooTest {
    @Test
    void shouldConvertBackToSameInstant() throws DatatypeConfigurationException {
        Instant initialInstant = Instant.now();
        XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance().newXMLGregorianCalendar(String.valueOf(initialInstant));
        Instant convertedInstant = xmlGregorianCalendar.toGregorianCalendar().toInstant();
        assertEquals(initialInstant, convertedInstant);
    }
}
org.opentest4j.AssertionFailedError: 
Expected :2021-03-10T08:34:30.700715078Z
Actual   :2021-03-10T08:34:30.700Z

Answer

Why is this test not passing?

Your XMLGregorianCalendar has got the full precision of your Instant. It’s your conversion back to Instant that is lossy. You are converting to GregorianCalendar through the call to .toGregorianCalendar(). A GreogrianCalendar only has got millisecond precision, so this is where you lose precision.

How to fix it?

Just as you go through a String for your first conversion, you may do the same for the opposite conversion:

    Instant convertedInstant = Instant.parse(xmlGregorianCalendar.toString());

Now your test passes (it did on my Java 9).

Concluding remarks

Instant has got precision up to 9 decimals on the seconds (so nanoseconds). XMLGregorianCalendar has got virtually infinite precision. So if you have converted an Instant to an XMLGregorianCalendar, you know that it hasn’t got precision beyond 9 decimals, so you can safely convert it back. If on the other hand from somewhere you should get an XMLGregorianCalendar with still higher precision (10 decimals or more), the conversion I have shown you will fail with a DateTimeParseException.



Source: stackoverflow