android - SimpleDateFormat 以奇怪的方式解析数据。 or/and 月错了

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

我有以下代码片段:

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

value 是来自 json、

的字符串值

format 是一个 java.text.SimpleDateFormat,

d 是从 value

解析的日期

有时它工作正常,但有时它 returns 奇怪的日期。

示例来自 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

如您所见,它 returns 错误的日期(错误的年份 or/and 月份 or/and 小时)对于具有完全相同格式且仅彼此不同的字符串值按小时和分钟。

问题是:为什么

您的格式正确。语言环境与这里无关。

好吧,您还提供了问题中的输入,以便我们调查是否有任何不可打印的字符。没有(并且 JSON 不会产生这样的废话 - 非常不可能)。

因此,对于观察到的不可预测行为的解释是缺乏线程安全。 SimpleDateFormat 不是线程安全的,不幸的是(并且还有很多其他缺点)。因此,仅将 SimpleDateFormat 的一个实例存储为静态 class 字段确实很危险。

如何绕过SimpleDateFormat的这个限制?

  • 同步对 parse() 方法的调用(导致性能损失)
  • SimpleDateFormat 对象存储到 ThreadLocal(更好)
  • 使用FastDateFormat(性能堪比ThreadLocale-solution,前缀"Fast"现在有点过时了)
  • 使用 ThreetenABP-library(Android 围绕 Java-8 中合并的新时间库包 java.time 的向后移植改编),提供不可变的解析器),例如:OffsetDateTime.parse(input, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSZ"))
  • 使用Joda-Time-Android(比ThreetenABP解析更快,也是不可变的),例子:DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSSZ").parseDateTime(input)
  • 或者试试我的库 Time4A(恕我直言,最快的解决方案,也是不可变的),示例:ChronoFormatter.ofMomentPattern("yyyy-MM-dd HH:mm:ss.SSSZ", PatternType.CLDR, Locale.ROOT, ZonalOffset.UTC).parse(input)

选择不可变的formatter/parser无疑是多线程环境中最好和最现代的方式。对于 Android,Apache Commons 和 ThreetenABP 库比更快的替代品 Joda-Time 或 Time4A 更紧凑。您必须自己评估什么对您更重要,无论是尺寸还是性能(或者您可能需要的其他功能)。