Java 8:多个单位中两个 LocalDateTime 之间的差异
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25747499/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java 8: Difference between two LocalDateTime in multiple units
提问by Tapas Bose
I am trying to calculate the difference between two LocalDateTime
.
我正在尝试计算两个LocalDateTime
.
The output needs to be of the format y years m months d days h hours m minutes s seconds
. Here is what I have written:
输出需要是格式y years m months d days h hours m minutes s seconds
。这是我写的:
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
public class Main {
static final int MINUTES_PER_HOUR = 60;
static final int SECONDS_PER_MINUTE = 60;
static final int SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
public static void main(String[] args) {
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 9, 19, 46, 45);
LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);
Period period = getPeriod(fromDateTime, toDateTime);
long time[] = getTime(fromDateTime, toDateTime);
System.out.println(period.getYears() + " years " +
period.getMonths() + " months " +
period.getDays() + " days " +
time[0] + " hours " +
time[1] + " minutes " +
time[2] + " seconds.");
}
private static Period getPeriod(LocalDateTime dob, LocalDateTime now) {
return Period.between(dob.toLocalDate(), now.toLocalDate());
}
private static long[] getTime(LocalDateTime dob, LocalDateTime now) {
LocalDateTime today = LocalDateTime.of(now.getYear(),
now.getMonthValue(), now.getDayOfMonth(), dob.getHour(), dob.getMinute(), dob.getSecond());
Duration duration = Duration.between(today, now);
long seconds = duration.getSeconds();
long hours = seconds / SECONDS_PER_HOUR;
long minutes = ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
long secs = (seconds % SECONDS_PER_MINUTE);
return new long[]{hours, minutes, secs};
}
}
The output that I am getting is 29 years 8 months 24 days 12 hours 0 minutes 50 seconds
. I have checked my result from this website(with values 12/16/1984 07:45:55
and 09/09/2014 19:46:45
). The following screenshot shows the output:
我得到的输出是29 years 8 months 24 days 12 hours 0 minutes 50 seconds
. 我已经从这个网站检查了我的结果(带有值12/16/1984 07:45:55
和09/09/2014 19:46:45
)。以下屏幕截图显示了输出:
I am pretty sure that the fields after the month value is coming wrong from my code. Any suggestion would be very helpful.
我很确定我的代码中月份值之后的字段是错误的。任何建议都会非常有帮助。
Update
更新
I have tested my result from another website and the result I got is different. Here it is: Calculate duration between two dates(result: 29 years, 8 months, 24 days, 12 hours, 0 minutes and 50 seconds).
我从另一个网站测试了我的结果,我得到的结果是不同的。这是:计算两个日期之间的持续时间(结果:29 年 8 个月、24 天、12 小时、0 分 50 秒)。
Update
更新
Since I got two different results from two different sites, I am wondering if the algorithm of my calculation is legitimate or not. If I use following two LocalDateTime
objects:
由于我从两个不同的站点得到两个不同的结果,我想知道我的计算算法是否合法。如果我使用以下两个LocalDateTime
对象:
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 40, 45);
LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);
Then the output is coming: 29 years 8 months 25 days -1 hours -5 minutes -10 seconds.
然后输出来了: 29 years 8 months 25 days -1 hours -5 minutes -10 seconds.
From this linkit should be 29 years 8 months 24 days 22 hours, 54 minutes and 50 seconds
. So the algorithm needs to handle the negative numbers too.
从这个链接应该是29 years 8 months 24 days 22 hours, 54 minutes and 50 seconds
。所以算法也需要处理负数。
Note the question is not about which site gave me what result, I need to know the right algorithm and need to have right results.
请注意,问题不是关于哪个网站给了我什么结果,我需要知道正确的算法并需要有正确的结果。
采纳答案by Thomas
Unfortunately, there doesn't seem to be a period class that spans time as well, so you might have to do the calculations on your own.
不幸的是,似乎没有跨时间的周期类,因此您可能必须自己进行计算。
Fortunately, the date and time classes have a lot of utility methods that simplify that to some degree. Here's a way to calculate the difference although not necessarily the fastest:
幸运的是,日期和时间类有很多实用方法,可以在一定程度上简化它。这是一种计算差异的方法,尽管不一定是最快的:
LocalDateTime fromDateTime = LocalDateTime.of(1984, 12, 16, 7, 45, 55);
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 40, 45);
LocalDateTime tempDateTime = LocalDateTime.from( fromDateTime );
long years = tempDateTime.until( toDateTime, ChronoUnit.YEARS );
tempDateTime = tempDateTime.plusYears( years );
long months = tempDateTime.until( toDateTime, ChronoUnit.MONTHS );
tempDateTime = tempDateTime.plusMonths( months );
long days = tempDateTime.until( toDateTime, ChronoUnit.DAYS );
tempDateTime = tempDateTime.plusDays( days );
long hours = tempDateTime.until( toDateTime, ChronoUnit.HOURS );
tempDateTime = tempDateTime.plusHours( hours );
long minutes = tempDateTime.until( toDateTime, ChronoUnit.MINUTES );
tempDateTime = tempDateTime.plusMinutes( minutes );
long seconds = tempDateTime.until( toDateTime, ChronoUnit.SECONDS );
System.out.println( years + " years " +
months + " months " +
days + " days " +
hours + " hours " +
minutes + " minutes " +
seconds + " seconds.");
//prints: 29 years 8 months 24 days 22 hours 54 minutes 50 seconds.
The basic idea is this: create a temporary start date and get the full years to the end. Then adjust that date by the number of years so that the start date is less then a year from the end. Repeat that for each time unit in descending order.
基本思想是这样的:创建一个临时的开始日期并获得完整的年份结束。然后按年数调整该日期,使开始日期距结束日期不到一年。按降序对每个时间单位重复此操作。
Finally a disclaimer: I didn't take different timezones into account (both dates should be in the same timezone) and I also didn't test/check how daylight saving time or other changes in a calendar (like the timezone changes in Samoa) affect this calculation. So use with care.
最后免责声明:我没有考虑不同的时区(两个日期应该在同一时区),我也没有测试/检查夏令时或日历中的其他变化(如萨摩亚的时区变化)影响这个计算。所以请谨慎使用。
回答by satnam
I found the best way to do this is with ChronoUnit.
我发现最好的方法是使用ChronoUnit。
long minutes = ChronoUnit.MINUTES.between(fromDate, toDate);
long hours = ChronoUnit.HOURS.between(fromDate, toDate);
Additional documentation is here: https://docs.oracle.com/javase/tutorial/datetime/iso/period.html
其他文档在这里:https: //docs.oracle.com/javase/tutorial/datetime/iso/period.html
回答by Gennady Kolomoets
There is some problem for Tapas Bose code and Thomas code. If time differenсe is negative, array gets the negative values. For example if
Tapas Bose 代码和 Thomas 代码存在一些问题。如果时差为负,则数组获取负值。例如如果
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 46, 45);
LocalDateTime fromDateTime = LocalDateTime.of(2014, 9, 9, 7, 46, 45);
it returns 0 years 0 months 1 days -1 hours 0 minutes 0 seconds.
它返回 0 年 0 月 1 天 -1 小时 0 分 0 秒。
I think the right output is: 0 years 0 months 0 days 23 hours 0 minutes 0 seconds.
我认为正确的输出是:0 年 0 月 0 天 23 小时 0 分 0 秒。
I propose to separate the LocalDateTime instances on LocalDate and LocalTime instances. After that we can obtain the Java 8 Period and Duration instances. The Duration instance is separated on the number of days and throughout-the-day time value (< 24h) with subsequent correction of the period value. When the second LocalTime value is before the firstLocalTime value, it is necessary to reduce the period for one day.
我建议在 LocalDate 和 LocalTime 实例上分离 LocalDateTime 实例。之后,我们可以获得 Java 8 Period 和 Duration 实例。Duration 实例按天数和全天时间值(< 24 小时)分开,随后对周期值进行更正。当第二个LocalTime值在第一个LocalTime值之前时,需要将周期减少一天。
Here's my way to calculate the LocalDateTime difference:
这是我计算 LocalDateTime 差异的方法:
private void getChronoUnitForSecondAfterFirst(LocalDateTime firstLocalDateTime, LocalDateTime secondLocalDateTime, long[] chronoUnits) {
/*Separate LocaldateTime on LocalDate and LocalTime*/
LocalDate firstLocalDate = firstLocalDateTime.toLocalDate();
LocalTime firstLocalTime = firstLocalDateTime.toLocalTime();
LocalDate secondLocalDate = secondLocalDateTime.toLocalDate();
LocalTime secondLocalTime = secondLocalDateTime.toLocalTime();
/*Calculate the time difference*/
Duration duration = Duration.between(firstLocalDateTime, secondLocalDateTime);
long durationDays = duration.toDays();
Duration throughoutTheDayDuration = duration.minusDays(durationDays);
Logger.getLogger(PeriodDuration.class.getName()).log(Level.INFO,
"Duration is: " + duration + " this is " + durationDays
+ " days and " + throughoutTheDayDuration + " time.");
Period period = Period.between(firstLocalDate, secondLocalDate);
/*Correct the date difference*/
if (secondLocalTime.isBefore(firstLocalTime)) {
period = period.minusDays(1);
Logger.getLogger(PeriodDuration.class.getName()).log(Level.INFO,
"minus 1 day");
}
Logger.getLogger(PeriodDuration.class.getName()).log(Level.INFO,
"Period between " + firstLocalDateTime + " and "
+ secondLocalDateTime + " is: " + period + " and duration is: "
+ throughoutTheDayDuration
+ "\n-----------------------------------------------------------------");
/*Calculate chrono unit values and write it in array*/
chronoUnits[0] = period.getYears();
chronoUnits[1] = period.getMonths();
chronoUnits[2] = period.getDays();
chronoUnits[3] = throughoutTheDayDuration.toHours();
chronoUnits[4] = throughoutTheDayDuration.toMinutes() % 60;
chronoUnits[5] = throughoutTheDayDuration.getSeconds() % 60;
}
The above method can be used to calculate the difference of any local date and time values, for example:
上述方法可用于计算任何本地日期和时间值的差异,例如:
public long[] getChronoUnits(String firstLocalDateTimeString, String secondLocalDateTimeString) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime firstLocalDateTime = LocalDateTime.parse(firstLocalDateTimeString, formatter);
LocalDateTime secondLocalDateTime = LocalDateTime.parse(secondLocalDateTimeString, formatter);
long[] chronoUnits = new long[6];
if (secondLocalDateTime.isAfter(firstLocalDateTime)) {
getChronoUnitForSecondAfterFirst(firstLocalDateTime, secondLocalDateTime, chronoUnits);
} else {
getChronoUnitForSecondAfterFirst(secondLocalDateTime, firstLocalDateTime, chronoUnits);
}
return chronoUnits;
}
It is convenient to write a unit test for the above method (both of them are PeriodDuration class members). Here's the code:
为上述方法编写单元测试很方便(它们都是 PeriodDuration 类成员)。这是代码:
@RunWith(Parameterized.class)
public class PeriodDurationTest {
private final String firstLocalDateTimeString;
private final String secondLocalDateTimeString;
private final long[] chronoUnits;
public PeriodDurationTest(String firstLocalDateTimeString, String secondLocalDateTimeString, long[] chronoUnits) {
this.firstLocalDateTimeString = firstLocalDateTimeString;
this.secondLocalDateTimeString = secondLocalDateTimeString;
this.chronoUnits = chronoUnits;
}
@Parameters
public static Collection<Object[]> periodValues() {
long[] chronoUnits0 = {0, 0, 0, 0, 0, 0};
long[] chronoUnits1 = {0, 0, 0, 1, 0, 0};
long[] chronoUnits2 = {0, 0, 0, 23, 0, 0};
long[] chronoUnits3 = {0, 0, 0, 1, 0, 0};
long[] chronoUnits4 = {0, 0, 0, 23, 0, 0};
long[] chronoUnits5 = {0, 0, 1, 23, 0, 0};
long[] chronoUnits6 = {29, 8, 24, 12, 0, 50};
long[] chronoUnits7 = {29, 8, 24, 12, 0, 50};
return Arrays.asList(new Object[][]{
{"2015-09-09 21:46:44", "2015-09-09 21:46:44", chronoUnits0},
{"2015-09-09 21:46:44", "2015-09-09 22:46:44", chronoUnits1},
{"2015-09-09 21:46:44", "2015-09-10 20:46:44", chronoUnits2},
{"2015-09-09 21:46:44", "2015-09-09 20:46:44", chronoUnits3},
{"2015-09-10 20:46:44", "2015-09-09 21:46:44", chronoUnits4},
{"2015-09-11 20:46:44", "2015-09-09 21:46:44", chronoUnits5},
{"1984-12-16 07:45:55", "2014-09-09 19:46:45", chronoUnits6},
{"2014-09-09 19:46:45", "1984-12-16 07:45:55", chronoUnits6}
});
}
@Test
public void testGetChronoUnits() {
PeriodDuration instance = new PeriodDuration();
long[] expResult = this.chronoUnits;
long[] result = instance.getChronoUnits(this.firstLocalDateTimeString, this.secondLocalDateTimeString);
assertArrayEquals(expResult, result);
}
}
}
All tests are successful whether or not the value of the first LocalDateTime is before and for any LocalTime values.
无论第一个 LocalDateTime 的值是否在任何 LocalTime 值之前和之前,所有测试都成功。
回答by ChrLipp
And the version of @Thomas in Groovywith takes the desired units in a list instead of hardcoding the values. This implementation (which can easily ported to Java - I made the function declaration explicit) makes Thomas approach more reuseable.
Groovy 中的@Thomas 版本使用列表中的所需单位,而不是对值进行硬编码。这个实现(可以很容易地移植到 Java - 我明确地声明了函数)使 Thomas 方法更易于重用。
def fromDateTime = LocalDateTime.of(1968, 6, 14, 0, 13, 0)
def toDateTime = LocalDateTime.now()
def listOfUnits = [
ChronoUnit.YEARS, ChronoUnit.MONTHS, ChronoUnit.DAYS,
ChronoUnit.HOURS, ChronoUnit.MINUTES, ChronoUnit.SECONDS,
ChronoUnit.MILLIS]
println calcDurationInTextualForm(listOfUnits, fromDateTime, toDateTime)
String calcDurationInTextualForm(List<ChronoUnit> listOfUnits, LocalDateTime ts, LocalDateTime to)
{
def result = []
listOfUnits.each { chronoUnit ->
long amount = ts.until(to, chronoUnit)
ts = ts.plus(amount, chronoUnit)
if (amount) {
result << "$amount ${chronoUnit.toString()}"
}
}
result.join(', ')
}
At the time of this writing,the code above returns 47 Years, 8 Months, 9 Days, 22 Hours, 52 Minutes, 7 Seconds, 140 Millis
. And, for @Gennady Kolomoets input, the code returns 23 Hours
.
在撰写本文时,上面的代码返回47 Years, 8 Months, 9 Days, 22 Hours, 52 Minutes, 7 Seconds, 140 Millis
. 而且,对于@Gennady Kolomoets 输入,代码返回23 Hours
.
When you provide a list of units it must be sorted by size of the units (biggest first):
当您提供单位列表时,它必须按单位的大小排序(最大的在前):
def listOfUnits = [ChronoUnit.WEEKS, ChronoUnit.DAYS, ChronoUnit.HOURS]
// returns 2495 Weeks, 3 Days, 8 Hours
回答by Junior Batista
Here a single example using Duration and TimeUnit to get 'hh:mm:ss' format.
这是一个使用 Duration 和 TimeUnit 来获取“hh:mm:ss”格式的示例。
Duration dur = Duration.between(localDateTimeIni, localDateTimeEnd);
long millis = dur.toMillis();
String.format("%02d:%02d:%02d",
TimeUnit.MILLISECONDS.toHours(millis),
TimeUnit.MILLISECONDS.toMinutes(millis) -
TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(millis)),
TimeUnit.MILLISECONDS.toSeconds(millis) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)));
回答by SavedBeau
Here is a very simple answer to your question. It works.
这是对你的问题的一个非常简单的答案。有用。
import java.time.*;
import java.util.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
public class MyClass {
public static void main(String args[]) {
DateTimeFormatter T = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
Scanner h = new Scanner(System.in);
System.out.print("Enter date of birth[dd/mm/yyyy hh:mm]: ");
String b = h.nextLine();
LocalDateTime bd = LocalDateTime.parse(b,T);
LocalDateTime cd = LocalDateTime.now();
long minutes = ChronoUnit.MINUTES.between(bd, cd);
long hours = ChronoUnit.HOURS.between(bd, cd);
System.out.print("Age is: "+hours+ " hours, or " +minutes+ " minutes old");
}
}
回答by Zon
It should be simpler!
应该更简单!
Duration.between(startLocalDateTime, endLocalDateTime).toMillis();
回答by Kushal
Joda-Time
乔达时间
Since many of the answers required API 26support and my min API was 23, I solved it by below code :
由于许多答案需要API 26支持,而我的最小 API 是 23,我通过以下代码解决了这个问题:
import org.joda.time.Days
LocalDate startDate = Something
LocalDate endDate = Something
// The difference would be exclusive of both dates,
// so in most of use cases we may need to increment it by 1
Days.daysBetween(startDate, endDate).days
回答by Anakhand
TL;DR
TL; 博士
Duration duration = Duration.between(start, end);
duration = duration.minusDays(duration.toDaysPart()); // essentially "duration (mod 1 day)"
Period period = Period.between(start.toLocalDate(), end.toLocalDate());
and then use the methods period.getYears()
, period.getMonths()
, period.getDays()
, duration.toHoursPart()
, duration.toMinutesPart()
, duration.toSecondsPart()
.
然后使用方法period.getYears()
, period.getMonths()
, period.getDays()
, duration.toHoursPart()
, duration.toMinutesPart()
, duration.toSecondsPart()
。
Expanded answer
扩展答案
I'll answer the original question, i.e. how to get the time difference between two LocalDateTimes
in years, months, days, hours & minutes, such that the "sum" (see note below) of all the values for the different units equals the total temporal difference, and such that the value in each unit is smaller than the next bigger unit—i.e. minutes < 60
, hours < 24
, and so on.
我将回答最初的问题,即如何获得两个LocalDateTimes
以年、月、日、小时和分钟为单位的时差,使得不同单位的所有值的“总和”(见下面的注释)等于总和时间差异,使得每个单元中的值小于下一个更大的单元——即minutes < 60
,hours < 24
,等等。
Given two LocalDateTimes
start
and end
, e.g.
给定两个LocalDateTimes
start
和end
,例如
LocalDateTime start = LocalDateTime.of(2019, 11, 29, 17, 15);
LocalDateTime end = LocalDateTime.of(2020, 11, 30, 18, 44);
we can represent the absolute timespan between the two with a Duration
—perhaps using Duration.between(start, end)
. But the biggest unit we can extract out of a Duration
is days (as a temporal unit equivalent to 24h)—see the note below for an explanation. To use larger units (months, years) we can represent this Duration
with a pair of (Period
, Duration
), where the Period
measures the difference up to a precision of days and the Duration
represents the remainder:
我们可以用 表示两者之间的绝对时间跨度——Duration
也许使用Duration.between(start, end)
。但是我们可以从 a 中提取的最大单位Duration
是天(作为相当于 24 小时的时间单位)——请参阅下面的注释以获取解释。要使用更大的单位(月、年),我们可以Duration
用一对 ( Period
, Duration
)表示它,其中Period
测量差异达到天数的精度,而Duration
表示余数:
Duration duration = Duration.between(start, end);
duration = duration.minusDays(duration.toDaysPart()); // essentially "duration (mod 1 day)"
Period period = Period.between(start.toLocalDate(), end.toLocalDate());
Now we can simply use the methods defined on Period
and Duration
to extract the individual units:
现在我们可以简单地使用Period
和Duration
上定义的方法来提取单个单位:
System.out.printf("%d years, %d months, %d days, %d hours, %d minutes, %d seconds",
period.getYears(), period.getMonths(), period.getDays(), duration.toHoursPart(),
duration.toMinutesPart(), duration.toSecondsPart());
1 years, 0 months, 1 days, 1 hours, 29 minutes, 0 seconds
or, using the default format:
或者,使用默认格式:
System.out.println(period + " + " + duration);
P1Y1D + PT1H29M
Note on years, months & days
注意年月日
Note that, in java.time
's conception, "units" like "month" or "year" don't represent a fixed, absolute temporal value—they're date- and calendar-dependent, as the following example illustrates:
请注意,在 injava.time
的概念中,像“月”或“年”这样的“单位”不代表固定的绝对时间值——它们依赖于日期和日历,如下例所示:
LocalDateTime
start1 = LocalDateTime.of(2020, 1, 1, 0, 0),
end1 = LocalDateTime.of(2021, 1, 1, 0, 0),
start2 = LocalDateTime.of(2021, 1, 1, 0, 0),
end2 = LocalDateTime.of(2022, 1, 1, 0, 0);
System.out.println(Period.between(start1.toLocalDate(), end1.toLocalDate()));
System.out.println(Duration.between(start1, end1).toDays());
System.out.println(Period.between(start2.toLocalDate(), end2.toLocalDate()));
System.out.println(Duration.between(start2, end2).toDays());
P1Y
366
P1Y
365
回答by Gennady Kolomoets
After more than five years I answer my question. I think that the problem with a negative duration can be solved by a simple correction:
五年多后,我回答了我的问题。我认为可以通过简单的更正来解决负持续时间的问题:
LocalDateTime fromDateTime = LocalDateTime.of(2014, 9, 9, 7, 46, 45);
LocalDateTime toDateTime = LocalDateTime.of(2014, 9, 10, 6, 46, 45);
Period period = Period.between(fromDateTime.toLocalDate(), toDateTime.toLocalDate());
Duration duration = Duration.between(fromDateTime.toLocalTime(), toDateTime.toLocalTime());
if (duration.isNegative()) {
period = period.minusDays(1);
duration = duration.plusDays(1);
}
long seconds = duration.getSeconds();
long hours = seconds / SECONDS_PER_HOUR;
long minutes = ((seconds % SECONDS_PER_HOUR) / SECONDS_PER_MINUTE);
long secs = (seconds % SECONDS_PER_MINUTE);
long time[] = {hours, minutes, secs};
System.out.println(period.getYears() + " years "
+ period.getMonths() + " months "
+ period.getDays() + " days "
+ time[0] + " hours "
+ time[1] + " minutes "
+ time[2] + " seconds.");
Note:The site https://www.epochconverter.com/date-differencenow correctly calculates the time difference.
注意:站点https://www.epochconverter.com/date-difference现在可以正确计算时差。
Thank you all for your discussion and suggestions.
谢谢大家的讨论和建议。