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.