0

I had the below issue During daylight change CST-CDT reset.

Am getting the Input from Was8.5 server 2018-03-11-05.00 (UTC-5) as expected, but when it comes to WAS7 server, the below method returns Sun Mar 10 00.00.00 CST 2018 instead of Sun Mar 11 00.00.00 CDT 2018

/*
 * Converts XMLGregorianCalendar to java.util.Date
 */
public static Date toDate(XMLGregorianCalendar calendar){
    if(calendar == null) {
        return null;
    }
    return calendar.toGregorianCalendar().getTime();
}

I know the server date/timezone reset didn’t take place properly, but in case if I want to get right Time when CST to CDT change or vise versa. How can I rewrite the code to convert XMLGregorianCalendar to java.util.Date in Java?

Something like, If incoming request was CST(UTC-6), the toDate(XMLGregorianCalendar calendar) returns CDT (UTC-5). then I want toDate() should return CST (UTC-6).

the same way,

If incoming request was CDT(UTC-5), the toDate(XMLGregorianCalendar calendar) returns CST(UTC-6). then i want toDate() should return CDT(UTC-5).

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
jcrshankar
  • 1,165
  • 8
  • 25
  • 45
  • A `Date` doesn’t (as in cannot) contain a time zone or UTC offset. Your question is possibly a duplicate of [How to set time zone of a java.util.Date?](https://stackoverflow.com/questions/2891361/how-to-set-time-zone-of-a-java-util-date) – Ole V.V. Apr 03 '18 at 09:06
  • 1
    What you ask for doesn’t really make sense, sorry. March 11 at 00:00 hours DST was not yet in effect (it began at 02:00), so “Sun Mar 11 00.00.00 CDT 2018” doesn’t denote an existing time. – Ole V.V. Apr 03 '18 at 09:44

2 Answers2

2

java.util.Date doesn't have a timezone. It just have a long value that represents the number of milliseconds since unix epoch.

What you see (Sun Mar 10 00.00.00 CST 2018) is the result of toString() method, and it uses the JVM default timezone to convert the long value to a date and time in that timezone. See this article for more details: https://codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date/

Anyway, one way to really know what's happening is to check this long value:

long millis = calendar.toGregorianCalendar().getTimeInMillis();

And then you can print this value in UTC:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss XXX");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(sdf.format(new Date(millis)));

Or, if you use Java 8:

System.out.println(Instant.ofEpochMilli(millis));

This will tell you the UTC instant that the Date corresponds to, so you can debug your code a little better than relying on Date::toString() method, which is confusing and misleading.

Regarding your main issue, I've tried to reproduce (I'm using Java 8 because it's easier to manipulate than using Date). First I created a date/time corresponding to 2018-03-11 in UTC-05:00, and I assumed the time to be midnight:

// March 11th 2018, midnight, UTC-05:00
OffsetDateTime odt = OffsetDateTime.parse("2018-03-11T00:00-05:00");

Then I converted this to America/Chicago timezone, which is a zone that uses CST/CDT:

// get the same instant in Central Time
ZonedDateTime zdt = odt.atZoneSameInstant(ZoneId.of("America/Chicago"));

Then I printed this:

// print the date/time with timezone abbreviation
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm xxx z", Locale.US);
System.out.println(zdt.format(fmt)); // 2018-03-10 23:00 -06:00 CST

Note that the result is 2018-03-10 23:00 -06:00 CST: March 10th in UTC-06:00.

That's because in 2018, Daylight Saving Time starts only at 2 AM of March 11th. At midnight, DST has not started yet, so the offset is still UTC-06:00.

Anyway, your conversion code is correct, because Date just represents a point in time (a count of elapsed time since epoch) and doesn't have timezone attached to it. Perhaps the problem lies somewhere, and checking the millis value might help you to understand what's going on (my guess is that XmlGregorianCalendar sets the time to midnight when it's not present, which would explain the result of Sun Mar 10 00.00.00 CST 2018).

If that helps, the exact UTC instant where DST transition occurs (March 11th 2018 at 2 AM in UTC-06:00) corresponds to the millis value 1520755200000. If your dates in March 2018 have a value lower than that, it means they're before DST starts, and they'll be in CST.

iolus
  • 55
  • 3
0

My first suggestion is that you don’t need what you are asking for. As I see it, you’ve got a date and a UTC offset, and I don’t really see that the offset adds any useful information. Just take the date. I believe what has happened was that a point in time after the transition to summer time on March 11 was stripped of the time-of-day, but the UTC offset was kept for whatever reason or maybe for no reason at all. When giving the time at start of day (00:00), the offset disagrees with your time zone of America/Chicago (or Central Time Zone, but the ID in region/city format is unambiguous and recommended).

And don’t use java.util.Date for your date. That class is long outdated. Today we have so much better in java.time, the modern Java date and time API. Furthermore its LocalDate class is better suited for a date without time-of-day because this is exactly what it is, while a Date is really a point a in time, that is, a whole different story. Depending on taste conversion from XMLGregorianCalendar can happen in two ways.

The direct way

    return LocalDate.of(calendar.getYear(), calendar.getMonth(), calendar.getDay());

With your XMLGregorianCalendar of 2018-03-11-05:00 the result is a LocalDate of 2018-03-11.

The indirect way via GregorianCalendar and ZonedDateTime:

    return calendar.toGregorianCalendar().toZonedDateTime().toLocalDate();

The result is the same. The advantage of the latter is you don’t need to concern yourself with the individual fields of year, month and day-of-month. Among other things this means you don’t risk putting them in the wrong order.

If you do insist on keeping the time zone or UTC offset, at least take the offset. Sun Mar 11 00.00.00 CDT 2018 doesn’t make sense because March 11 at 00:00 hours DST was not yet in effect (it began at 02:00). Such a non-existing time will just confuse everyone. Convert your calendar object to OffsetDateTime:

    return calendar.toGregorianCalendar().toZonedDateTime().toOffsetDateTime();

Result: 2018-03-11T00:00-05:00. This point in time exists.:-)

Since your calendar comes from a foreign system, you will probably want to validate it since any field may be undefined and return DatatypeConstants.FIELD_UNDEFINED. When using LocalDate.of(), you may decide that its argument validation is enough since it will object to DatatypeConstants.FIELD_UNDEFINED being passed as an argument. toGregorianCalendar() on the other hand will tacitly use default values, so when using it I would consider validation indispensable.

What went wrong in your code?

I ran your code, and similarly to iolus (see the other answer) I got Sat Mar 10 23:00:00 CST 2018. This the correct point in time. As iolus also explained, this is Date.toString rendering the point in time this way. The Date object itself doesn’t have a time zone or UTC offset in it. So I should say that your code was correct. It was just you getting confused by the toString method. Many have been before you, and the good solution is to avoid the Date class completely. Also I would think that your observations have nothing to do with any difference between WAS 7 and WAS 8.5.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161