java.time:比较两个Instants——获取两者之间的时、分、秒、年、月数
java.time: Compare two Instants - get the number of hours, minutes, seconds, years, months between the two
我试过这段代码:
public class TimePassed {
private long seconds;
private long minutes;
private long hours;
private long days;
private long years;
...
public TimePassed(double unixSeconds) {
Instant now = Instant.now();
Instant ago = Instant.ofEpochSecond((long) unixSeconds);
this.seconds = ChronoUnit.SECONDS.between(
ago.atZone(ZoneId.systemDefault()),
now.atZone(ZoneId.systemDefault())); //6100
this.minutes = ChronoUnit.MINUTES.between(
ago.atZone(ZoneId.systemDefault()),
now.atZone(ZoneId.systemDefault())); //101
this.hours = ChronoUnit.HOURS.between(
ago.atZone(ZoneId.systemDefault()),
now.atZone(ZoneId.systemDefault())); //1
this.days = ChronoUnit.DAYS.between(
ago.atZone(ZoneId.systemDefault()),
now.atZone(ZoneId.systemDefault())); //0
this.years = ChronoUnit.YEARS.between(
ago.atZone(ZoneId.systemDefault()),
now.atZone(ZoneId.systemDefault())); //0
}
}
然而 TimePassed
对象将有 seconds = 6100
和 minutes = 101
和 hours = 1
,而我希望它是 hours = 1
,minutes = 41, seconds = 40
,所以60*60 + 41*60 + 40 = 6100
。是否可以使用 java.time
包?因为截至目前,我只能通过秒数、通过分钟数或通过小时数等。并且两者都不会占另一个。
Java 9 个回答:Duration.toXxxPart 方法
基本思路,未完成:
Duration dur = Duration.between(ago, now);
this.seconds = dur.toSecondsPart(); // 40
this.minutes = dur.toMinutesPart(); // 41
this.hours = dur.toHoursPart(); // 1
this.days = dur.toDaysPart(); // 0
使用与问题相距 6100 秒的瞬间进行测试。 toXxxPart
方法是在 Java 9 中引入的。对于 Java 8(或 ThreeTen Backport),您需要从较粗略的单位、天开始,然后从持续时间中减去它们,然后才能得到下一个更好的单位。有关示例,请参阅 this answer by lauhub
虽然年和日要完全正确有点棘手。要只获取超过整年的天数,这里是完整代码:
ZoneId zone = ZoneId.systemDefault();
ZonedDateTime agoZdt = ago.atZone(zone);
ZonedDateTime nowZdt = now.atZone(zone);
this.years = ChronoUnit.YEARS.between(agoZdt, nowZdt);
ZonedDateTime afterWholeYears = agoZdt.plusYears(this.years);
Duration dur = Duration.between(afterWholeYears, nowZdt);
this.seconds = dur.toSecondsPart(); // 40
this.minutes = dur.toMinutesPart(); // 41
this.hours = dur.toHoursPart(); // 1
this.days = dur.toDays(); // 0
我故意只阅读 ZoneId.systemDefault()
一次,以防万一有人更改正在进行的默认时区设置。
像这样的东西应该可以工作:
ZoneId zone = ZoneId.systemDefault();
ZonedDateTime ago = ZonedDateTime.ofInstant(Instant.ofEpochSeconds(unixSeconds), zone);
ZonedDateTime now = ZonedDateTime.now(zone);
Period period = Period.between(ago.toLocalDate(), now.toLocalDate());
ZonedDateTime adjusted = ago.with(now.toLocalDate());
if (adjusted.isAfter(now)) {
adjusted = adjusted.minusDays(1);
period = period.minusDays(1);
}
Duration duration = Duration.between(adjusted, now);
assert duration.toDaysPart() == 0;
years = period.getYears();
months = period.getMonths();
days = period.getDays();
hours = duration.toHoursPart();
minutes = duration.toMinutesPart();
seconds = duration.toSecondsPart();
Why/how 这个有效:
- 非时间字段(年、月、日)的差异是通过使用专门的
Period
类型计算的,该类型正是为此目的而设计的。它使用两个日期时间的 LocalDate
部分,这是安全的,因为它们都在同一时区
- 为了了解其余字段的差异,我们调整“
ago
”值,使天数完全相同。如果我们碰巧超过了(如果 "ago" 发生在比 "now" 更早的本地时间,则可能会发生这种情况),我们通过将调整后的日期时间和周期都减少一天来进行调整
- 然后,我们使用
Duration
class 来获取基于时间的字段之间的差异。由于我们在那里查询调整后的日期时间之间的差异,因此我们的持续时间不会超过一天,我为此添加了断言。
- 最后,我使用了
Period
和 Duration
上可用的各种方法来获取它们的 "fields"。请注意,我使用的 Duration
class 方法仅在 Java 9 之后可用,因此如果您还没有,则必须使用 toMinutes()
等方法并手动将它们除以每小时的分钟数等:
// those constants you'll have to define on your own, shouldn't be hard
hours = duration.toHours() % Constants.HOURS_PER_DAY;
或者,如果您不想定义常量,可以通过调整 "ago" 变量来重复此技巧:
adjusted = ago.with(now.toLocalDate());
if (adjusted.isAfter(now)) {
adjusted = adjusted.minusDays(1);
period = period.minusDays(1);
}
hours = HOURS.between(adjusted, now);
adjusted = adjusted.withHour(now.getHour());
if (adjusted.isAfter(now)) {
adjusted = adjusted.minusHour(1);
hours -= 1;
}
minutes = MINUTES.between(adjusted, now);
adjusted = adjusted.withMinute(now.getMinute());
if (adjusted.isAfter(now)) {
adjusted = adjusted.minusMinutes(1);
minutes -= 1;
}
seconds = SECONDS.between(adjusted, now);
我试过这段代码:
public class TimePassed {
private long seconds;
private long minutes;
private long hours;
private long days;
private long years;
...
public TimePassed(double unixSeconds) {
Instant now = Instant.now();
Instant ago = Instant.ofEpochSecond((long) unixSeconds);
this.seconds = ChronoUnit.SECONDS.between(
ago.atZone(ZoneId.systemDefault()),
now.atZone(ZoneId.systemDefault())); //6100
this.minutes = ChronoUnit.MINUTES.between(
ago.atZone(ZoneId.systemDefault()),
now.atZone(ZoneId.systemDefault())); //101
this.hours = ChronoUnit.HOURS.between(
ago.atZone(ZoneId.systemDefault()),
now.atZone(ZoneId.systemDefault())); //1
this.days = ChronoUnit.DAYS.between(
ago.atZone(ZoneId.systemDefault()),
now.atZone(ZoneId.systemDefault())); //0
this.years = ChronoUnit.YEARS.between(
ago.atZone(ZoneId.systemDefault()),
now.atZone(ZoneId.systemDefault())); //0
}
}
然而 TimePassed
对象将有 seconds = 6100
和 minutes = 101
和 hours = 1
,而我希望它是 hours = 1
,minutes = 41, seconds = 40
,所以60*60 + 41*60 + 40 = 6100
。是否可以使用 java.time
包?因为截至目前,我只能通过秒数、通过分钟数或通过小时数等。并且两者都不会占另一个。
Java 9 个回答:Duration.toXxxPart 方法
基本思路,未完成:
Duration dur = Duration.between(ago, now);
this.seconds = dur.toSecondsPart(); // 40
this.minutes = dur.toMinutesPart(); // 41
this.hours = dur.toHoursPart(); // 1
this.days = dur.toDaysPart(); // 0
使用与问题相距 6100 秒的瞬间进行测试。 toXxxPart
方法是在 Java 9 中引入的。对于 Java 8(或 ThreeTen Backport),您需要从较粗略的单位、天开始,然后从持续时间中减去它们,然后才能得到下一个更好的单位。有关示例,请参阅 this answer by lauhub
虽然年和日要完全正确有点棘手。要只获取超过整年的天数,这里是完整代码:
ZoneId zone = ZoneId.systemDefault();
ZonedDateTime agoZdt = ago.atZone(zone);
ZonedDateTime nowZdt = now.atZone(zone);
this.years = ChronoUnit.YEARS.between(agoZdt, nowZdt);
ZonedDateTime afterWholeYears = agoZdt.plusYears(this.years);
Duration dur = Duration.between(afterWholeYears, nowZdt);
this.seconds = dur.toSecondsPart(); // 40
this.minutes = dur.toMinutesPart(); // 41
this.hours = dur.toHoursPart(); // 1
this.days = dur.toDays(); // 0
我故意只阅读 ZoneId.systemDefault()
一次,以防万一有人更改正在进行的默认时区设置。
像这样的东西应该可以工作:
ZoneId zone = ZoneId.systemDefault();
ZonedDateTime ago = ZonedDateTime.ofInstant(Instant.ofEpochSeconds(unixSeconds), zone);
ZonedDateTime now = ZonedDateTime.now(zone);
Period period = Period.between(ago.toLocalDate(), now.toLocalDate());
ZonedDateTime adjusted = ago.with(now.toLocalDate());
if (adjusted.isAfter(now)) {
adjusted = adjusted.minusDays(1);
period = period.minusDays(1);
}
Duration duration = Duration.between(adjusted, now);
assert duration.toDaysPart() == 0;
years = period.getYears();
months = period.getMonths();
days = period.getDays();
hours = duration.toHoursPart();
minutes = duration.toMinutesPart();
seconds = duration.toSecondsPart();
Why/how 这个有效:
- 非时间字段(年、月、日)的差异是通过使用专门的
Period
类型计算的,该类型正是为此目的而设计的。它使用两个日期时间的LocalDate
部分,这是安全的,因为它们都在同一时区 - 为了了解其余字段的差异,我们调整“
ago
”值,使天数完全相同。如果我们碰巧超过了(如果 "ago" 发生在比 "now" 更早的本地时间,则可能会发生这种情况),我们通过将调整后的日期时间和周期都减少一天来进行调整 - 然后,我们使用
Duration
class 来获取基于时间的字段之间的差异。由于我们在那里查询调整后的日期时间之间的差异,因此我们的持续时间不会超过一天,我为此添加了断言。 - 最后,我使用了
Period
和Duration
上可用的各种方法来获取它们的 "fields"。请注意,我使用的Duration
class 方法仅在 Java 9 之后可用,因此如果您还没有,则必须使用toMinutes()
等方法并手动将它们除以每小时的分钟数等:
// those constants you'll have to define on your own, shouldn't be hard
hours = duration.toHours() % Constants.HOURS_PER_DAY;
或者,如果您不想定义常量,可以通过调整 "ago" 变量来重复此技巧:
adjusted = ago.with(now.toLocalDate());
if (adjusted.isAfter(now)) {
adjusted = adjusted.minusDays(1);
period = period.minusDays(1);
}
hours = HOURS.between(adjusted, now);
adjusted = adjusted.withHour(now.getHour());
if (adjusted.isAfter(now)) {
adjusted = adjusted.minusHour(1);
hours -= 1;
}
minutes = MINUTES.between(adjusted, now);
adjusted = adjusted.withMinute(now.getMinute());
if (adjusted.isAfter(now)) {
adjusted = adjusted.minusMinutes(1);
minutes -= 1;
}
seconds = SECONDS.between(adjusted, now);