Java 如何获取两个日历实例之间的天数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19462912/
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
How to get number of days between two calendar instance?
提问by Akram
I want to find the difference between two Calendar
objects in number of days if there is date change like If clock ticked from 23:59-0:00 there should be a day difference.
Calendar
如果有日期更改,我想找到两个对象之间的天数差异,例如如果时钟从 23:59-0:00 打勾,应该有天差。
i wrote this
我写了这个
public static int daysBetween(Calendar startDate, Calendar endDate) {
return Math.abs(startDate.get(Calendar.DAY_OF_MONTH)-endDate.get(Calendar.DAY_OF_MONTH));
}
but its not working as it only gives difference between days if there is month difference its worthless.
但它不起作用,因为如果有月差,它只会给出天之间的差异,它毫无价值。
采纳答案by Mohamed Anees A
回答by Jk1
Try the following approach:
尝试以下方法:
public static long daysBetween(Calendar startDate, Calendar endDate) {
long end = endDate.getTimeInMillis();
long start = startDate.getTimeInMillis();
return TimeUnit.MILLISECONDS.toDays(Math.abs(end - start));
}
回答by stackoverflowuser2010
This function computes the number of days between two Calendars as the number of calendar days of the month that are between them, which is what the OP wanted. The calculation is performed by counting how many multiples of 86,400,000 milliseconds are between the calendars after both have been set to midnight of their respective days.
此函数计算两个日历之间的天数作为它们之间的月份日历天数,这是 OP 想要的。计算是通过计算日历之间有多少 86,400,000 毫秒的倍数被设置为各自日期的午夜来执行的。
For example, my function will compute 1 day's difference between a Calendar on January 1, 11:59PM and January 2, 12:01AM.
例如,我的函数将计算日历在 1 月 1 日晚上 11:59 和 1 月 2 日上午 12:01 之间的 1 天差异。
import java.util.concurrent.TimeUnit;
/**
* Compute the number of calendar days between two Calendar objects.
* The desired value is the number of days of the month between the
* two Calendars, not the number of milliseconds' worth of days.
* @param startCal The earlier calendar
* @param endCal The later calendar
* @return the number of calendar days of the month between startCal and endCal
*/
public static long calendarDaysBetween(Calendar startCal, Calendar endCal) {
// Create copies so we don't update the original calendars.
Calendar start = Calendar.getInstance();
start.setTimeZone(startCal.getTimeZone());
start.setTimeInMillis(startCal.getTimeInMillis());
Calendar end = Calendar.getInstance();
end.setTimeZone(endCal.getTimeZone());
end.setTimeInMillis(endCal.getTimeInMillis());
// Set the copies to be at midnight, but keep the day information.
start.set(Calendar.HOUR_OF_DAY, 0);
start.set(Calendar.MINUTE, 0);
start.set(Calendar.SECOND, 0);
start.set(Calendar.MILLISECOND, 0);
end.set(Calendar.HOUR_OF_DAY, 0);
end.set(Calendar.MINUTE, 0);
end.set(Calendar.SECOND, 0);
end.set(Calendar.MILLISECOND, 0);
// At this point, each calendar is set to midnight on
// their respective days. Now use TimeUnit.MILLISECONDS to
// compute the number of full days between the two of them.
return TimeUnit.MILLISECONDS.toDays(
Math.abs(end.getTimeInMillis() - start.getTimeInMillis()));
}
回答by stackoverflowuser2010
Calendar day1 = Calendar.getInstance();
日历 day1 = Calendar.getInstance();
Calendar day2 = Calendar.getInstance();
日历 day2 = Calendar.getInstance();
int diff = day1.get(Calendar.DAY_OF_YEAR) - day2.get(Calendar.DAY_OF_YEAR);回答by Basil Bourque
UPDATEThe Joda-Timeproject, now in maintenance mode, advises migration to the java.timeclasses. See the Answerby Anees A for the calculation of elapsed hours, and see my new Answerfor using java.timeto calculate elapsed days with respect for the calendar.
UPDATE的乔达时间的项目,现在在维护模式,建议迁移到java.time类。请参阅Anees A的答案以了解经过的小时数的计算,并参阅我的新答案以使用java.time计算与日历相关的经过天数。
Joda-Time
乔达时间
The old java.util.Date/.Calendar classes are notoriously troublesome and should be avoided.
旧的 java.util.Date/.Calendar 类是出了名的麻烦,应该避免。
Instead use the Joda-Timelibrary. Unless you have Java 8 technology in which case use its successor, the built-in java.time framework (not in Android as of 2015).
而是使用Joda-Time库。除非你有 Java 8 技术,在这种情况下使用它的后继者,内置的 java.time 框架(2015 年 Android 中没有)。
Since you only care about "days" defined as dates (not 24-hour periods), let's focus on dates. Joda-Time offers the class LocalDate
to represent a date-only value without time-of-day nor time zone.
由于您只关心定义为日期的“天”(而不是 24 小时周期),因此让我们关注日期。Joda-Time 提供了LocalDate
表示没有时间和时区的仅日期值的类。
While lacking a time zone, note that time zone is crucialin determining a date such as "today". A new day dawns earlier to the east than to the west. So the date is not the same around the world at one moment, the date depends on your time zone.
虽然缺少时区,但请注意时区对于确定“今天”等日期至关重要。新的一天在东方比西方更早的到来。因此,某一时刻世界各地的日期都不相同,日期取决于您所在的时区。
DateTimeZone zone = DateTimeZone.forID ( "America/Montreal" );
LocalDate today = LocalDate.now ( zone );
Let's count the number of days until next week, which should of course be seven.
让我们数一数到下周的天数,当然应该是七天。
LocalDate weekLater = today.plusWeeks ( 1 );
int elapsed = Days.daysBetween ( today , weekLater ).getDays ();
The getDays
on the end extracts a plain int
number from the Days
object returned by daysBetween
.
该getDays
对端提取纯int
从数Days
由返回的对象daysBetween
。
Dump to console.
转储到控制台。
System.out.println ( "today: " + today + " to weekLater: " + weekLater + " is days: " + days );
today: 2015-12-22 to weekLater: 2015-12-29 is days: 7
今天:2015-12-22 到星期以后:2015-12-29 是天:7
You have Calendar objects. We need to convert them to Joda-Time objects. Internally the Calendar objects have a long
integer tracking the number of milliseconds since the epoch of first moment of 1970 in UTC. We can extract that number, and feed it to Joda-Time. We also need to assign the desired time zone by which we intend to determine a date.
您有 Calendar 对象。我们需要将它们转换为 Joda-Time 对象。在内部 Calendar 对象有一个long
整数,用于跟踪自UTC1970 年第一个时刻以来的毫秒数。我们可以提取该数字,并将其提供给 Joda-Time。我们还需要指定我们打算确定日期的所需时区。
long startMillis = myStartCalendar.getTimeInMillis();
DateTime startDateTime = new DateTime( startMillis , zone );
long stopMillis = myStopCalendar.getTimeInMillis();
DateTime stopDateTime = new DateTime( stopMillis , zone );
Convert from DateTime objects to LocalDate.
从 DateTime 对象转换为 LocalDate。
LocalDate start = startDateTime.toLocalDate();
LocalDate stop = stopDateTime.toLocalDate();
Now do the same elapsed calculation we saw earlier.
现在做我们之前看到的同样的经过计算。
int elapsed = Days.daysBetween ( start , stop ).getDays ();
回答by Arpit
I have the similar (not exact same) approach given above by https://stackoverflow.com/a/31800947/3845798.
我有https://stackoverflow.com/a/31800947/3845798上面给出的类似(不完全相同)的方法。
And have written test cases around the api, for me it failed if I passed 8th march 2017 - as the start date and 8th apr 2017 as the end date.
并且围绕 api 编写了测试用例,对我来说,如果我通过 2017 年 3 月 8 日作为开始日期和 2017 年 4 月 8 日作为结束日期,它就会失败。
There are few dates where you will see the difference by 1day. Therefore, I have kind of made some small changes to my api and my current api now looks something like this
很少有日期会相差 1 天。因此,我对我的 api 进行了一些小的更改,我当前的 api 现在看起来像这样
public long getDays(long currentTime, long endDateTime) {
Calendar endDateCalendar;
Calendar currentDayCalendar;
//expiration day
endDateCalendar = Calendar.getInstance(TimeZone.getTimeZone("EST"));
endDateCalendar.setTimeInMillis(endDateTime);
endDateCalendar.set(Calendar.MILLISECOND, 0);
endDateCalendar.set(Calendar.MINUTE, 0);
endDateCalendar.set(Calendar.HOUR, 0);
endDateCalendar.set(Calendar.HOUR_OF_DAY, 0);
//current day
currentDayCalendar = Calendar.getInstance(TimeZone.getTimeZone("EST"));
currentDayCalendar.setTimeInMillis(currentTime);
currentDayCalendar.set(Calendar.MILLISECOND, 0);
currentDayCalendar.set(Calendar.MINUTE, 0);
currentDayCalendar.set(Calendar.HOUR,0);
currentDayCalendar.set(Calendar.HOUR_OF_DAY, 0);
long remainingDays = (long)Math.ceil((float) (endDateCalendar.getTimeInMillis() - currentDayCalendar.getTimeInMillis()) / (24 * 60 * 60 * 1000));
return remainingDays;}
I am not using TimeUnit.MILLISECONDS.toDays that were causing me some issues.
我没有使用导致我出现一些问题的 TimeUnit.MILLISECONDS.toDays。
回答by Steve Lukis
Extension to @JK1 great answer :
扩展@JK1 很好的答案:
public static long daysBetween(Calendar startDate, Calendar endDate) {
//Make sure we don't change the parameter passed
Calendar newStart = Calendar.getInstance();
newStart.setTimeInMillis(startDate.getTimeInMillis());
newStart.set(Calendar.HOUR_OF_DAY, 0);
newStart.set(Calendar.MINUTE, 0);
newStart.set(Calendar.SECOND, 0);
newStart.set(Calendar.MILLISECOND, 0);
Calendar newEnd = Calendar.getInstance();
newEnd.setTimeInMillis(endDate.getTimeInMillis());
newEnd.set(Calendar.HOUR_OF_DAY, 0);
newEnd.set(Calendar.MINUTE, 0);
newEnd.set(Calendar.SECOND, 0);
newEnd.set(Calendar.MILLISECOND, 0);
long end = newEnd.getTimeInMillis();
long start = newStart.getTimeInMillis();
return TimeUnit.MILLISECONDS.toDays(Math.abs(end - start));
}
回答by Александр Бабич
Kotlin solution, purely relies on Calendar. At the end gives exact number of days difference. Inspired by @Jk1
Kotlin 解决方案,纯粹依赖 Calendar。最后给出了确切的天数差异。灵感来自@Jk1
private fun daysBetween(startDate: Calendar, endDate: Calendar): Long {
val start = Calendar.getInstance().apply {
timeInMillis = 0
set(Calendar.DAY_OF_YEAR, startDate.get(Calendar.DAY_OF_YEAR))
set(Calendar.YEAR, startDate.get(Calendar.YEAR))
}.timeInMillis
val end = Calendar.getInstance().apply {
timeInMillis = 0
set(Calendar.DAY_OF_YEAR, endDate.get(Calendar.DAY_OF_YEAR))
set(Calendar.YEAR, endDate.get(Calendar.YEAR))
}.timeInMillis
val differenceMillis = end - start
return TimeUnit.MILLISECONDS.toDays(differenceMillis)
}
回答by Basil Bourque
java.time
时间
The Answer by Mohamed Anees Ais correct for hours but wrong for days. Counting days requires a time zone. That other Answer uses the Instant
which is a moment in UTC, always in UTC. So you are notgetting the correct number of calendardays elapsed.
Mohamed Anees A的答案在几小时内是正确的,但在几天内是错误的。计算天数需要一个时区。另一个答案使用Instant
UTC 中的时刻,始终使用 UTC。所以你没有得到正确的日历天数。
To count days by the calendar, convert your legacy Calendar
to a ZonedDateTime
, then feed to ChronoUnit.DAYS.between
.
要按日历计算天数,请将您的遗产转换Calendar
为ZonedDateTime
,然后馈入ChronoUnit.DAYS.between
。
Time zone
时区
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris Franceis a new day while still “yesterday” in Montréal Québec.
时区对于确定日期至关重要。对于任何给定时刻,日期因地区而异。例如,在法国巴黎午夜过后几分钟是新的一天,而在魁北克蒙特利尔仍然是“昨天” 。
If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any momentduring runtime(!), so your results may vary. Better to specify your desired/expected time zone explicitly as an argument. If critical, confirm the zone with your user.
如果未指定时区,JVM 会隐式应用其当前默认时区。该默认值可能会在运行时随时更改(!),因此您的结果可能会有所不同。最好将您想要/预期的时区明确指定为参数。如果关键,请与您的用户确认该区域。
Specify a proper time zone namein the format of Continent/Region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 2-4 letter abbreviation such as EST
or IST
as they are nottrue time zones, not standardized, and not even unique(!).
以、、 或等格式指定正确的时区名称。永远不要使用 2-4 个字母的缩写,例如或因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。Continent/Region
America/Montreal
Africa/Casablanca
Pacific/Auckland
EST
IST
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ; // Capture the current date as seen through the wall-clock time used by the people of a certain region (a time zone).
If you want to use the JVM's current default time zone, ask for it and pass as an argument. If omitted, the code becomes ambiguous to read in that we do not know for certain if you intended to use the default or if you, like so many programmers, were unaware of the issue.
如果您想使用 JVM 的当前默认时区,请询问它并作为参数传递。如果省略,代码读起来会变得模棱两可,因为我们不确定您是否打算使用默认值,或者您是否像许多程序员一样没有意识到这个问题。
ZoneId z = ZoneId.systemDefault() ; // Get JVM's current default time zone.
Convert from GregorianCalendar
to ZonedDateTime
转换GregorianCalendar
为ZonedDateTime
The terrible GregorianCalendar
is likely the concrete class behind your Calendar
. If so, convert from that legacy class to the modern class, ZonedDateTime
.
可怕的GregorianCalendar
是你的Calendar
. 如果是这样,请从该遗留类转换为现代类,ZonedDateTime
.
GregorianCalendar gc = null ; // Legacy class representing a moment in a time zone. Avoid this class as it is terribly designed.
if( myCal instanceof GregorianCalendar ) { // See if your `Calendar` is backed by a `GregorianCalendar` class.
gc = (GregorianCalendar) myCal ; // Cast from the more general class to the concrete class.
ZonedDateTime zdt = gc.toZonedDateTime() ; // Convert from legacy class to modern class.
}
The resulting ZonedDateTime
object carries a ZoneId
object for the time zone. With that zone in place, you can then calculate elapsed calendar days.
生成的ZonedDateTime
对象带有ZoneId
时区的对象。设置好该区域后,您就可以计算经过的日历天数。
Calculate elapsed days
计算经过的天数
To calculate the elapsed time in terms of years-months-days, use Period
class.
要以年-月-日为单位计算经过的时间,请使用Period
class。
Period p = Period.between( zdtStart , zdtStop ) ;
If you want total number of days as the elapsed time, use ChronoUnit
.
如果您想要总天数作为经过的时间,请使用eChronoUnit
。
long days = ChronoUnit.DAYS.between( zdtStart , zdtStop ) ;
About java.time
关于java.time
The java.timeframework is built into Java 8 and later. These classes supplant the troublesome old legacydate-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
该java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date
, Calendar
, & SimpleDateFormat
。
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
要了解更多信息,请参阅Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规范是JSR 310。
The Joda-Timeproject, now in maintenance mode, advises migration to the java.timeclasses.
现在处于维护模式的Joda-Time项目建议迁移到java.time类。
You may exchange java.timeobjects directly with your database. Use a JDBC drivercompliant with JDBC 4.2or later. No need for strings, no need for java.sql.*
classes.
您可以直接与您的数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC 驱动程序。不需要字符串,不需要类。java.sql.*
Where to obtain the java.time classes?
从哪里获得 java.time 类?
- Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
- Java 9 adds some minor features and fixes.
- Java SE 6and Java SE 7
- Most of the java.timefunctionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
- Android
- Later versions of Android bundle implementations of the java.timeclasses.
- For earlier Android (<26), the ThreeTenABPproject adapts ThreeTen-Backport(mentioned above). See How to use ThreeTenABP….
- Java SE 8、Java SE 9、Java SE 10、Java SE 11及更高版本 - 标准 Java API 的一部分,具有捆绑实现。
- Java 9 添加了一些小功能和修复。
- Java SE 6和Java SE 7
- 大部分java.time功能在ThreeTen-Backport中向后移植到 Java 6 & 7 。
- 安卓
- 更高版本的 Android 捆绑实现java.time类。
- 对于早期的 Android(<26),ThreeTenABP项目采用了ThreeTen-Backport(上面提到过)。请参阅如何使用ThreeTenABP ...。
The ThreeTen-Extraproject extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.
该ThreeTen-额外项目与其他类扩展java.time。该项目是未来可能添加到 java.time 的试验场。你可能在这里找到一些有用的类,比如Interval
,YearWeek
,YearQuarter
,和更多。
回答by Jay
Here's my solution using good old Calendar objects:
这是我使用旧日历对象的解决方案:
public static int daysApart(Calendar d0,Calendar d1)
{
int days=d0.get(Calendar.DAY_OF_YEAR)-d1.get(Calendar.DAY_OF_YEAR);
Calendar d1p=Calendar.getInstance();
d1p.setTime(d1.getTime());
for (;d1p.get(Calendar.YEAR)<d0.get(Calendar.YEAR);d1p.add(Calendar.YEAR,1))
{
days+=d1p.getActualMaximum(Calendar.DAY_OF_YEAR);
}
return days;
}
This assumes d0 is later than d1. If that's not guaranteed, you could always test and swap them.
这假设 d0 晚于 d1。如果不能保证,您可以随时测试和交换它们。
Basic principle is to take the difference between the day of the year of each. If they're in the same year, that would be it.
基本原则是取每一年的天数之差。如果他们在同一年,那就是了。
But they might be different years. So I loop through all the years between them, adding the number of days in a year. Note that getActualMaximum returns 366 in leap years and 365 in non-leap years. That's why we need a loop, you can't just multiply the difference between the years by 365 because there might be a leap year in there. (My first draft used getMaximum, but that doesn't work because it returns 366 regardless of the year. getMaximum is the maximum for ANY year, not this particular year.)
但他们可能是不同的年份。所以我遍历它们之间的所有年份,添加一年中的天数。请注意,getActualMaximum 在闰年返回 366,在非闰年返回 365。这就是为什么我们需要一个循环,你不能只是将年份之间的差异乘以 365,因为那里可能有一个闰年。(我的初稿使用了 getMaximum,但这不起作用,因为无论年份如何,它都会返回 366。getMaximum 是任何年份的最大值,而不是特定年份。)
As this code makes no assumptions about the number of hours in a day, it is not fooled by daylight savings time.
由于此代码不对一天中的小时数进行假设,因此不会被夏令时所迷惑。