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

问题描述 投票:30回答:10

如果有日期变化,我想在两天内找到两个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 android date calendar
10个回答
22
投票

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

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

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

-4
投票

Calendar day1 = Calendar.getInstance();

Calendar day2 = Calendar.getInstance();

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

30
投票

尝试以下方法:

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

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
投票

更新Joda-Time项目,现在在maintenance mode,建议迁移到java.time班。请参阅Anees A的the Answer计算经过的小时数,并查看我的new Answer,了解如何使用java.time计算日历的经过天数。

Joda-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

你有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 ();

2
投票

扩展到@ 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));
}

1
投票

这是我使用旧的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是任何一年的最大值,而不是特定年份。)

由于此代码不对一天中的小时数做出假设,因此不会被夏令时愚弄。


0
投票

我有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。


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

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

proper time zone name的格式指定Continent/Region,例如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.

转换GregorianCalendarZonedDateTime

可怕的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 ) ;

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


About 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

© www.soinside.com 2019 - 2024. All rights reserved.