我正在使用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 。 我做错了什么?
答案 0 :(得分:2)
感谢您的评论,我现在可以重现您的问题。只需将以下静态初始化程序添加到测试类(首先),以模拟您观察到预期输出的设备:
static {
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
}
根据规范(另请参阅accepted answer on this SO-post),转化duration.toPeriod(periodType)
应仅使用所谓的精确持续时间字段,即小时,分钟,秒和毫秒,但不是天。
内部类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());