如何获取两个日历实例之间的天数?

时间:2013-10-19 05:58:22

标签: java android date calendar

如果有日期更改,我想找到两个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));  
} 

但是它不起作用,因为它只会在天数差异之间给出差异,如果有月份差异则毫无价值。

11 个答案:

答案 0 :(得分:27)

尝试以下方法:

public static long daysBetween(Calendar startDate, Calendar endDate) {
    long end = endDate.getTimeInMillis();
    long start = startDate.getTimeInMillis();
    return TimeUnit.MILLISECONDS.toDays(Math.abs(end - start));
}

答案 1 :(得分:20)

在Java 8及更高版本中,我们可以简单地使用java.time类。

hoursBetween = ChronoUnit.HOURS.between(calendarObj.toInstant(), calendarObj.toInstant());

daysBetween = ChronoUnit.DAYS.between(calendarObj.toInstant(), calendarObj.toInstant());

答案 2 :(得分:8)

此函数计算两个日历之间的天数,作为它们之间的月份日历天数,这是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()));
}

答案 3 :(得分:3)

更新现在位于Joda-Timemaintenance 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 ();

最后的getDaysint返回的Days对象中提取纯daysBetween个数字。

转储到控制台。

System.out.println ( "today: " + today + " to weekLater: " + weekLater + " is days: " + days );
  

今天:2015-12-22至周时间:2015-12-29是天:7

您有日历对象。我们需要将它们转换为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 ();

答案 4 :(得分:2)

Extension to @JK1 great answer :

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));
}

答案 5 :(得分:0)

我有https://stackoverflow.com/a/31800947/3845798给出的类似(不完全相同)的方法。

并且已经围绕api编写了测试用例,对我来说,如果我通过它就失败了 2017年3月8日 - 作为开始日期和2017年8月8日作为结束日期。

有几天你会在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;}

我没有使用导致我出现问题的TimeUnit.MILLISECONDS.toDays。

答案 6 :(得分:0)

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)
    }

答案 7 :(得分:0)

java.time

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,因此您的结果可能会有所不同。最好将您的期望/期望时区明确指定为参数。如果紧急,请与您的用户确认区域。

Continent/Region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用2-4个字母的缩写,例如ESTIST,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

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 ) ;

如果您希望将天数作为经过的时间,请e ChronoUnit

 long days = ChronoUnit.DAYS.between( zdtStart , zdtStop ) ;

Table of types of date-time classes in modern java.time versus legacy


关于 java.time

java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

目前位于Joda-Timemaintenance mode项目建议迁移到java.time类。

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

在哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

答案 8 :(得分:0)

这是我使用好的老式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在leap年返回366,在非-年返回365。这就是为什么我们需要一个循环,您不能仅仅将年份之间的差值乘以365,因为那里可能会有a年。 (我的初稿使用的是getMaximum,但是不起作用,因为无论年份如何,它都会返回366。getMaximum是任何年份(而不是该特定年份)的最大值。)

由于此代码不假设一天中的小时数,因此不会被夏令时所迷惑。

答案 9 :(得分:0)

如果您的项目不支持新的Java 8类(作为选定的答案),则可以添加此方法来计算天数,而不受时区或其他事实的影响。

它不如其他方法快(时间复杂度更高),但是它是可靠的,无论如何,日期比较很少超过几百年或数千年。

(Kotlin)

/**
     * Returns the number of DAYS between two dates. Days are counted as calendar days
     * so that tomorrow (from today date reference) will be 1 ,  the day after 2 and so on
     * independent on the hour of the day.
     *
     * @param date - reference date, normally Today
     * @param selectedDate - date on the future
     */
fun getDaysBetween(date: Date, selectedDate: Date): Int {

            val d = initCalendar(date)
            val s = initCalendar(selectedDate)
            val yd = d.get(Calendar.YEAR)
            val ys = s.get(Calendar.YEAR)

            if (ys == yd) {
                return s.get(Calendar.DAY_OF_YEAR) - d.get(Calendar.DAY_OF_YEAR)
            }

            //greater year
            if (ys > yd) {
                val endOfYear = Calendar.getInstance()
                endOfYear.set(yd, Calendar.DECEMBER, 31)
                var daysToFinish = endOfYear.get(Calendar.DAY_OF_YEAR) - d.get(Calendar.DAY_OF_YEAR)
                while (endOfYear.get(Calendar.YEAR) < s.get(Calendar.YEAR)-1) {
                    endOfYear.add(Calendar.YEAR, 1)
                    daysToFinish += endOfYear.get(Calendar.DAY_OF_YEAR)
                }
                return daysToFinish + s.get(Calendar.DAY_OF_YEAR)
            }

            //past year
            else {
                val endOfYear = Calendar.getInstance()
                endOfYear.set(ys, Calendar.DECEMBER, 31)
                var daysToFinish = endOfYear.get(Calendar.DAY_OF_YEAR) - s.get(Calendar.DAY_OF_YEAR)
                while (endOfYear.get(Calendar.YEAR) < d.get(Calendar.YEAR)-1) {
                    endOfYear.add(Calendar.YEAR, 1)
                    daysToFinish += endOfYear.get(Calendar.DAY_OF_YEAR)
                }
                return daysToFinish + d.get(Calendar.DAY_OF_YEAR)
            }
        }

单元测试,您可以改进它们,我不需要负面的日子,所以我没有做太多的测试:

@Test
    fun `Test days between on today and following days`() {
        val future = Calendar.getInstance()
        calendar.set(2019, Calendar.AUGUST, 26)

        future.set(2019, Calendar.AUGUST, 26)
        Assert.assertEquals(0, manager.getDaysBetween(calendar.time, future.time))

        future.set(2019, Calendar.AUGUST, 27)
        Assert.assertEquals(1, manager.getDaysBetween(calendar.time, future.time))

        future.set(2019, Calendar.SEPTEMBER, 1)
        Assert.assertEquals(6, manager.getDaysBetween(calendar.time, future.time))

        future.set(2020, Calendar.AUGUST, 26)
        Assert.assertEquals(366, manager.getDaysBetween(calendar.time, future.time)) //leap year

        future.set(2022, Calendar.AUGUST, 26)
        Assert.assertEquals(1096, manager.getDaysBetween(calendar.time, future.time))

        calendar.set(2019, Calendar.DECEMBER, 31)
        future.set(2020, Calendar.JANUARY, 1)
        Assert.assertEquals(1, manager.getDaysBetween(calendar.time, future.time))
    }

    @Test
    fun `Test days between on previous days`() {
        val future = Calendar.getInstance()
        calendar.set(2019, Calendar.AUGUST, 26)

        future.set(2019,Calendar.AUGUST,25)
        Assert.assertEquals(-1, manager.getDaysBetween(calendar.time, future.time))
    }

    @Test
    fun `Test days between hour doesn't matter`() {
        val future = Calendar.getInstance()
        calendar.set(2019, Calendar.AUGUST, 26,9,31,15)

        future.set(2019,Calendar.AUGUST,28, 7,0,0)
        Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))

        future.set(2019,Calendar.AUGUST,28, 9,31,15)
        Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))

        future.set(2019,Calendar.AUGUST,28, 23,59,59)
        Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))
    }

    @Test
    fun `Test days between with time saving change`() {
        val future = Calendar.getInstance()
        calendar.set(2019, Calendar.OCTOBER, 28)

        future.set(2019, Calendar.OCTOBER,29)
        Assert.assertEquals(1, manager.getDaysBetween(calendar.time, future.time))

        future.set(2019, Calendar.OCTOBER,30)
        Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))
    }

答案 10 :(得分:-4)

Calendar day1 = Calendar.getInstance();

Calendar day2 = Calendar.getInstance();

int diff = day1.get(Calendar.DAY_OF_YEAR) - day2.get(Calendar.DAY_OF_YEAR);