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 becomesThreadLocal
(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).