2

I am getting an OffsetDateTime from our backend in a String format like this:

"2017-07-15T10:52:59Z"

I am trying to parse this String to a Android Date:

private SimpleDateFormat mSimpleDateFormat;
private static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";
mSimpleDateFormat = new SimpleDateFormat(DATE_FORMAT_PATTERN, Locale.getDefault());

Date newDate = null;
String dateString = notice.getCreated();

try {
    newDate = mSimpleDateFormat.parse(dateString);
} catch (ParseException e) {
    LogHelper.showExceptionLog(MyClass.class, e);
}

It always throws:

Unparseable date: "2017-07-15T10:52:59Z"

Vancore
  • 657
  • 1
  • 9
  • 18

1 Answers1

3

To parse the Z (which is the UTC designator) you must use the X pattern (as explained in javadoc):

SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");

String dateString = "2017-07-15T10:52:59Z";
Date newDate = mSimpleDateFormat.parse(dateString);

If you use just yyyy-MM-dd'T'HH:mm:ss as a pattern, SimpleDateFormat will use the system's default timezone and ignore the Z, giving incorrect results: it'll parse the date/time as 10:52 in the default timezone, which can be different to 10:52 in UTC. By using the X pattern, you get the correct result.

I also removed the Locale because this formatter is not dealing with any locale-sensitive information (like month and day of week names), so it doesn't affect the parsing in this case (and SimpleDateFormat already uses the default locale if you don't specify one).

PS: the X pattern was introduced in JDK 7. If you're using and older version, it won't be available. In this case, you can set the UTC as a timezone of the formatter:

SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
mSimpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

Obviously this code is assuming that the input is always in UTC (with Z in the end).


Java new Date/Time API

The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.

For Android, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need ThreeTenABP (more on how to use it here).

As the input string is in ISO 8601 format, you can easily parse it to a org.threeten.bp.OffsetDateTime:

String dateString = "2017-07-15T10:52:59Z";
OffsetDateTime odt = OffsetDateTime.parse(dateString);

You can then convert this to a java.util.Date easily, using the org.threeten.bp.DateTimeUtils class:

Date date = DateTimeUtils.toDate(odt.toInstant());

If the input is always in UTC (always with the Z in the end), you can also use a org.threeten.bp.Instant:

String dateString = "2017-07-15T10:52:59Z";
Instant instant = Instant.parse(dateString);
Date date = DateTimeUtils.toDate(instant);

The only difference is that Instant only parses UTC inputs (ending with Z) and OffsetDateTime accepts any valid UTC offset (like -03:00 or +05:30).