java.time
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
"EEE MMM dd uuuu HH:mm:ss 'GMT'xx (zzzz)", Locale.ENGLISH);
String str = "Sat Sep 14 2019 00:00:00 GMT+0530 (India Standard Time)";
TemporalAccessor parsed = formatter.parse(str);
ZonedDateTime zdt = ZonedDateTime.from(parsed);
// Check the offset against the time zone for validation
ZoneOffset offset = ZoneOffset.from(parsed);
if (offset.equals(zdt.getOffset())) {
System.out.println(zdt);
} else {
System.err.println("Offset doesn’t match");
}
Output from this snippet is:
2019-09-14T00:00+05:30[Asia/Kolkata]
Don’t use SimpleDateFormat
and Date
. Thise classes are poorly designed and long outdated. Instead I am using java.time, the modern Java date and time API. It is so much nicer to work with.
Validation is king. If one day you receive a string where the offset (like +0530
or -0400
) does not agree with the time zone (like India Standard Time
or Australian Central Daylight Time
), you will want to discover and take appropriate action. I am therefore taking the offset out from the parsed date-time and comparing it with the offset that Java derives from the time zone. In this case they did agree. If it wasn’t for the need for this validation, the normal way to parse a ZonedDateTime
would be the briefer:
ZonedDateTime zdt = ZonedDateTime.parse(str, formatter);
Specify locale. Sat
, Sep
and India Standard Time
are in English. If you don’t make sure that an English-speaking locale is used for parsing, parsing may well fail. If not today, then one day when you run your program on a computer with a different locale setting.
What went wrong in your code?
There are a couple of errors in your format pattern string. While the error message you got from SimpleDateFormat
is quite general, java.time is sometimes more helpful. Let’s see if it can help us here. Using your format pattern string:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E MMM dd hh:mm:ss Z yyyy", Locale.ENGLISH);
ZonedDateTime zdt = ZonedDateTime.parse(str, formatter);
This gets us a
java.time.format.DateTimeParseException: Text 'Sat Sep 14 2019 00:00:00 GMT+0530 (India Standard Time)' could not be parsed at index 13
Index 13 is already something. Index 13 in your string is where the 19
in 2019
is. The parse
method uses your format pattern for parsing the string and has successfully parsed Sat Sep 14 20
because it matched E MMM dd hh
. It knows that an hour can only be 2 digits, so parses only 20
as hour and then objects because the format pattern specifies a colon after the hour, and there isn’t any colon after 20
. You may have seen now that we should have specified a year ( uuuu
or yyyy
) after dd
in the format pattern. Let’s repair, moving the yyyy
in the string:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E MMM dd yyyy hh:mm:ss Z", Locale.ENGLISH);
java.time.format.DateTimeParseException: Text 'Sat Sep 14 2019 00:00:00 GMT+0530 (India Standard Time)' could not be parsed at index 25
We’re about double as far. Index 25 is where GMT+0530
is. So this doesn’t match format pattern letter Z
. The documentation says that Z
is for zone offset, for example +0000; -0800; -08:00. Your string had GMT
in front of the offset. We solve it by escaping it as literal text enclosing it in single quote:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E MMM dd yyyy hh:mm:ss 'GMT'Z", Locale.ENGLISH);
java.time.format.DateTimeParseException: Text 'Sat Sep 14 2019 00:00:00 GMT+0530 (India Standard Time)' could not be parsed, unparsed text found at index 33
Index 33 is the space after GMT+0530
. Your format pattern string ends here. There is a way to instruct the parsing to parse only as much of the string as it can, but that would deprive us of the validation of the time zone, so let’s not resort to that. Lowercase z
is for time zone name:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E MMM dd yyyy hh:mm:ss 'GMT'Z (zzzz)", Locale.ENGLISH);
java.time.format.DateTimeParseException: Text 'Sat Sep 14 2019 00:00:00 GMT+0530 (India Standard Time)' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {NanoOfSecond=0, MinuteOfHour=0, MilliOfSecond=0, MicroOfSecond=0, OffsetSeconds=19800, HourOfAmPm=0, SecondOfMinute=0},ISO,Asia/Kolkata resolved to 2019-09-14 of type java.time.format.Parsed
This is a new one. The detail to notice is HourOfAmPm=0
. We didn’t intend to put an hour within AM or PM in the string, just an hour of day. The error is lowercase hh
for hour. For hour of day from 00 through 23 we need uppercase HH
:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ss 'GMT'Z (zzzz)", Locale.ENGLISH);
Now parsing succeeds and gives the same result as above. What I wanted to say is that java.time is trying quite much harder to help us get the format pattern right.
Links