John Smith John Smith - 2 months ago 22
Android Question

android - SimpleDateFormat parses data the strange way. Wrong month or/and year

I have following code snippet:

final Date d = format.parse(value);
LOGGER.debug("Compare:\nOriginal: {}, Format: {}, Result: {}", value, format.toPattern(), d);
return d;


value
is a String value from json,

format
is a java.text.SimpleDateFormat,

d
is a Date which is parsed from
value





Sometimes it works fine, but sometimes it returns strange dates.

Example from logcat:


D/App: 20:14:47.309 com.example.backend.BackendHelper - Compare:
Original: 2016-09-16 13:45:00.000+0200, Format: yyyy-MM-dd HH:mm:ss.SSSZ, Result: Fri Jan 01 05:00:00 GMT+07:00 2016
D/App: 20:14:47.309 com.example.backend.BackendHelper - Compare:
Original: 2016-09-16 13:20:00.000+0200, Format: yyyy-MM-dd HH:mm:ss.SSSZ, Result: Fri Jan 01 18:20:00 GMT+07:00 2016
D/App: 20:14:47.338 com.example.backend.BackendHelper - Compare:
Original: 2016-09-16 15:20:00.000+0200, Format: yyyy-MM-dd HH:mm:ss.SSSZ, Result: Thu Jan 01 05:00:00 GMT+07:00 1970


As you can see, it returns incorrect Date (wrong year or/and month or/and hour) for the String values which have absolutely the same format and differ from each other only by hour and minutes.

The question is: Why?

Answer

Your format pattern is correct. And the locale is not relevant here.

Well, you have also supplied the input in your question so we could investigate if there were any non-printable characters. There aren't (and JSON does not produce such nonsense - very unlikely).

So what is left as explanation for the observed unpredictable behaviour is lack of thread-safety. SimpleDateFormat is not thread-safe, unfortunately (and has also many other disadvantages). So storing only one instance of SimpleDateFormat as static class field is indeed dangerous.

How to circumvent this limitation of SimpleDateFormat?

  • Synchronize the call to parse()-method (causing performance penalty)
  • Store the SimpleDateFormat-object into a ThreadLocal (better)
  • Use FastDateFormat (performance comparable to ThreadLocale-solution, the prefix "Fast" is nowadays a little bit outdated)
  • Use the ThreetenABP-library (an Android adaptation around a backport of the new time library package java.time incorporated in Java-8), offers an immutable parser), example: OffsetDateTime.parse(input, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSZ"))
  • Use Joda-Time-Android (quicker parsing than ThreetenABP, also immutable), example: DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSSZ").parseDateTime(input)
  • Or try out my library Time4A (IMHO the fastest solution, immutable, too), example: ChronoFormatter.ofMomentPattern("yyyy-MM-dd HH:mm:ss.SSSZ", PatternType.CLDR, Locale.ROOT, ZonalOffset.UTC).parse(input)

Choosing an immutable formatter/parser is certainly the best and most modern way to go in a multi-thread-environment. For Android, the libraries Apache Commons and ThreetenABP are more compact than the quicker alternatives Joda-Time or Time4A. You have to make your own evaluation what is more important for you, either size or Performance (or maybe other features you need).