Java中两个日期之间的天数差异?


81

我需要找到两个日期之间的天数:一个是来自报表,另一个是当前日期。我的片段:

  int age=calculateDifference(agingDate, today);

calculateDifference是一个私有方法,agingDate并且todayDate对象,仅供您说明。我关注了Java论坛中的两篇文章Thread 1 / Thread 2

它在独立程序中可以正常工作,尽管当我将其包含在逻辑中以从报告中读取时,我会在值上获得不同寻常的区别。

为什么会发生,如何解决?

编辑:

与实际天数相比,我得到的天数更多。

public static int calculateDifference(Date a, Date b)
{
    int tempDifference = 0;
    int difference = 0;
    Calendar earlier = Calendar.getInstance();
    Calendar later = Calendar.getInstance();

    if (a.compareTo(b) < 0)
    {
        earlier.setTime(a);
        later.setTime(b);
    }
    else
    {
        earlier.setTime(b);
        later.setTime(a);
    }

    while (earlier.get(Calendar.YEAR) != later.get(Calendar.YEAR))
    {
        tempDifference = 365 * (later.get(Calendar.YEAR) - earlier.get(Calendar.YEAR));
        difference += tempDifference;

        earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
    }

    if (earlier.get(Calendar.DAY_OF_YEAR) != later.get(Calendar.DAY_OF_YEAR))
    {
        tempDifference = later.get(Calendar.DAY_OF_YEAR) - earlier.get(Calendar.DAY_OF_YEAR);
        difference += tempDifference;

        earlier.add(Calendar.DAY_OF_YEAR, tempDifference);
    }

    return difference;
}

注意 :

不幸的是,没有任何答案可以帮助我解决问题。我已经在Joda-time库的帮助下解决这个问题


3
您所说的异常差值是什么意思?您能说得更清楚还是举个例子?
Marcel Gheorghita 2010年

1
您可以发布calculateDifference方法的代码吗?
Eugene Ryzhikov 2010年

仅供参考,麻烦的旧日期,时间类,如java.util.Datejava.util.Calendarjava.text.SimpleDateFormat现在的遗产,由取代java.time类。请参见Oracle教程。同样,Joda-Time项目现在处于维护模式,团队建议迁移到java.time类。
罗勒·布尔克

Answers:


149

我建议您使用出色的Joda Time库,而不要使用有缺陷的java.util.Date和朋友。你可以简单地写

import java.util.Date;
import org.joda.time.DateTime;
import org.joda.time.Days;

Date past = new Date(110, 5, 20); // June 20th, 2010
Date today = new Date(110, 6, 24); // July 24th 
int days = Days.daysBetween(new DateTime(past), new DateTime(today)).getDays(); // => 34

3
joda-time.sourceforge.net/faq.html#datediff-我打算提出同样的建议。
Nebril

2
jodaInstance =新的DateTime(jdkDate); 关于joda time与java.util.Date和朋友之间的转换,请参阅joda-time.sourceforge.net/userguide.html#JDK_Interoperability
Adam Schmideg 2010年

2
@ven编码器-我认为您可以将其发布为有关使用joda时间的单独问题。您还可以提供一个代码片段,以给出看似错误的结果。
亚当·施密德

2
@ven coder-我更新了示例代码,它对我有用。我看到这个问题得到了回答。在此处或在另一个问题中显示不适合您的代码。
亚当·施密德

1
仅供参考,Joda-Time项目现在处于维护模式,团队建议迁移到java.time类。请参见Oracle教程
罗勒·布尔克

48

我加入游戏可能为时已晚,但是到底是什么呢?:)

您是否认为这是线程问题?例如,如何使用此方法的输出?要么

我们可以更改您的代码来执行以下操作吗?

Calendar calendar1 = Calendar.getInstance();
    Calendar calendar2 = Calendar.getInstance();
    calendar1.set(<your earlier date>);
    calendar2.set(<your current date>);
    long milliseconds1 = calendar1.getTimeInMillis();
    long milliseconds2 = calendar2.getTimeInMillis();
    long diff = milliseconds2 - milliseconds1;
    long diffSeconds = diff / 1000;
    long diffMinutes = diff / (60 * 1000);
    long diffHours = diff / (60 * 60 * 1000);
    long diffDays = diff / (24 * 60 * 60 * 1000);
    System.out.println("\nThe Date Different Example");
    System.out.println("Time in milliseconds: " + diff
 + " milliseconds.");
    System.out.println("Time in seconds: " + diffSeconds
 + " seconds.");
    System.out.println("Time in minutes: " + diffMinutes 
+ " minutes.");
    System.out.println("Time in hours: " + diffHours 
+ " hours.");
    System.out.println("Time in days: " + diffDays 
+ " days.");
  }

18
该代码没有注意夏令时,因此以天为单位的差异结果可能不正确。
约翰娜

@Johanna虽然已经很晚了,但是如果失败了,您能举个例子吗?我已经尝试了很多,但是找不到任何失败的日期范围。谢谢。
2014年

4
仅当您处于使用夏时制的时区(您当地的时区设置)时,才会发生此错误,例如中欧时间,柏林,巴黎或阿姆斯​​特丹。夏令时的开始和结束时间没有24小时,例如2014年3月30日只有23小时,而2014年10月26日将只有25小时。如果较早的日期在3月30日2:00之前,而较晚的日期在3月30日3:00之后,则计算将失败。
约翰娜

23

diff /(24 *等)不考虑时区,因此,如果您的默认时区中包含DST,则可以取消计算。

这个链接有一个很好的实现。

如果链接断开,以下是上述链接的来源:

/** Using Calendar - THE CORRECT WAY**/  
public static long daysBetween(Calendar startDate, Calendar endDate) {  
  //assert: startDate must be before endDate  
  Calendar date = (Calendar) startDate.clone();  
  long daysBetween = 0;  
  while (date.before(endDate)) {  
    date.add(Calendar.DAY_OF_MONTH, 1);  
    daysBetween++;  
  }  
  return daysBetween;  
}  

/** Using Calendar - THE CORRECT (& Faster) WAY**/  
public static long daysBetween(final Calendar startDate, final Calendar endDate)
{
  //assert: startDate must be before endDate  
  int MILLIS_IN_DAY = 1000 * 60 * 60 * 24;  
  long endInstant = endDate.getTimeInMillis();  
  int presumedDays = 
    (int) ((endInstant - startDate.getTimeInMillis()) / MILLIS_IN_DAY);  
  Calendar cursor = (Calendar) startDate.clone();  
  cursor.add(Calendar.DAY_OF_YEAR, presumedDays);  
  long instant = cursor.getTimeInMillis();  
  if (instant == endInstant)  
    return presumedDays;

  final int step = instant < endInstant ? 1 : -1;  
  do {  
    cursor.add(Calendar.DAY_OF_MONTH, step);  
    presumedDays += step;  
  } while (cursor.getTimeInMillis() != endInstant);  
  return presumedDays;  
}

4
第二种方法是错误的,最后while应该是。while (cursor.getTimeInMillis() <= endInstant); 否则,如果不到一天,您将陷入无限循环。
克里斯·詹金斯(Chris.Jenkins)2013年

通过链接中的注释“您应注意,给出的算法可能会比您预期的多'一天'。它给出1表示2009-02-28 19:00:00和2009-02-28之间的天数19:00:01。”
Zyoo 2015年

16

java.time

在Java 8和更高版本中,请使用java.time框架Tutorial)。

Duration

所述Duration类表示一时间跨度为秒数加的分数秒。它可以数天,小时,分钟和秒。

ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime oldDate = now.minusDays(1).minusMinutes(10);
Duration duration = Duration.between(oldDate, now);
System.out.println(duration.toDays());

ChronoUnit

如果您只需要天数,则可以使用enum。请注意,计算方法返回而不是。ChronoUnit longint

long days = ChronoUnit.DAYS.between( then, now );

还要确保将日期以正确的顺序传递给ChronoUnit.DAYS.between,否则它将返回-ve结果
$$

13
import java.util.Calendar;
import java.util.Date;

public class Main {
    public static long calculateDays(String startDate, String endDate)
    {
        Date sDate = new Date(startDate);
        Date eDate = new Date(endDate);
        Calendar cal3 = Calendar.getInstance();
        cal3.setTime(sDate);
        Calendar cal4 = Calendar.getInstance();
        cal4.setTime(eDate);
        return daysBetween(cal3, cal4);
    }

    public static void main(String[] args) {
        System.out.println(calculateDays("2012/03/31", "2012/06/17"));

    }

    /** Using Calendar - THE CORRECT WAY**/
    public static long daysBetween(Calendar startDate, Calendar endDate) {
        Calendar date = (Calendar) startDate.clone();
        long daysBetween = 0;
        while (date.before(endDate)) {
            date.add(Calendar.DAY_OF_MONTH, 1);
            daysBetween++;
        }
        return daysBetween;
    }
}

1
循环不是一个好主意。如果有更多简单的选项可以实现相同的性能呢?
angelcervera

1
一个性能改进将是增加2的幂,然后当条件date.before(endDate)为false时,返回到上一个迭代并将递增器重置为1。也就是说,如果您距离十亿天,可能会做30次迭代,而不是十亿次。您还可以通过检查毫秒数来猜测性能,从而提高性能,但这可能不太优雅。
Muhd

12

这取决于您定义的差异。可以在午夜比较两个日期。

long day1 = ...; // in milliseconds.
long day2 = ...; // in milliseconds.
long days = (day2 - day1) / 86400000;

9
该代码不注意夏令时,因此结果可能不正确。
约翰娜

@Johanna此解决方案与DST一起使用。当除法后应用回合时,此差异将被忽略并且结果可以。
angelcervera

1
@angelcervera在大多数情况下,它是正确的,但是如果很接近,那额外的一小时可能会使精度提高+/- 1天(即,走错了方向)。
Muhd

3
@Muhd是的,您是对的。第三行必须是:long days = Math.round((day2-day1)/ 86400000D); 除数是双精度值非常重要。
angelcervera

3
抱歉。我今天早上忘记将一年级数学模块插入大脑。我当时想减去负数会导致计算失败。我们应该能够对评论投反对票。
Snekse

9

解决方案使用毫秒之间的时间差,并为DST日期正确舍入:

public static long daysDiff(Date from, Date to) {
    return daysDiff(from.getTime(), to.getTime());
}

public static long daysDiff(long from, long to) {
    return Math.round( (to - from) / 86400000D ); // 1000 * 60 * 60 * 24
}

注意事项:当然,日期必须在某个时区。

重要代码:

Math.round( (to - from) / 86400000D )

如果您不想四舍五入,可以使用UTC日期,


1
@marcolopes否,返回32。System.out.println(daysDiff(new Date(2014,2,1),new Date(2014,3,2))); 要小心,因为一月是0
angelcervera

4

问题的说明:(我的代码在几周内计算增量,但同一问题在几天内适用于增量)

这是一个看起来很合理的实现:

public static final long MILLIS_PER_WEEK = 7L * 24L * 60L * 60L * 1000L;

static public int getDeltaInWeeks(Date latterDate, Date earlierDate) {
    long deltaInMillis = latterDate.getTime() - earlierDate.getTime();
    int deltaInWeeks = (int)(deltaInMillis / MILLIS_PER_WEEK);
    return deltaInWeeks; 
}

但是此测试将失败:

public void testGetDeltaInWeeks() {
    delta = AggregatedData.getDeltaInWeeks(dateMar09, dateFeb23);
    assertEquals("weeks between Feb23 and Mar09", 2, delta);
}

原因是:

2009年3月9日星期一00:00:00 EDT = 1,236,571,200,000美国东部时间
2月23日00:00:00美国东部时间2009年= 1,235,365,200,000
MillisPerWeek = 604,800,000
因此
((Mar09-Feb23)/ MillisPerWeek =
1,206,000,000 / 604,800,000 = 1.994 ...

但任何查看日历的人都会同意答案是2。


1
注意EDTEST。您正在寻找夏令时。一周168个小时(春季为“收获”一小时,则为+1)*两周,加上额外的DST小时,您将得到:335/168 = 1.9940476190。
jpswain 2011年

3

我使用此功能:

DATEDIFF("31/01/2016", "01/03/2016") // me return 30 days

我的功能:

import java.util.Date;

public long DATEDIFF(String date1, String date2) {
        long MILLISECS_PER_DAY = 24 * 60 * 60 * 1000;
        long days = 0l;
        SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); // "dd/MM/yyyy HH:mm:ss");

        Date dateIni = null;
        Date dateFin = null;        
        try {       
            dateIni = (Date) format.parse(date1);
            dateFin = (Date) format.parse(date2);
            days = (dateFin.getTime() - dateIni.getTime())/MILLISECS_PER_DAY;                        
        } catch (Exception e) {  e.printStackTrace();  }   

        return days; 
     }


2

基于@Mad_Troll的答案,我开发了此方法。

我已经针对它运行了大约30个测试用例,这是正确处理次日时间段的唯一方法。

示例:如果现在和现在通过+ 1毫秒,那仍然是同一天。这样做1-1-13 23:59:59.098,以1-1-13 23:59:59.099返回0天,正确; 分配此处发布的其他方法将无法正确执行此操作。

值得一提的是,它并不关心您将它们放入哪种方式。如果结束日期早于开始日期,它将倒数。

/**
 * This is not quick but if only doing a few days backwards/forwards then it is very accurate.
 *
 * @param startDate from
 * @param endDate   to
 * @return day count between the two dates, this can be negative if startDate is after endDate
 */
public static long daysBetween(@NotNull final Calendar startDate, @NotNull final Calendar endDate) {

    //Forwards or backwards?
    final boolean forward = startDate.before(endDate);
    // Which direction are we going
    final int multiplier = forward ? 1 : -1;

    // The date we are going to move.
    final Calendar date = (Calendar) startDate.clone();

    // Result
    long daysBetween = 0;

    // Start at millis (then bump up until we go back a day)
    int fieldAccuracy = 4;
    int field;
    int dayBefore, dayAfter;
    while (forward && date.before(endDate) || !forward && endDate.before(date)) {
        // We start moving slowly if no change then we decrease accuracy.
        switch (fieldAccuracy) {
            case 4:
                field = Calendar.MILLISECOND;
                break;
            case 3:
                field = Calendar.SECOND;
                break;
            case 2:
                field = Calendar.MINUTE;
                break;
            case 1:
                field = Calendar.HOUR_OF_DAY;
                break;
            default:
            case 0:
                field = Calendar.DAY_OF_MONTH;
                break;
        }
        // Get the day before we move the time, Change, then get the day after.
        dayBefore = date.get(Calendar.DAY_OF_MONTH);
        date.add(field, multiplier);
        dayAfter = date.get(Calendar.DAY_OF_MONTH);

        // This shifts lining up the dates, one field at a time.
        if (dayBefore == dayAfter && date.get(field) == endDate.get(field))
            fieldAccuracy--;
        // If day has changed after moving at any accuracy level we bump the day counter.
        if (dayBefore != dayAfter) {
            daysBetween += multiplier;
        }
    }
    return daysBetween;
}

您可以删除@NotNull批注,Intellij使用这些批注即时进行代码分析


我考虑了Millis,但该计算器没有考虑,我假设它将数字四舍五入为0。正如我在答案顶部所述。展平您的小时/分钟/秒/毫秒,您会发现它会正确计数。
克里斯·詹金斯(Chris.Jenkins)2014年

1

您说它“在独立程序中可以正常工作”,但是当您“将其包含在我的逻辑中以从报告中读取”时,会得到“异常的差值”。这表明您的报表具有某些无法正常使用的值,而您的独立程序没有这些值。我建议使用一个测试用例,而不是一个独立的程序。从JUnit的TestCase类继承子类,就像编写独立程序一样编写测试用例。现在,您可以运行一个非常具体的示例,了解您期望的值(并且今天不提供测试值,因为今天随着时间而变化)。如果输入在独立程序中使用的值,则测试可能会通过。太好了-您希望这些案件继续进行。现在,从您的报告中添加一个值,工作正常。您的新测试可能会失败。找出失败的原因,进行修复,然后变为绿色(所有测试均通过)。运行报告。看看仍然有什么问题;编写测试;使它通过。很快您就会发现报告正在运行。


1

此基本功能的一百行代码???

只是一个简单的方法:

protected static int calculateDayDifference(Date dateAfter, Date dateBefore){
    return (int)(dateAfter.getTime()-dateBefore.getTime())/(1000 * 60 * 60 * 24); 
    // MILLIS_IN_DAY = 1000 * 60 * 60 * 24;
}

5
忽略时区。忽略夏时制和其他异常。忽略部分天的舍入。
罗勒·布尔克

1
public static int getDifferenceIndays(long timestamp1, long timestamp2) {
    final int SECONDS = 60;
    final int MINUTES = 60;
    final int HOURS = 24;
    final int MILLIES = 1000;
    long temp;
    if (timestamp1 < timestamp2) {
        temp = timestamp1;
        timestamp1 = timestamp2;
        timestamp2 = temp;
    }
    Calendar startDate = Calendar.getInstance(TimeZone.getDefault());
    Calendar endDate = Calendar.getInstance(TimeZone.getDefault());
    endDate.setTimeInMillis(timestamp1);
    startDate.setTimeInMillis(timestamp2);
    if ((timestamp1 - timestamp2) < 1 * HOURS * MINUTES * SECONDS * MILLIES) {
        int day1 = endDate.get(Calendar.DAY_OF_MONTH);
        int day2 = startDate.get(Calendar.DAY_OF_MONTH);
        if (day1 == day2) {
            return 0;
        } else {
            return 1;
        }
    }
    int diffDays = 0;
    startDate.add(Calendar.DAY_OF_MONTH, diffDays);
    while (startDate.before(endDate)) {
        startDate.add(Calendar.DAY_OF_MONTH, 1);
        diffDays++;
    }
    return diffDays;
}

1

三人行

Vitalii FedorenkoAnswer是正确的,它描述了如何使用Java 8和更高版本(以及反向移植到Java 6和7Android)中内置的java.time类(DurationChronoUnit)以现代方式执行此计算。

Days

如果您经常在代码中使用几天,则可以使用类替换纯整数。该Days班可在发现ThreeTen-额外的项目,未来可能增加的java.time java.time的延伸和试验场。本Days类提供代表应用程序中的天数的类型安全的方法。该类包含用于ZERO和的便捷常量ONE

给定问题中的旧过时java.util.Date对象,首先将它们转换为现代java.time.Instant对象。旧的日期时间类具有新添加的方法,以方便转换为java.time,例如java.util.Date::toInstant

Instant start = utilDateStart.toInstant(); // Inclusive.
Instant stop = utilDateStop.toInstant();  // Exclusive.

将两个Instant对象都传递给的工厂方法org.threeten.extra.Days

在当前实现(2016-06)中,这是一个包装器调用java.time.temporal.ChronoUnit.DAYS.betweenChronoUnit有关详细信息,请阅读类doc。需要明确的是:所有大写字母DAYS都在枚举中,ChronoUnit而initial-capDays是ThreeTen-Extra中的一类。

Days days = Days.between( start , stop );

您可以Days在自己的代码中传递这些对象。您可以通过调用序列化为标准ISO 8601格式的字符串toString。这种PnD使用a的格式P来标记开始,并D表示“天”,中间是天数。生成和解析表示日期时间值的字符串时,默认情况下,java.time类和ThreeTen-Extra都使用这些标准格式。

String output = days.toString();

P3D

Days days = Days.parse( "P3D" );  

0

此代码计算两个日期字符串之间的天数:

    static final long MILLI_SECONDS_IN_A_DAY = 1000 * 60 * 60 * 24;
    static final String DATE_FORMAT = "dd-MM-yyyy";
    public long daysBetween(String fromDateStr, String toDateStr) throws ParseException {
    SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
    Date fromDate;
    Date toDate;
    fromDate = format.parse(fromDateStr);
    toDate = format.parse(toDateStr);
    return (toDate.getTime() - fromDate.getTime()) / MILLI_SECONDS_IN_A_DAY;
}

0

如果你正在寻找一个解决方案,返回正确的号码或如之间的天11/30/2014 23:59,并12/01/2014 00:01使用约达时间在这里的解决方案。

private int getDayDifference(long past, long current) {
    DateTime currentDate = new DateTime(current);
    DateTime pastDate = new DateTime(past);
    return currentDate.getDayOfYear() - pastDate.getDayOfYear();
} 

此实现将以1天数的差额返回。此处发布的大多数解决方案都会计算两个日期之间的毫秒差。这意味着0将返回该日期,因为这两个日期之间只有2分钟的时间差。


0

您应该使用Joda时间库,因为Java Util Date有时返回错误的值。

乔达vs Java Util日期

例如,从昨天(dd-mm-yyyy,2016年12月7日)到1957年第一天的第一天(dd-mm-yyyy,01-01-1957)之间的天数:

public class Main {

public static void main(String[] args) {
    SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");

    Date date = null;
    try {
        date = format.parse("12-07-2016");
    } catch (ParseException e) {
        e.printStackTrace();
    }

    //Try with Joda - prints 21742
    System.out.println("This is correct: " + getDaysBetweenDatesWithJodaFromYear1957(date));
    //Try with Java util - prints 21741
    System.out.println("This is not correct: " + getDaysBetweenDatesWithJavaUtilFromYear1957(date));    
}


private static int getDaysBetweenDatesWithJodaFromYear1957(Date date) {
    DateTime jodaDateTime = new DateTime(date);
    DateTimeFormatter formatter = DateTimeFormat.forPattern("dd-MM-yyyy");
    DateTime y1957 = formatter.parseDateTime("01-01-1957");

    return Days.daysBetween(y1957 , jodaDateTime).getDays();
}

private static long getDaysBetweenDatesWithJavaUtilFromYear1957(Date date) {
    SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");

    Date y1957 = null;
    try {
        y1957 = format.parse("01-01-1957");
    } catch (ParseException e) {
        e.printStackTrace();
    }

    return TimeUnit.DAYS.convert(date.getTime() - y1957.getTime(), TimeUnit.MILLISECONDS);
}

因此,我真的建议您使用Joda Time库。


1
仅供参考,尽管仍积极支持Joda-Time,但其开发团队建议迁移到java.time。在其主页上引用:“ Joda-Time是Java SE 8之前的Java的事实上的标准日期和时间库。现在,要求用户迁移到java.time(JSR-310)。”
罗勒·布尔克

-6

我是这样做的。这很简单 :)

Date d1 = jDateChooserFrom.getDate();
Date d2 = jDateChooserTo.getDate();

Calendar day1 = Calendar.getInstance();
day1.setTime(d1);

Calendar day2 = Calendar.getInstance();
day2.setTime(d2);

int from = day1.get(Calendar.DAY_OF_YEAR);
int to = day2.get(Calendar.DAY_OF_YEAR);

int difference = to-from;

4
在此进行一些测试,您将很快意识到这将不起作用。从2013年12月31日到2014年1月1日,那一天有差额吗?您的计算将得出1-365 = -364。哪个当然是不正确的。
克里斯·詹金斯(Chris.Jenkins)2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.