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 更紧凑。您必须自己评估什么对您更重要,无论是尺寸还是性能(或者您可能需要的其他功能)。
我有以下代码片段:
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 更紧凑。您必须自己评估什么对您更重要,无论是尺寸还是性能(或者您可能需要的其他功能)。