我正在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文章,如thisthat
从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的唯一问题就是:它不为人所知。

08-18 00:45