Julio
Julio

Reputation: 439

Java XMLGregorianCalendar is changing the time - Strange behavior

I have a date as input = 2021-03-12T10:42:01.000Z.... and I want to transform into this format:

String pattern = "yyyy-MM-dd'T'HH:mm:ssZ";


public String getDate(XMLGregorianCalendar input) {
    DateFormat f = new SimpleDateFormat(pattern);
    input.toGregorianCalendar().setTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC));
    String output = f.format(input.toGregorianCalendar().getTime());
    System.out.println(output);
}

2021-03-12T12:42:01+0200

Basically, it's adding 2hs more. Maybe it's related with the time zone, I didn't test it in another computer. I have 2 questions:

Thanks

Upvotes: 2

Views: 1101

Answers (2)

Basil Bourque
Basil Bourque

Reputation: 338181

The Answer by Jon Skeet is correct, and smart. You appear to be seeing simply a time zone adjustment. Your two strings 2021-03-12T10:42:01.000Z & 2021-03-12T12:42:01+0200 represent the very same moment. The 12 noon hour, if two hours ahead of UTC, is the same as 10 AM hour with an offset-from-UTC of zero hours-minutes-seconds.

And, as mentioned in that other Answer, you really should avoid using the terrible date-time classes bundled with the earliest versions of Java.

tl;dr

myXMLGregorianCalendar     // Legacy class, representing a moment as seen in some time zone.
.toGregorianCalendar()     // Another legacy class, also representing a moment as seen in some time zone.
.toZonedDateTime()         // A modern *java.time* class, representing a moment as seen in some time zone.
.toInstant()               // Another *java.time* class, for representing a moment as seen in UTC.
.truncatedTo(              // Lopping off some smaller part of the date-time value.
    ChronoUnit.SECONDS     // Specifying whole seconds as our granularity of truncation, so lopping off any fractional second.
)                          // Returns another `Instant` object, rather than altering (mutating) the original, per immutable objects pattern.
.toString()                // Generating text representing the content of our `Instant` object, using standard ISO 8601 format.

java.time

The modern approach uses the java.time classes that years ago supplanted SimpleDateFormat, XMLGregorianCalendar , GregorianCalendar, and such.

Convert legacy <——> modern

You can easily convert from the legacy types to java.time. Look for new to/from methods on the old classes.

ZonedDateTime zdt = myXMLGregorianCalendar.toGregorianCalendar().toZonedDateTime() ;

Adjust to offset of zero

Adjust from whatever time zone to UTC by extracting an Instant. This class represents a moment as seen in UTC, always in UTC.

Instant instant = zdt.toInstant() ; 

Understand that zdt and instant both represent the same moment, the same point on the timeline, but differ in their wall-clock time.

Truncation

Given the formatting pattern seen in your Question, you seem want to work with a granularity of whole seconds. To lop off any fractional second, truncate to seconds.

Instant truncated = instant.truncatedTo( ChronoUnit.SECONDS ) ;

ISO 8601

Your desired text format is defined in the ISO 8601 standard. That standard is used by default in java.time for parsing/generating strings. So no need to specify any formatting pattern.

String output = truncated.toString() ;

Upvotes: 3

Jon Skeet
Jon Skeet

Reputation: 1499790

Basically, it's adding 2hs more

Not really. It's giving you the output for the same instant in time, but in your system local time zone - because you're creating a SimpleDateFormat without specifying a time zone (or a culture):

DateFormat f = new SimpleDateFormat(pattern);

Personally I'd recommend avoiding using java.text.SimpleDateFormat entirely, preferring the java.time types and formatters. But if you definitely want to use SimpleDateFormat, just make sure you set the time zone to UTC (assuming you always want UTC) and ideally set the culture as well (e.g. to Locale.ROOT).

Upvotes: 3

Related Questions