简体   繁体   English

LocalDateTime.now()在Sony Bravia上崩溃了

[英]LocalDateTime.now() crashes on Sony Bravia

I am using the ThreeTen Android Backport in an app for AndroidTV. 我在AndroidTV的应用程序中使用ThreeTen Android Backport

While everything works perfectly on the Nexus Player and on all tested Amazon Fire TV devices, the call to LocalDateTime.now() consistently crashes the app on a Sony Bravia 4K 2015 (KD-55x8509C). 虽然一切都在Nexus播放器和所有经过测试的亚马逊Fire TV设备上完美运行,但对LocalDateTime.now()的调用一直在索尼Bravia 4K 2015(KD-55x8509C)上崩溃应用程序。

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)

What's going on and what can I do about it? 发生了什么事,我能做些什么呢?

The reason of your exception is quite clear and documented in your exception message: 您的异常的原因非常明确,并在您的异常消息中记录:

Broken zone id ("-01:00GMT-02:00,J086/02:00,J176/02:00"). 断区ID(“ - 01:00GMT-02:00,J086 / 02:00,J176 / 02:00”)。

It is also clear that Threeten-ABP (and Java-8, too) does not allow to construct an invalid zone id at all, see following example which tries a syntactically valid format: 很明显, 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

This is different than in old JDK-class java.util.TimeZone where you can set any arbitrary ID. 这与旧的JDK-class java.util.TimeZone ,您可以在其中设置任意ID。 So the question arises what to do with such a zone-id. 所以问题出现了如何处理这样的区域ID。 It is so horribly broken that you cannot even guess which real timezone identifier was meant. 它是如此可怕地破碎,你甚至无法猜出哪个真正的时区标识符。

The only reasonable thing is to use the underlying platform timezone which is still available by the expression TimeZone.getDefault() although its zone-id is unuseable. 唯一合理的事情是使用表达式TimeZone.getDefault()仍然可用的底层平台时区,尽管它的zone-id不可用。 Note that the broken zone id prevents you from using the tz data of Threeten-ABP or any other tz repository but the platform data. 请注意,损坏的区域ID会阻止您使用Threeten-ABP或任何其他tz存储库的tz数据,但会使用平台数据。

The best workaround / hack based on the platform timezone data is as follows (still using ThreetenABP only): 基于平台时区数据的最佳解决方法/ 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));
}

As I mentioned in my comment, I have taken this strange behaviour of some Android devices into account to stabilize my time library Time4A so I feel it is good to mention following cleaner and safer alternative starting with version v3.16-2016a: 正如我在评论中提到的,我已经将一些Android设备的这种奇怪行为考虑在内以稳定我的时间库Time4A所以我觉得从版本v3.16-2016a开始提到以下更清洁和更安全的替代方案是好的:

PlainTimestamp tsp = SystemClock.inLocalView().now();

It is cleaner because it does not depend on any ugly exception handling, not even internally. 它更干净,因为它不依赖于任何丑陋的异常处理,甚至不依赖于内部。 If the zone id of the underlying system timezone cannot be resolved then Time4A automatically switches to a wrapper around the system timezone instead of using the own tz repository. 如果无法解析基础系统时区的区域ID,则Time4A会自动切换到系统时区周围的包装器,而不是使用自己的tz存储库。 No user action required. 无需用户操作。

Note that Time4A has a uniform facade for timezones based on the own tz data as well as based on the Android platform tz data. 请注意,Time4A具有基于自己的tz数据以及基于Android平台tz数据的时区的统一外观。 You can even use both tz data in parallel ( Timezone.of("Europe/Berlin") uses the Time4A-data (up-to-date) while Timezone.of("java.util.TimeZone~Europe/Berlin") uses the platform data which might be old). 您甚至可以并行使用两个tz数据( Timezone.of("Europe/Berlin")使用Time4A数据(最新),而Timezone.of("java.util.TimeZone~Europe/Berlin")使用平台数据可能很旧)。 This feature is very useful to resolve local times of user-input as displayed on Android device. 此功能对于解析Android设备上显示的用户输入的本地时间非常有用。

It is also safer because exotic device timestamps are correctly validated in contrast to ThreetenABP, see also some other SO-posts like this and that . 它也更安全,因为与ThreetenABP相比,异常的设备时间戳被正确验证,另见其他一些SO帖子, 如此那样

A bridge from Time4A to ThreetenABP might look like: 从Time4A到ThreetenABP的桥梁可能如下所示:

LocalDateTime threeten = 
    LocalDateTime.of(
       tsp.getYear(), tsp.getMonth(), tsp.getDayOfMonth(), 
       tsp.getHour(), tsp.getMinute(), tsp.getSecond(), 
       tsp.getInt(PlainTime.NANO_OF_SECOND));

However, I don't recommend it because 但是,我不推荐它,因为

a) the dex limit can be reached soon (using two libraries simultaneously), a)可以很快达到dex限制(同时使用两个库),

b) Time4A has so many features and offers much better i18n-experience and a superior format and parse engine that it can completely replace ThreetenABP. b)Time4A具有如此多的功能,并提供更好的i18n体验和卓越的格式和解析引擎,它可以完全取代ThreetenABP。

The only problem of Time4A is just this: It is not well known. Time4A的唯一问题就是:它并不为人所知。

yeah, than damned Sony Bravia 4K 2015 是的,比该死的索尼Bravia 4K 2015

Not only LocalDate.now() can throw, in fact any method that depends on "ZoneId.systemDefault()". 不仅LocalDate.now()可以抛出,实际上任何依赖于“ZoneId.systemDefault()”的方法。 So wrapping in try-catch every time can lead to... unpleasant coding experience. 因此,每次包装try-catch都会导致...令人不快的编码体验。

LocalDate.now() implicitly calls ZoneId.systemDefault() . LocalDate.now()隐式调用ZoneId.systemDefault()

So, As workaround I'm constructing foolproof ZoneId and feeding it to LocalDate.now() and such. 因此,作为解决方法我正在构建万无一失的ZoneId并将其提供给LocalDate.now()等。

public final class Hack {

    private static @NonNull String fix_TimeZone_getDefault_getID(String offsetId) {
        /* todo */
        return fixed_offset_id;
    }

    public static @NonNull ZoneId ZoneId() {
        String mayBeWeirdZoneId = TimeZone.getDefault().getID();
        ZoneId id;
        try {
            id = ZoneId.of(mayBeWeirdZoneId, ZoneId.SHORT_IDS);
        } catch (DateTimeException ignore) {
            id = ZoneId.of(fix_TimeZone_getDefault_getID(mayBeWeirdZoneId));
        }
        return id;
    }
}

Use: 采用:

LocalDateTime.now(Hack.ZoneId() /* instead of ZoneId.systemDefault() */ );  

or 要么

ZoneId systemZone = Hack.ZoneId(); // my timezone, instead of ZoneId.systemDefault()

Obviously, you have to remember to use it everytime everywhere. 显然,你必须记住每次都到处使用它。 Damn, Sony. 该死的,索尼。
Kotlin's extension methods will come in handy here. Kotlin的扩展方法在这里会派上用场。 Something like ZoneId.systemDefaultAndNowSeriously() or LocalDate.nowLikeAGoodGirl() . ZoneId.systemDefaultAndNowSeriously()LocalDate.nowLikeAGoodGirl()
But still, damn. 但是,该死的。

PS: PS:
FWIW FWIW
Well, I believe Bravia have not completely wrong offsetId, so I just parsing it: 好吧,我相信Bravia没有完全错误的offsetId,所以我只是解析它:

private static @NonNull String fix_TimeZone_getDefault_getID(String offsetId) {
    if (offsetId == null) return ZoneOffset.UTC.getId();

    int gmt_pos = offsetId.indexOf("GMT");
    String off_fix = ZoneOffset.UTC.getId();
    if (gmt_pos > 0) {
        off_fix = offsetId.substring(0, gmt_pos);
    }
    return off_fix;
}

BUT, I'm not sure about last one, don't have device at hands. 但是,我不确定最后一个,手上没有设备。 Probably, will update receipt when will know more. 可能会在知道更多时更新收据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 索尼Bravia Android TV-如何启用/检索ADB Logcat日志 - Sony Bravia Android TV - How to enable/retrieve adb logcat logs 索尼 bravia tv 中 Surfaceview 高于另一个 Surfaceview 交换时的问题 - issue when Surfaceview Above Another Surfaceview swap in sony bravia tv Android TV发布的应用程序在sony bravia android tv中不可见 - Android TV published app not visible in sony bravia android tv 索尼 Bravia 电视 KD-55AF8:安装 HTML5 应用程序? - Sony Bravia TV KD-55AF8: install HTML5 app? Android应用程序在HTC和索尼爱立信手机上崩溃 - Android App Crashes on HTC and Sony Ericsson Phone 应用程序仅在Sony设备上首次启动时崩溃 - App crashes on first-time launch only in Sony devices 改为gridview应用程序现在崩溃? - Changed to gridview application now crashes? 应用程序代码曾经可以运行,但是现在崩溃了 - Application code used to work but now crashes Android Q RingtonePicker现在使应用程序崩溃? - Android Q RingtonePicker now crashes app? 哪个最适合作为 Android 中的时间戳 [仅使用新的 Java.time]? LocalDateTime 还是 Instant.now()? - Which is best as Timestamp in Android [only using new Java.time]? LocalDateTime or Instant.now()?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM