【问题标题】:Difference in days between two dates in Java?Java中两个日期之间的天数差异?
【发布时间】:2011-03-19 00:24:25
【问题描述】:

我需要找到两个日期之间的天数:一个来自报告,一个来自当前日期。我的sn-p:

  int age=calculateDifference(agingDate, today);

这里calculateDifference 是私有方法,agingDatetodayDate 对象,仅供您澄清。我关注了 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 库的帮助下完成了this problem

【问题讨论】:

  • 异常差值是什么意思?请你说得更清楚些,或者举个例子?
  • 能贴出calculateDifference方法的代码吗?
  • 仅供参考,麻烦的旧日期时间类,如 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat 现在是 legacy,被 java.time 类取代。见Tutorial by Oracle。同样,Joda-Time 项目现在位于maintenance mode,团队建议迁移到 java.time 类。

标签: java jodatime datediff


【解决方案1】:

我建议您使用出色的 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

【讨论】:

【解决方案2】:

我加入游戏可能为时已晚,但这是怎么回事啊? :)

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

我们可以改变你的代码来做一些简单的事情吗:

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

【讨论】:

  • 这段代码不关注夏令时,所以天数差异的结果可能不正确。
  • @Johanna 虽然已经很晚了,但是当失败时你能举个例子吗?我已经尝试了很多,但找不到任何失败的日期范围。谢谢。
  • 只有当您所在的时区(您的本地时区设置)使用夏令时,例如中欧时间、柏林、巴黎或阿姆斯​​特丹时,才会发生此错误。夏令时的开始日和结束日没有 24 小时,例如 2014 年 3 月 30 日只有 23 小时,而 2014 年 10 月 26 日将有 25 小时。如果较早的日期在 3 月 30 日 2:00 之前,而较晚的日期在 3 月 30 日 3:00 之后,则计算失败。
【解决方案3】:

diff / (24 * etc) 不考虑时区,因此如果您的默认时区中包含 DST,它可能会导致计算中断。

这个link 有一个不错的小实现。

如果链接断开,这里是上述链接的来源:

/** 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;  
}

【讨论】:

  • 第二种方法不对,最后的while应该是。 while (cursor.getTimeInMillis() &lt;= endInstant); 否则,如果不到一天,你会得到一个无限循环。
  • 来自链接中的评论“您应该知道,给定的算法可能比您预期的多一天。它给出 1 作为 2009-02-28 19:00 之间的天数:00 和 2009-02-28 19:00:01。”
【解决方案4】:

java.time

在 Java 8 及更高版本中,使用 java.time framework (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

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

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

【讨论】:

  • 还要确保以正确的顺序将日期传递给 ChronoUnit.DAYS.between,否则它将返回 -ve 结果
【解决方案5】:
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;
    }
}

【讨论】:

  • 循环不是好主意。当有更简单的选项可以做同样的事情时,性能如何?
  • 性能改进是按 2 的幂递增,然后当条件 date.before(endDate) 为 false 时,返回上一次迭代并将递增器重置为 1。如果你还有 10 亿天,你可能会做 30 次迭代而不是 10 亿次。您还可以通过检查毫秒来进行猜测来提高性能,但这可能不太优雅。
【解决方案6】:

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

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

【讨论】:

  • 这段代码不关注夏令时,所以结果可能不正确。
  • @Johanna 此解决方案适用于 DST。当除法后使用round时,忽略这个差异,结果是ok的。
  • @angelcervera 大多数情况下它是正确的,但如果它接近,额外的一小时可能会使准确性减少 +/- 1 天(即它会以错误的方式舍入)。跨度>
  • @Muhd 是的,你是对的。第三行必须是:long days = Math.round((day2 - day1) / 86400000D);除数是双精度值非常重要。
  • 对不起。今天早上我忘记将一年级的数学模块插入我的大脑。我在想减去一个负数会抛出计算。我们应该能够对 cme​​ts 投反对票。
【解决方案7】:

使用毫秒时间差的解决方案,正确舍入 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 日期,

【讨论】:

  • @marcolopes 否,返回 32。 System.out.println(daysDiff(new Date(2014, 2, 1), new Date(2014, 3, 2)));小心,因为一月是 0。
【解决方案8】:

问题说明:(我的代码是以周为单位计算增量,但同样的问题也适用于以天为单位的增量)

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

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 EST 2009 = 1,235,365,200,000
MillisPerWeek = 604,800,000
因此,
(Mar09 - Feb23) / MillisPerWeek =
1,206,000,000 / 604,800,000 = 1.994...

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

【讨论】:

  • 通知 EDTEST。您正在查看夏令时。一周 168 小时(春季“增加”一小时 +1)* 两周,加上额外的 DST 小时,你有:335 / 168 = 1.9940476190。
【解决方案9】:

我使用这个函数:

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

【讨论】:

    【解决方案10】:

    查看这个 apache commons-lang 类 DateUtils 中的 getFragmentInDays 方法。

    【讨论】:

      【解决方案11】:

      根据@Mad_Troll 的回答,我开发了这个方法。

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

      示例:如果您通过 now & now + 1 毫秒,仍然是同一天。 执行 1-1-13 23:59:59.0981-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。正如我在答案顶部所述。将小时/分钟/秒/毫秒展平,您会发现它会正确计数。
      【解决方案12】:

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

      【讨论】:

        【解决方案13】:

        这个基本功能一百行代码???

        只是一个简单的方法:

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

        【讨论】:

        • 忽略时区。忽略夏令时和其他异常情况。忽略部分天数的舍入。
        【解决方案14】:
        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;
        }
        

        【讨论】:

          【解决方案15】:

          三十加分

          Answer by Vitalii Fedorenko 是正确的,它描述了如何使用 Java 8 及更高版本(以及 back-ported to Java 6 & 7to Android)内置的 java.time 类(DurationChronoUnit)以现代方式执行此计算.

          Days

          如果您在代码中经常使用天数,则可以使用类替换单纯的整数。 Days 类可以在 ThreeTen-Extra 项目中找到,它是 java.time 的扩展,也是 java.time 未来可能添加的试验场。 Days 类提供了一种类型安全的方式来表示应用程序中的天数。该类包括ZEROONE 的方便常量。

          鉴于问题中旧的过时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.between 的包装器,请阅读 ChronoUnit 类文档了解详细信息。需要说明的是:所有大写字母 DAYS 都在枚举 ChronoUnit 中,而 initial-cap Days 是 ThreeTen-Extra 中的一个类。

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

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

          String output = days.toString();
          

          P3D

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

          【讨论】:

            【解决方案16】:

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

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

            【讨论】:

              【解决方案17】:

              如果您正在寻找能够返回正确数字或天数的解决方案,例如11/30/2014 23:5912/01/2014 00:01 这是使用 Joda Time 的解决方案。

              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 分钟的差异。

              【讨论】:

                【解决方案18】:

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

                Joda 与 Java 使用日期

                例如,昨天 (dd-mm-yyyy, 12-07-2016) 和 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 库。

                【讨论】:

                • 仅供参考,虽然 Joda-Time 仍受到积极支持,但其开发团队建议迁移到 java.time。引用他们的主页:“Joda-Time 是 Java SE 8 之前 Java 的事实上的标准日期和时间库。现在要求用户迁移到 java.time (JSR-310)。”
                【解决方案19】:

                我是这样做的。这很容易:)

                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;
                

                【讨论】:

                • 对此做一些测试,你很快就会意识到这行不通。从 2013 年 12 月 31 日到 2014 年 1 月 1 日,这是一天的差异吧?你的计算会做,1 - 365 = -364。这肯定是不正确的。
                猜你喜欢
                • 2011-03-20
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-07-24
                • 2010-12-09
                • 1970-01-01
                • 2017-10-24
                • 1970-01-01
                相关资源
                最近更新 更多