Java – android – SimpleDateFormat parses data in strange ways. Wrong month or/and year

android – SimpleDateFormat parses data in strange ways. Wrong month or/and year… here is a solution to the problem.

android – SimpleDateFormat parses data in strange ways. Wrong month or/and year

I have the following code fragment :

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 the date resolved 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 dates (wrong year or/and month or /and hour) for string values that have exactly the same format and differ from each other only in hours and minutes.

The question is: Why?

Solution

Your format pattern is correct. The locale is not relevant here.

Well, you also provided input in the question so that we can investigate if there are any non-printable characters. No (and JSON doesn’t produce such nonsense – very unlikely).

Therefore, the explanation for the observed unpredictable behavior is the lack of thread safety. Unfortunately, SimpleDateFormat is not thread-safe (and has many other drawbacks). So only one instance of SimpleDateFormat is stored, because static class fields are really dangerous.

How do I circumvent the limitations of SimpleDateFormat?

  • Synchronous calls to the parse() method (resulting in performance degradation).
  • Storage SimpleDateFormat – The object becomes ThreadLocal (better).
  • Use FastDateFormat (performance is comparable to ThreadLocale-solution, the prefix “Fast” is now a bit outdated).
  • Use ThreetenABP -library (backported around the new time library package java.time merged into Java-8 Android adaptation), providing an immutable parser, such as: OffsetDateTime.parse(input, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss. SSSZ"))
  • Use Joda-Time-Android (faster and immutable than ThreetenABP parsing), example: DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss. SSSZ").parseDateTime(input)
  • Or try my library Time4A (IMHO, the fastest solution, also immutable), for example: ChronoFormatter.ofMomentPattern("yyyy-MM-dd HH:mm:ss. SSSZ", PatternType.CLDR, Locale.ROOT, ZonalOffset.UTC).parse(input)

Choosing an immutable formatters/parser is undoubtedly the best and most modern way to get into a multithreaded environment. For Android, the libraries Apache Commons and ThreetenABP are more compact than the faster alternatives Joda-Time or Time4A. You’ll have to evaluate for yourself what is more important to you, whether it’s size or performance (or other features you might need).

Related Problems and Solutions