Java期间和持续时间之间的微妙之处
我不确定我是否理解了JavaJava期间和持续时间之间的微妙之处,java,time,duration,Java,Time,Duration,我不确定我是否理解了JavaPeriod和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=n
Period
和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();
当我读到甲骨文的时候,它说我可以知道像这样的生日已经过了多少天(使用他们使用的示例日期):
但正如他们所指出的,这并没有考虑到你出生的时区和你现在所在的时区。但这是一台计算机,我们可以精确,对吗?那么我会使用持续时间吗
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
值规范化时区,然后使用持续时间
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();
但是,要得到一个精确的答案,这似乎真的很复杂。JavaDoc的状态是,它建模:
ISO-8601日历系统中基于日期的时间量,例如“2年、3个月和4天”
我知道它没有提到时间点
您可能希望从project中检查以下模型:
两个瞬间之间不可变的时间间隔
该项目网站声明该项目“[…]由Java 8日期和时间库的主要作者Stephen Colebourne策划”
可以通过调用间隔上的toDuration()
从间隔中检索
我将转换您的代码以给出一个示例:
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();
您对Java-8类期间
和持续时间
的分析或多或少是正确的
- 类
java.time.Period
仅限于日历日期精度
- 类
java.time.Duration
仅处理秒(和纳秒)精度,但将天始终视为等同于24小时=86400秒
通常,在计算一个人的年龄时,忽略时钟精度或时区就足够了,因为像护照这样的个人文件不能记录一个人出生的确切时间。如果是这样的话,Period
-类将完成它的工作(但请小心处理它的方法,如getDays()
,请参见下文)
但您需要更高的精度,并根据本地字段描述结果,同时考虑时区。好的,第一部分(精度)由持续时间支持,但第二部分不支持
使用Period
也没有帮助,因为确切的时差(被Period
忽略)会影响以天为单位的增量。此外(只需打印代码的输出):
如您所见,使用方法preciseBirthdayPeriod.getDays()
以天为单位获取总增量是非常危险的。不,它只是总增量的一部分。还有11个月和56年。我认为不仅在几天内打印增量是明智的,因为这样人们可以更容易地想象增量有多大(参见社交媒体中常见的打印持续时间用例,如“3年、2个月和4天”)
显然,您需要一种方法来确定持续时间,包括特定时区(在您的示例中:某人出生的时区)中的日历单位和时钟单位。Java-8-time-library的缺点是:它不支持Period
和Duration
的任何组合。导入外部库Threen额外类Interval
也不会有帮助,因为long daysPassed=Interval.toDuration().toDays()代码>仍将忽略时区影响(1天==24小时),并且也无法以其他单位(如月份等)打印增量
摘要:
您已经尝试了时段
-解决方案。@swiedsw给出的答案尝试了基于持续时间的解决方案。这两种方法在精度方面都有缺点。您可以尝试将这两个类合并到一个新的类中,该类实现TemporalAmount
,并自己实现必要的时间算法(不是那么简单)
旁注:
我自己已经在我的时间库中实现了您所寻找的内容,因此它可能对您自己的实现很有用。例如:
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
Timezone bornZone=Timezone.of(AMERICA.NEW_YORK);
临终时刻=
PlainTimestamp.of(1960,net.time4j.Month.janur.getValue(),1,22,34,56)(
冰片
);
矩currentTime=矩.nowInSystemTime();
MomentInterval interval=MomentInterval.between(bornTime,currentTime);
MachineTime mt=interval.getSimpleDuration();
系统输出打印项次(mt);//1797324427.356000000s[POSIX]
net.time4j.Duration持续时间=
interval.getNominalDuration(
bornZone,//如果力矩穿过DST边界,则相关
日历单位。年,
日历单位。个月,
日历单位:天,
时钟单位。小时,
时钟单位。分钟
);
//P56Y11M12DT12H52M(如果一天的出生时间在当前时钟时间之后,则为12天)
//如果上面只指定了天数,则输出为:P20801D
系统输出打印项次(持续时间);
System.out.println(duration.getPartialAmount(CalendarUnit.DAYS));//12
这个例子还表明了我的一般态度,即使用月、日、小时等单位在严格意义上并不准确。唯一严格精确的方法(从科学角度来看)是使用十进制秒的机器时间(最好是SI秒,也可能是1972年后的Time4J)。这两类之间的主要区别是:
java.time.Period
使用基于日期的值(2018年5月31日)
- 而
java.time.Duration
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