PeriodFormatter 在不同设备上的不同行为 (JodaTime)
Different behavior of PeriodFormatter on different devices (JodaTime)
我正在使用 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。
我做错了什么?
感谢您的评论,我现在可以重现您的问题。只需将以下静态初始化程序添加到您的测试 class(首先)以模拟您观察预期输出的设备:
static {
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
}
根据规范(另见 accepted answer on this SO-post),转换 duration.toPeriod(periodType)
应该只使用 so-called 精确的持续时间字段,即小时、分钟、秒和毫秒,但不天。
我对Joda-Time(v2.9.6)源码的分析:
内部 class org.joda.time.chrono.BasicChronology 包含以下常量:
private static final DurationField cDaysField = new PreciseDurationField(DurationFieldType.days(), 86400000L);
所以我们看到这里这个持续时间字段被标记为 "precise",但是: subclass ZonedChronology 包装它并覆盖方法 isPrecise() 的行为:
public boolean isPrecise() {
return iTimeField ? iField.isPrecise() : iField.isPrecise() && this.iZone.isFixed();
}
这显示了天数 ()-duration-field 的精度 属性 的额外区域依赖性,即对于像 UTC 这样的固定区域是精确的,否则是不精确的。
我不知道分析和观察到的行为是特性还是错误。比方说,期望 duration.toPeriod(...)
创建 Period
个对象是 zone-independent 是危险的。如果系统区域是固定的,那么精确的days-component就是not documented there。
不幸的是,对默认时区的隐式依赖通过其 chronology-design 被深深地编码到 Joda-Time 中。 作为解决方法,您可以使用:
Period p = new Period(nowDateTime, openingDateTime, PeriodType.dayTime());
我正在使用 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。 我做错了什么?
感谢您的评论,我现在可以重现您的问题。只需将以下静态初始化程序添加到您的测试 class(首先)以模拟您观察预期输出的设备:
static {
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
}
根据规范(另见 accepted answer on this SO-post),转换 duration.toPeriod(periodType)
应该只使用 so-called 精确的持续时间字段,即小时、分钟、秒和毫秒,但不天。
我对Joda-Time(v2.9.6)源码的分析:
内部 class org.joda.time.chrono.BasicChronology 包含以下常量:
private static final DurationField cDaysField = new PreciseDurationField(DurationFieldType.days(), 86400000L);
所以我们看到这里这个持续时间字段被标记为 "precise",但是: subclass ZonedChronology 包装它并覆盖方法 isPrecise() 的行为:
public boolean isPrecise() {
return iTimeField ? iField.isPrecise() : iField.isPrecise() && this.iZone.isFixed();
}
这显示了天数 ()-duration-field 的精度 属性 的额外区域依赖性,即对于像 UTC 这样的固定区域是精确的,否则是不精确的。
我不知道分析和观察到的行为是特性还是错误。比方说,期望 duration.toPeriod(...)
创建 Period
个对象是 zone-independent 是危险的。如果系统区域是固定的,那么精确的days-component就是not documented there。
不幸的是,对默认时区的隐式依赖通过其 chronology-design 被深深地编码到 Joda-Time 中。 作为解决方法,您可以使用:
Period p = new Period(nowDateTime, openingDateTime, PeriodType.dayTime());