简介
我们经常会发现自己在计算某个特定日期的 起止天数。幸运的是,在Java中做到这一点并不难,而且也有很多方法可以做到这一点。
因此,在这篇文章中,我们将看看如何在Java中获得两个日期之间的天数。
寻找Java 8之前的两个日期之间的差异
这是计算两个日期之间天数差异的最简单和最直接的方法之一。为了做到这一点,我们需要有两个Date 对象。使用getTime() from java.util.Date类,我们将得到自1970年1月1日00:00:00 GMT以来的毫秒数,由一个特定的Date 对象表示。
注意:这个特定的时间也不是巧合,因为它经常被称为UNIX Epoch或UNIX Time的开始。
在得到毫秒的时间后,我们将通过减去两个值并将新值转换为天数来得到两个值之间的差值--可以手动(通过简单的计算)或使用TimeUnit.DAYS.convert 方法从 java.util.concurrent.TimeUnit.
我们可以通过Date ,也可以通过String 对象来获取日期。在第二种情况下,我们需要使用SimpleDateFormat 类中的parse 方法将String 转换为Date 。
让我们计算一下两个例子的日期的天数。
Date dateBefore = new Date(2022, Calendar.APRIL, 21);
Date dateAfter = new Date(2022, Calendar.APRIL, 25);
long dateBeforeInMs = dateBefore.getTime();
long dateAfterInMs = dateAfter.getTime();
long timeDiff = Math.abs(dateAfterInMs - dateBeforeInMs);
long daysDiff = TimeUnit.DAYS.convert(timeDiff, TimeUnit.MILLISECONDS);
// Alternatevly:
// int daysDiff = (int) (timeDiff / (1000 * 60 * 60 * 24));
System.out.println(" The number of days between dates: " + daysDiff);
由于我们的例子日期之间有四天,前面的代码将产生以下输出。
The number of days between dates: 4
如前所述,如果我们使用的是String ,而不是Date 对象,我们首先需要对它进行转换,才能使用它。为了将String 转换为Date ,我们需要首先定义我们将使用的日期格式,以便正确地解析这些值。在我们的例子中,我们将使用MM/dd/yyyy 格式。之后,我们将String 解析为Date ,并采取与前一个例子相同的做法。
try {
// Convert `String` to `Date`
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH);
Date dateBefore = sdf.parse("04/21/2022");
Date dateAfter = sdf.parse("04/25/2022");
// Calculate the number of days between dates
long timeDiff = Math.abs(dateAfter.getTime() - dateBefore.getTime());
long daysDiff = TimeUnit.DAYS.convert(timeDiff, TimeUnit.MILLISECONDS);
System.out.println("The number of days between dates: " + daysDiff);
}catch(Exception e){
e.printStackTrace();
}
我们首先定义了一个新的SimpleDateFormat ,这样我们就知道如何解析出通过String 给出的日期,之后我们使用方法parse 来完成这一工作。这里我们唯一需要注意的是,我们需要用一个try-catch 块来包围这部分代码,因为parse 方法会导致错误,破坏我们的程序。最后,在我们计算出时间差后,我们再次应用TimeUnit.DAYS.convert 方法将其转换为天数,并产生以下输出。
The number of days between dates: 4
使用java.time.temporal.ChronoUnit查找两个日期之间的差异
ChronoUnit 是一个实现了 接口的 ,它提供了TemporalUnit enum Java日期时间API中使用的标准单位(本质上是一套标准的日期周期单位)。这组单位提供了基于单位的访问,以操纵一个日期、时间或日期-时间。
ChronoUnit 只有几个方法,而我们最感兴趣的是 ***between()***方法。这个方法接收了两个Temporal 对象并计算它们之间的时间量。开始点和结束点必须是兼容的类型。在计算金额之前,实现会将第二种类型转换为第一种类型的一个实例。计算返回一个整数,代表两个时间点之间的完整单位数。
注意如果结束日期在开始日期之前,结果将是负数。
有两种不同的方式来使用between() 方法。第一种是直接调用这个方法。第二种是使用Temporal.until(Temporal, TemporalUnit) ,隐含地调用该方法。
try {
LocalDate dateBefore = LocalDate.parse("2022-04-21");
LocalDate dateAfter = LocalDate.parse("2022-04-25");
// Approach 1
long daysDiff = ChronoUnit.DAYS.between(dateBefore, dateAfter);
System.out.println("The number of days between dates: " + daysDiff);
// Approach 2
long daysDiff = dateBefore.until(dateAfter, ChronoUnit.DAYS);
System.out.println("The number of days between dates: " + daysDiff);
}catch(Exception e){
e.printStackTrace();
}
这两种方法产生的结果是一样的。
The number of days between dates: 4
使用java.Time.Period查找两个日期之间的差异
Period 类是一个基于日期的类,这意味着它以年、月、日为单位来模拟时间的数量(金额)--例如*"3年5个月7天"*。它有一个基于时间的等价物,即Duration 类,它以秒和纳秒为单位对时间量进行建模。
在添加到ZonedDateTime 中时,期限和时期在处理夏令时方面有所不同。一个Duration 将添加一个精确的秒数,因此一天的持续时间总是精确的24小时。相比之下,一个Period 将增加一个概念上的一天,试图保持当地时间。
Period 类有一个 between()方法--就像之前讨论的ChronoUnit 。这个方法接收两个LocalDate 对象,一个代表开始日期,另一个是结束日期。它返回一个Period ,由两个日期之间的年、月、日的数量组成。值得注意的是,开始日期被包括在内,而结束日期则不包括在内。
让我们看一个如何使用这种方法的例子。
LocalDate dateBefore = LocalDate.of(2022, 4, 21);
LocalDate dateAfter = LocalDate.of(2022, 4, 25);
Period period = Period.between(dateBefore, dateAfter);
long daysDiff = Math.abs(period.getDays());
System.out.println(" The number of days between dates: " + daysDiff);
这样做很好,会产生与我们之前所有的例子一样的结果。然而,如果你想计算各月之间的天数时间,这种方法有一个很大的问题。
LocalDate dateBefore = LocalDate.of(2022, 2, 21);
LocalDate dateAfter = LocalDate.of(2022, 4, 25);
如果我们计算这两个日期之间的差异,预期答案应该是63天。然而,我们得到的输出结果又是4天。这背后的原因是--周期包括年、月和日的数量,是独立的实体。我们无法将月和年 "加起来 "成为自然的天数,所以你必须为此创建一个额外的方法。
static long getDayDiff(Period period) {
long years = period.getYears();
long months = period.getMonths();
long days = period.getDays();
return (years*365)+(months*30)+days;
}
不过,这仍然只是一个近似值,没有考虑到闰年,也没有考虑到一个月是否有30天或31天。所有的if-clauses和检查很快就会变得非常冗长,所以我们强烈建议坚持使用between() 方法来获取日期之间的天数。
使用Joda-Time查找两个日期之间的差异
众所周知,Java 8之前的标准日期和时间类很差,因此Joda-Time ,成为当时使用最广泛的标准日期和时间库。该设计允许多种日历系统,同时仍然提供了一个简单的API。大部分的日历系统都被包括在内,以及支持时区、持续时间、格式和解析的类。
注意本节是为那些仍然使用Joda-Time而不是新的Date/Time API的遗留系统而添加的。如果你使用的是较新版本的Java,最好不要包括新的依赖关系,这样更容易,也更有性能。
向我们的项目添加Joda-Time ,需要从Maven中心添加以下依赖。
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.14</version>
</dependency>
我们不会深入研究Joda-Time 库提供的一切,如果你有兴趣进一步了解,可以查看Joda-Time的官方网站。
现在我们已经将该库添加到我们的项目中,获取日期之间的差异非常简单,与之前的方法没有任何区别。
org.joda.time.LocalDate dateBefore = org.joda.time.LocalDate.parse("2022-04-21");
org.joda.time.LocalDate dateAfter = org.joda.time.LocalDate.parse("2022-04-25");
long daysDiff = Math.abs(Days.daysBetween(dateBefore, dateAfter).getDays());
System.out.println(" The number of days between dates: " + daysDiff);
虽然很有用,但这个库实际上被Java 8中时间和日期格式化的大修所 "淹没 "了,而且与之前讨论的方法相比,几乎没有任何好处。然而,如果你因为某种原因使用比Java 8更早的版本,这是最好的方法。
总结
在这篇文章中,我们看了如何完成编程中最基本的任务之一--找到两个日期之间的天数。首先,我们用最简单的方法完成了这个任务,然后研究了几种替代方法--ChronoUnit 枚举,Duration 和Period 类,java.time 。最后,我们看了一下如何使用一个广泛流行的外部库(如Joda-Time )来计算两个日期之间的天数,以获得一个更优雅的方法来解决这个问题。