如果有日期变化,我想在两天内找到两个Calendar
对象之间的差异。如果时钟从23:59-0:00开始,应该有一天的差异。
我写了这个
public static int daysBetween(Calendar startDate, Calendar endDate) {
return Math.abs(startDate.get(Calendar.DAY_OF_MONTH)-endDate.get(Calendar.DAY_OF_MONTH));
}
但它不起作用,因为它只会在天数差异之间给出差异,如果有月份差异则毫无价值。
在Java 8及更高版本中,我们可以简单地使用java.time类。
hoursBetween = ChronoUnit.HOURS.between(calendarObj.toInstant(), calendarObj.toInstant());
daysBetween = ChronoUnit.DAYS.between(calendarObj.toInstant(), calendarObj.toInstant());
Calendar day1 = Calendar.getInstance();
Calendar day2 = Calendar.getInstance();
int diff = day1.get(Calendar.DAY_OF_YEAR) - day2.get(Calendar.DAY_OF_YEAR);尝试以下方法:
public static long daysBetween(Calendar startDate, Calendar endDate) {
long end = endDate.getTimeInMillis();
long start = startDate.getTimeInMillis();
return TimeUnit.MILLISECONDS.toDays(Math.abs(end - start));
}
此函数计算两个日历之间的天数,作为它们之间的月份日历天数,这是OP想要的。通过计算两个日历被设置为各自日期的午夜之后的日历之间的多少86,400,000毫秒来执行计算。
例如,我的函数将计算1月1日晚上11:59和1月2日12:01 AM之间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()));
}
更新Joda-Time项目,现在在maintenance mode,建议迁移到java.time班。请参阅Anees A的the Answer计算经过的小时数,并查看我的new Answer,了解如何使用java.time计算日历的经过天数。
旧的java.util.Date/.Calendar类是众所周知的麻烦,应该避免。
而是使用Joda-Time库。除非您使用Java 8技术,否则使用其后续版本,即内置的java.time框架(截至2015年不在Android中)。
由于您只关心定义为日期(而不是24小时)的“日期”,所以让我们关注日期。 Joda-Time提供类LocalDate
来表示没有时间或时区的仅日期值。
虽然缺少时区,但请注意时区对于确定诸如“今天”之类的日期至关重要。新的一天早些时候在东方而不是从西方开始。因此,某个时刻世界各地的日期不一样,日期取决于您的时区。
DateTimeZone zone = DateTimeZone.forID ( "America/Montreal" );
LocalDate today = LocalDate.now ( zone );
我们算一下下周的天数,当然应该是七天。
LocalDate weekLater = today.plusWeeks ( 1 );
int elapsed = Days.daysBetween ( today , weekLater ).getDays ();
最后的getDays
从int
返回的Days
对象中提取了一个普通的daysBetween
数。
转储到控制台。
System.out.println ( "today: " + today + " to weekLater: " + weekLater + " is days: " + days );
今日:2015-12-22至周后期:2015-12-29为天:7
你有Calendar对象。我们需要将它们转换为Joda-Time对象。在内部,Calendar对象有一个long
整数,跟踪自UTC 1970年第一个时刻以来的毫秒数。我们可以提取该数字,并将其提供给Joda-Time。我们还需要指定我们打算确定日期的期望时区。
long startMillis = myStartCalendar.getTimeInMillis();
DateTime startDateTime = new DateTime( startMillis , zone );
long stopMillis = myStopCalendar.getTimeInMillis();
DateTime stopDateTime = new DateTime( stopMillis , zone );
从DateTime对象转换为LocalDate。
LocalDate start = startDateTime.toLocalDate();
LocalDate stop = stopDateTime.toLocalDate();
现在进行我们之前看到的相同的计算。
int elapsed = Days.daysBetween ( start , stop ).getDays ();
扩展到@ 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));
}
这是我使用旧的Calendar对象的解决方案:
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;
}
这假定d0晚于d1。如果不能保证,您可以随时测试并交换它们。
基本原则是采取每一天的年份之间的差异。如果他们在同一年,那就是它。
但他们可能会有不同的年份。所以我在它们之间的所有年份循环,增加了一年中的天数。请注意,getActualMaximum在闰年中返回366,在非闰年中返回365。这就是为什么我们需要一个循环,你不能只将这些年份之间的差异乘以365,因为那里可能有一个闰年。 (我的第一个草稿使用了getMaximum,但这不起作用,因为无论年份如何,它都会返回366.getMaximum是任何一年的最大值,而不是特定年份。)
由于此代码不对一天中的小时数做出假设,因此不会被夏令时愚弄。
我有https://stackoverflow.com/a/31800947/3845798上面给出的类似(不完全相同)的方法。
并且已经围绕api编写了测试用例,对我来说,如果我在2017年3月8日通过 - 作为开始日期和2017年8月8日作为结束日期,它就失败了。
您可以在几天之内看到差异。因此,我对我的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;}
我没有使用导致我一些问题的TimeUnit.MILLISECONDS.toDays。
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)
}
Answer by Mohamed Anees A在几个小时内是正确的但是错了几天。计算天数需要time zone。另一个答案使用Instant
,它是UTC中的一个时刻,始终是UTC。因此,您没有获得正确的日历天数。
要按日历计算天数,请将您的遗产Calendar
转换为ZonedDateTime
,然后输入ChronoUnit.DAYS.between
。
时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因地区而异。例如,在Paris France午夜过后几分钟是新的一天,而在Montréal Québec仍然是“昨天”。
如果未指定时区,则JVM会隐式应用其当前的默认时区。在运行时(!)期间,该默认值可能是change at any moment,因此您的结果可能会有所不同。最好明确指定您期望/预期的时区作为参数。如果关键,请与您的用户确认该区域。
以proper time zone name的格式指定Continent/Region
,例如America/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用2-4字母缩写,如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).
如果要使用JVM的当前默认时区,请求它并作为参数传递。如果省略,代码变得模糊不清,因为我们不确定您是否打算使用默认值,或者如果您像许多程序员一样,不知道这个问题。
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
GregorianCalendar
到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.
}
生成的ZonedDateTime
对象携带时区的ZoneId
对象。使用该区域,您可以计算过去的日历天数。
要计算以年 - 月 - 天为单位的经过时间,请使用Period
类。
Period p = Period.between( zdtStart , zdtStop ) ;
如果你想要总天数作为经过的时间,use ChronoUnit
。
long days = ChronoUnit.DAYS.between( zdtStart , zdtStop ) ;
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,如java.util.Date
,Calendar
和SimpleDateFormat
。
要了解更多信息,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规格是JSR 310。
现在在Joda-Time的maintenance mode项目建议迁移到java.time班。
您可以直接与数据库交换java.time对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展了java.time。该项目是未来可能添加到java.time的试验场。你可能会在这里找到一些有用的类,如Interval
,YearWeek
,YearQuarter
和more。