How to get number of days between two calendar instance?

后端 未结 11 1088
孤城傲影
孤城傲影 2020-12-05 01:58

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

相关标签:
11条回答
  • 2020-12-05 02:32

    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.

    0 讨论(0)
  • 2020-12-05 02:32

    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.

    0 讨论(0)
  • 2020-12-05 02:32

    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)
        }
    
    0 讨论(0)
  • 2020-12-05 02:33

    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()));
    }
    
    0 讨论(0)
  • 2020-12-05 02:33

    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))
        }
    
    0 讨论(0)
  • 2020-12-05 02:33

    Calendar day1 = Calendar.getInstance();

    Calendar day2 = Calendar.getInstance();

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

    0 讨论(0)
提交回复
热议问题