Reputation: 503
I have a string 2017-07-31T01:01:00-07:00
and I am trying to parse it to date and in CST Timezone. I am getting different results when i parse this string using Date and Java 8 ZonedDateTime. I am not getting why this is happening and what I am doing wrong.
String dateStr = "2017-07-31T01:01:00-07:00";
LocalDateTime time = null;
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss-hh");
String[] dateArray = dateStr.split("-");
String[] timeZones = TimeZone
.getAvailableIDs(TimeZone.getTimeZone("GMT-" + dateArray[dateArray.length - 1]).getRawOffset());
format.setTimeZone(TimeZone.getTimeZone(timeZones[0]));
Date dateObj = null;
try {
dateObj = format.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
time = dateObj.toInstant().atZone(TimeZone.getTimeZone("CST").toZoneId()).toLocalDateTime();
ZonedDateTime time2 = ZonedDateTime.parse(dateStr).toInstant().atZone(TimeZone.getTimeZone("CST").toZoneId());
System.out.println(time);
System.out.println(time2.toLocalDateTime());
Upvotes: 3
Views: 2086
Reputation: 86379
I am not sure about the exact behaviour of TimeZone.getAvailableIDs
(it’s not perfectly well documented). When I run your code on my Java 9.0.4, the first ID it returns is America/Boise. Boise, Idaho, USA, is on Mountain Standard Time in winter (UTC-07:00) and on Mountain Daylight Time now (UTC-06:00). You set your format
to use this for parsing. Your format parses 01
as hour of day (uppercase HH
, 00 through 23) and 07
as hour within AM or PM (lowercase hh
, 01 through 12). So there’s a conflict here. Apparently the former wins, I don’t know why (don’t always insist on understanding SimpleDateFormat
). Since July 31 is in the summer time (DST) time of year, you get a time of 01:01-06:00, equal to 02:01 North American Central Daylight Time (-05:00). Which is incorrect.
ZonedDateTime.parse
parses your string correctly. TimeZone.getTimeZone("CST").toZoneId()
interprets CST as America/Chicago (which strictly speaking is incorrect since Chicago uses CST only for the minor part of the year). So in time2
you get 2017-07-31T03:01-05:00[America/Chicago]
, which is correct.
For what I think you were trying to obtain I recommend:
ZonedDateTime dateTime = OffsetDateTime.parse(dateStr)
.atZoneSameInstant(ZoneId.of("America/Chicago"));
System.out.println(dateTime.toLocalDateTime());
Output is the same as your second output:
2017-07-31T03:01
If you find it more appropriate for your situation, instead of America/Chicago you may consider for example America/Bahia_Banderas, America/Indiana/Knox, America/Indiana/Tell_City, America/Matamoros, America/Menominee or America/Winnipeg. Don’t rely on a three or four letter time zone abbreviation. CST is not a true time zone since it is only used for some of the year, and it is ambiguous, it may refer to Australian Central Standard Time, North and Central American Central Standard Time, China Standard Time or Cuba Standard Time. And there’s no guarantee which one Java gives you.
Upvotes: 1
Reputation: 159205
You should not parse the timezone offset yourself. Just use the X
pattern:
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX")
You should not use timezone CST
, since that is ambiguous (Central Standard Time, China Standard Time, Cuba Standard Time). Use America/Chicago
(I assume that's what you meant).
So, to parse the date string with old and new APIs:
String dateStr = "2017-07-31T01:01:00-07:00";
// Using Date
SimpleDateFormat parseFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
Date date = parseFormat.parse(dateStr);
SimpleDateFormat printFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm");
printFormat.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
System.out.println(printFormat.format(date));
// Using ZonedDateTime
ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateStr);
zonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("America/Chicago"));
System.out.println(zonedDateTime.toLocalDateTime());
Output
2017-07-31T03:01
2017-07-31T03:01
If you want to see the time zone, you can do this:
String dateStr = "2017-07-31T01:01:00-07:00";
// Using Date
SimpleDateFormat parseFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
Date date = parseFormat.parse(dateStr);
SimpleDateFormat printFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm XXX z");
printFormat.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
System.out.println(printFormat.format(date));
// Using ZonedDateTime
ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateStr);
DateTimeFormatter printFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm XXX z")
.withZone(ZoneId.of("America/Chicago"));
System.out.println(zonedDateTime.format(printFormatter));
Output
2017-07-31T03:01 -05:00 CDT
2017-07-31T03:01 -05:00 CDT
Note how the first example changes the ZonedDateTime
timezone, converts it to a LocalDateTime
, then prints that without a formatter, while in the second example the DateTimeFormatter
is setup to format the value in a specific timezone, similar to how the SimpleDateFormat
is doing it. Just different ways of accomplishing the same result.
Upvotes: 5