Java Period和Duration

时间:2016-12-13 14:08:47

标签: java time duration

我不确定我是否在Java PeriodDuration之间获得了微妙之处。

当我阅读甲骨文的explanation时,它说我可以找出自生日以来多少天(使用他们使用的示例日期):

LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1960, Month.JANUARY, 1);
Period birthdayPeriod = Period.between(birthday, today);
int daysOld = birthdayPeriod.getDays();

但正如他们所指出的那样,这并没有考虑到你出生的时区和你现在的时区。但这是一台电脑,我们可以准确,对吗?那么我会使用Duration吗?

ZoneId bornIn = ZoneId.of("America/New_York");
ZonedDateTime born = ZonedDateTime.of(1960, Month.JANUARY.getValue(), 1, 2, 34, 56, 0, bornIn);
ZonedDateTime now = ZonedDateTime.now();
Duration duration = Duration.between(born, now);
long daysPassed = duration.toDays();

现在实际时间是准确的,但如果我理解正确,那么日期可能无法正确表示日历日,例如与DST等。

那我该怎么做才能根据我的时区得到一个精确的答案?我唯一能想到的是回到使用LocalDate,但首先从ZonedDateTime值开始标准化时区,然后使用Duration

ZoneId bornIn = ZoneId.of("America/New_York");
ZonedDateTime born = ZonedDateTime.of(1960, Month.JANUARY.getValue(), 1, 2, 34, 56, 0, bornIn);
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime nowNormalized=now.withZoneSameInstant(born.getZone());
Period preciseBirthdayPeriod = Period.between(born.toLocalDate(), nowNormalized.toLocalDate());
int preciseDaysOld = preciseBirthdayPeriod.getDays();

但为了得到准确答案,这看起来真的很复杂。

4 个答案:

答案 0 :(得分:5)

您对Java-8课程PeriodDuration的分析或多或少是正确的。

  • 班级java.time.Period仅限于日历日期精确度。
  • java.time.Duration仅处理第二(和纳秒)精度但处理天数始终相当于24小时= 86400秒。

通常在计算人的年龄时忽略时钟精度或时区是完全足够的因为护照等个人文件没有记录某人出生时的确切时间。如果是这样,那么Period - 类就可以完成它的工作(但是请小心处理getDays()之类的方法 - 见下文)。

但是你需要更高的精确度并根据考虑时区的本地字段描述结果。嗯,第一部分(精度)由Duration支持,但不是第二部分一部分。

使用Period也没有帮助,因为确切的时差(Period忽略)可能会影响天数增量。此外(只打印代码的输出):

    Period preciseBirthdayPeriod =
        Period.between(born.toLocalDate(), nowNormalized.toLocalDate());
    int preciseDaysOld = preciseBirthdayPeriod.getDays();
    System.out.println(preciseDaysOld); // 13
    System.out.println(preciseBirthdayPeriod); // P56Y11M13D

正如您所看到的,使用方法preciseBirthdayPeriod.getDays()以获得以天为单位的总增量非常危险。不,它只是总delta的部分数量。还有11个月和56年。我认为不仅在几天内打印三角洲也是明智的,因为人们可以更容易地想象三角洲有多大(参见经常看到的打印持续时间的用例)社交媒体,如“3年,2个月,4天”)。

显然,您需要一种方法来确定持续时间,包括日历单位以及特殊时区的时钟单位(在您的示例中:某人出生的时区)。 Java-8-time-library的坏处是:它不支持PeriodDuration的任何组合。导入外部库Threeten-Extra-class Interval也无济于事,因为long daysPassed = interval.toDuration().toDays();仍会忽略时区效应(1天== 24小时),也无法在其他单位中打印delta像几个月等。

<强>要点:

您已尝试Period - 解决方案。 @swiedsw给出的答案尝试了基于Duration的解决方案。两种方法在精度方面都有缺点。您可以尝试在实现TemporalAmount的新类中组合这两个类,并自己实现必要的时间算法(不是那么简单)。

旁注:

我已经在我的时间库Time4J中实现了您所寻找的内容,因此它可能对您自己的实现有所帮助。例如:

Timezone bornZone = Timezone.of(AMERICA.NEW_YORK);
Moment bornTime =
    PlainTimestamp.of(1960, net.time4j.Month.JANUARY.getValue(), 1, 22, 34, 56).in(
        bornZone
    );
Moment currentTime = Moment.nowInSystemTime();
MomentInterval interval = MomentInterval.between(bornTime, currentTime);

MachineTime<TimeUnit> mt = interval.getSimpleDuration();
System.out.println(mt); // 1797324427.356000000s [POSIX]

net.time4j.Duration<?> duration =
    interval.getNominalDuration(
        bornZone, // relevant if the moments are crossing a DST-boundary
        CalendarUnit.YEARS,
        CalendarUnit.MONTHS,
        CalendarUnit.DAYS,
        ClockUnit.HOURS,
        ClockUnit.MINUTES
    );

// P56Y11M12DT12H52M (12 days if the birth-time-of-day is after current clock time)
// If only days were specified above then the output would be: P20801D
System.out.println(duration);
System.out.println(duration.getPartialAmount(CalendarUnit.DAYS)); // 12

这个例子也证明了我的一般态度,即使用像月,日,小时等单位并不是严格意义上的确切。唯一严格精确的方法(从科学的角度来看)将使用十进制秒内的机器时间(最好在SI秒内,也可能在1972年之后的Time4J中使用)。

答案 1 :(得分:3)

Period的JavaDoc声明它的模型:

  

ISO-8601日历系统中基于日期的时间量,例如“2年,3个月和4天”。

我知道它没有提及时间点。

您可能需要检查项目Interval中的ThreeTen-Extra模型:

  

两个时刻之间不可变的时间间隔。

项目网站称该项目“[...]由Java 8日期和时间库的主要作者Stephen Colebourne策划。”

您可以通过在Interval上调用toDuration()来检索Duration

我将转换你的代码给出一个例子:

ZoneId bornIn = ZoneId.of("America/New_York");
ZonedDateTime born = ZonedDateTime.of(1960, Month.JANUARY.getValue(), 1, 2, 34, 56, 0, bornIn);
ZonedDateTime now = ZonedDateTime.now();
Interval interval = Interval.of(born.toInstant(), now.toInstant());
long daysPassed = interval.toDuration().toDays();

答案 2 :(得分:0)

两个类之间的主要区别是:

  • java.time.Period使用基于日期的值(2018年5月31日)
  • 虽然java.time.Duration更精确,但它使用基于时间的值(“2018-05-31T11:45:20.223Z”)

答案 3 :(得分:0)

java.time.Period 更适合人类阅读

  • 例如 A 和 B 之间的时间段是 2 年 3 个月 3 天

java.time.Duration 用于机器。