您的异常原因非常清楚,并记录在您的异常消息中:
损坏的区域 ID(“-01:00GMT-02:00,J086/02:00,J176/02:00”)。
同样清楚的是,Threeten-ABP(以及 Java-8)根本不允许构造无效的区域 id,请参阅以下示例,该示例尝试了语法上有效的格式:
String unsupported = "System/Unknown";
ZoneId zid = ZoneId.of(unsupported);
// org.threeten.bp.zone.ZoneRulesException: Unknown time-zone ID: System/Unknown
java.util.TimeZone
这与可以设置任意 ID的旧 JDK 类不同。所以问题出现了如何处理这样的 zone-id。它是如此糟糕,以至于您甚至无法猜测是哪个真正的时区标识符。
唯一合理的做法是使用表达式仍然可用的底层平台时区,TimeZone.getDefault()
尽管它的 zone-id 不可用。请注意,损坏的区域 id 会阻止您使用 Threeten-ABP 的 tz 数据或除平台数据之外的任何其他 tz 存储库。
基于平台时区数据的最佳解决方法/hack 如下(仍仅使用 ThreetenABP):
LocalDateTime ldt;
try {
ldt = LocalDateTime.now();
} catch (DateTimeException ex) {
long now = System.currentTimeMillis();
int offsetInMillis = TimeZone.getDefault().getOffset(now);
ldt =
LocalDateTime.ofEpochSecond(
now / 1000,
(int) (now % 1000) * 1_000_000,
ZoneOffset.ofTotalSeconds(offsetInMillis / 1000));
}
正如我在评论中提到的,我已经考虑到一些 Android 设备的这种奇怪行为来稳定我的时间库Time4A,所以我觉得从 v3.16-2016a 版本开始提到以下更清洁、更安全的替代方案是很好的:
PlainTimestamp tsp = SystemClock.inLocalView().now();
它更干净,因为它不依赖于任何丑陋的异常处理,甚至不依赖于内部。如果无法解析底层系统时区的区域 ID,则 Time4A 会自动切换到系统时区周围的包装器,而不是使用自己的 tz 存储库。无需用户操作。
请注意,Time4A 具有基于自己的 tz 数据以及基于 Android 平台 tz 数据的时区的统一外观。您甚至可以同时使用两个 tz 数据(Timezone.of("Europe/Berlin")
使用 Time4A 数据(最新的),同时Timezone.of("java.util.TimeZone~Europe/Berlin")
使用可能是旧的平台数据)。此功能对于解析 Android 设备上显示的用户输入的本地时间非常有用。
它也更安全,因为与 ThreetenABP 相比,外来设备时间戳得到了正确验证,另请参阅其他一些 SO-posts like this and that。
从 Time4A 到 ThreetenABP的桥可能如下所示:
LocalDateTime threeten =
LocalDateTime.of(
tsp.getYear(), tsp.getMonth(), tsp.getDayOfMonth(),
tsp.getHour(), tsp.getMinute(), tsp.getSecond(),
tsp.getInt(PlainTime.NANO_OF_SECOND));
但是,我不推荐它,因为
a) 可以很快达到 dex 限制(同时使用两个库),
b) Time4A 具有如此多的功能,并提供更好的 i18n 体验和卓越的格式和解析引擎,它可以完全取代 ThreetenABP。
Time4A 的唯一问题就是:它并不为人所知。