How to get number of days between two calendar instance?

北战南征 提交于 2020-01-20 15:30:56

问题


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.

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.


回答1:


In Java 8 and later, we could simply use the java.time classes.

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

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



回答2:


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



回答3:


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.

For example, my function will compute 1 day's difference between a Calendar on January 1, 11:59PM and January 2, 12:01AM.

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



回答4:


UPDATE The Joda-Time project, now in maintenance mode, advises migration to the java.time classes. See the Answer by Anees A for the calculation of elapsed hours, and see my new Answer for using java.time to calculate elapsed days with respect for the calendar.

Joda-Time

The old java.util.Date/.Calendar classes are notoriously troublesome and should be avoided.

Instead use the Joda-Time library. Unless you have Java 8 technology in which case use its successor, the built-in java.time framework (not in Android as of 2015).

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.

While lacking a time zone, note that time zone is crucial in 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.

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

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.

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.

LocalDate start = startDateTime.toLocalDate();
LocalDate stop = stopDateTime.toLocalDate();

Now do the same elapsed calculation we saw earlier.

int elapsed = Days.daysBetween ( start , stop ).getDays ();



回答5:


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



回答6:


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.

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

As this code makes no assumptions about the number of hours in a day, it is not fooled by daylight savings time.




回答7:


java.time

The Answer by Mohamed Anees A is 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 not getting the correct number of calendar days elapsed.

To count days by the calendar, convert your legacy Calendar to a ZonedDateTime, then feed to 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 France is 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 moment during 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.

Specify a proper time zone name in 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 not true time zones, not standardized, and not even unique(!).

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.

ZoneId z = ZoneId.systemDefault() ;              // Get JVM’s current default time zone.

Convert from GregorianCalendar to 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 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.

Calculate elapsed days

To calculate the elapsed time in terms of years-months-days, use Period class.

Period p = Period.between( zdtStart , zdtStop ) ;

If you want total number of days as the elapsed time, use ChronoUnit.

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


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as 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.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

  • 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 6 and Java SE 7
    • Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
  • Android
    • Later versions of Android bundle implementations of the java.time classes.
    • For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….

The ThreeTen-Extra project 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.




回答8:


I have the similar (not exact same) approach given above by 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.

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

   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.




回答9:


Kotlin solution, purely relies on Calendar. At the end gives exact number of days difference. Inspired by @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)
    }



回答10:


If your project doesn't support new Java 8 classes (as selected answer), you can add this method to calculate the days without being influenced by timezones or other facts.

It is not as fast (greater time complexity) as other methods but it's reliable, anyways date comparisons are rarely larger than hundreds or thousands of years.

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

Unit Tests, you can improve them I didn't need the negative days so I didn't test that as much:

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



回答11:


Calendar day1 = Calendar.getInstance();

Calendar day2 = Calendar.getInstance();

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

来源:https://stackoverflow.com/questions/19462912/how-to-get-number-of-days-between-two-calendar-instance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!