我正在androidtv的应用程序中使用ThreeTen Android Backport。
尽管Nexus播放器和所有经过测试的Amazon Fire TV设备上的一切都运行得很好,但在Sony Bravia 4K 2015(KD-55x8509C)上,对LocalDateTime.now()
的调用始终会导致应用程序崩溃。
Caused by: org.threeten.bp.DateTimeException: Invalid ID for ZoneOffset, invalid format: -01:00GMT-02:00,J086/02:00,J176/02:00
at org.threeten.bp.ZoneOffset.of(ZoneOffset.java:221)
at org.threeten.bp.ZoneId.of(ZoneId.java:344)
at org.threeten.bp.ZoneId.of(ZoneId.java:285)
at org.threeten.bp.ZoneId.systemDefault(ZoneId.java:244)
at org.threeten.bp.Clock.systemDefaultZone(Clock.java:137)
at org.threeten.bp.LocalDateTime.now(LocalDateTime.java:152)
怎么回事,我能怎么办?
最佳答案
异常的原因非常清楚,并记录在异常消息中:
破碎带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
这与在旧的jdk类中可以设置任意id的情况不同。因此,问题是如何处理这样的zone-id。它非常糟糕,以至于您甚至无法猜出哪个是真正的时区标识符。
唯一合理的方法是使用底层平台时区,尽管其区域id不可用,但表达式
java.util.TimeZone
仍然可以使用该时区。请注意,断开的区域id阻止您使用3ten abp的tz数据或除了平台数据之外的任何其他tz存储库。基于平台时区数据的最佳解决方案/黑客攻击如下(仍仅使用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.getDefault()
使用Time4a数据(最新),而Timezone.of("Europe/Berlin")
使用可能是旧的平台数据)。此功能对于解决android设备上显示的用户输入的本地时间非常有用。它也更安全,因为奇异的设备时间戳与threetenabp相比是正确验证的,请参见其他一些so文章,如this和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具有如此many features的特性,它提供了更好的i18n体验和更优越的格式和解析引擎,可以完全取代Threetenabp。
时间4a的唯一问题就是:它不为人所知。