PeriodFormatter在不同设备上的不同行为(JodaTime)

时间:2017-02-08 13:22:21

标签: android jodatime duration period periodformatter

我正在使用JodaTime为Android应用程序实现倒计时器。 根据设备的不同,输出也不同。

       DateTime openingDateTime = new DateTime(2018, DateTimeConstants.JUNE, 14, 21, 0, 0, DateTimeZone.forID("Europe/Moscow"));
        DateTime nowDateTime = DateTime.now(DateTimeZone.forID("Europe/Moscow"));
        long difference = openingDateTime.getMillis() - nowDateTime.getMillis();
(...)
onTick(difference);

(...)

    PeriodFormatter periodFormatter = new PeriodFormatterBuilder()
            .printZeroAlways()
            .appendDays().appendSuffix(" day", " days")
            .appendSeparator(" ")
            .appendHours()
            .appendSeparator(":")
            .appendMinutes()
            .appendSeparator(":")
            .appendSeconds()
            .toFormatter();
(...)    

        @Override
        public void onTick(long millisUntilFinished) {
            Duration duration = new Duration(millisUntilFinished);
            Period period = duration.toPeriod(PeriodType.dayTime());
            tvCounter.setText(periodFormatter.print(period));
        }

在一台设备上输出正确: 491天4:39:18 另一方面是错误的: 0天11788:49:11 。 我做错了什么?

1 个答案:

答案 0 :(得分:2)

感谢您的评论,我现在可以重现您的问题。只需将以下静态初始化程序添加到测试类(首先),以模拟您观察到预期输出的设备:

static {
    TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
}

根据规范(另请参阅accepted answer on this SO-post),转化duration.toPeriod(periodType)应仅使用所谓的精确持续时间字段,即小时,分钟,秒和毫秒,但不是天。

我对Joda-Time(v2.9.6)源代码的分析:

内部类org.joda.time.chrono.BasicChronology包含以下常量:

private static final DurationField cDaysField = new PreciseDurationField(DurationFieldType.days(), 86400000L);

所以我们在这里看到这个持续时间字段标记为"精确",但是:子类ZonedChronology包装它并覆盖方法isPrecise()的行为:

public boolean isPrecise() { 
  return iTimeField ? iField.isPrecise() : iField.isPrecise() && this.iZone.isFixed(); 
} 

这显示了days() - duration-field的precision属性的额外区域依赖性,即对于UTC等固定区域的精确属性,或者不精确。

我不知道分析和观察到的行为是特征还是错误。让我们说,期望Period - duration.toPeriod(...)对象的创建与区域无关是很危险的。如果系统区域是固定的,则not documented there具有精确的天组件。{{3}}

不幸的是,通过其年代设计将对默认时区的隐式依赖性深深地编码到Joda-Time中。 作为解决方法,您可以使用

Period p = new Period(nowDateTime, openingDateTime, PeriodType.dayTime());