【问题标题】:Calculating the difference between two Java date instances计算两个 Java 日期实例之间的差异
【发布时间】:2018-08-17 07:51:27
【问题描述】:

我在 Scala 中使用 Java 的 java.util.Date 类,想要比较 Date 对象和当前时间。我知道我可以使用 getTime() 计算增量:

(new java.util.Date()).getTime() - oldDate.getTime()

但是,这只会给我留下一个代表毫秒的long。有没有更简单、更好的方法来获得时间增量?

【问题讨论】:

  • 为什么不喜欢joda time?如果您要在 java 中处理日期,这几乎是最好的选择。
  • 请检查我优雅的 2 班轮解决方案,不使用 Joda 并在 stackoverflow.com/a/10650881/82609 的任何 TimeUnit 中给出结果
  • 对所有推荐 Joda time 的人感到羞耻,并且不推荐真正的 Java 答案......
  • @Zizouz212 关于推荐 Joda-Time,与 Java 捆绑的旧日期时间类很糟糕,非常糟糕,设计不佳,令人困惑和麻烦。太糟糕了,Joda-Time 作为他们的替代品获得了巨大的成功。太糟糕了,甚至 Sun/Oracle 都放弃了它们,并采用 java.time 包作为 Java 8 及更高版本的一部分。 java.time 类的灵感来自 Joda-Time。 Joda-Time 和 java.time 都由同一个人领导,Stephen Colbourne。我会说,“任何推荐使用 DateCalendarSimpleDateFormat 的人都感到羞耻”。
  • 当问到这个问题时,Joda Time 可能是一个很好的答案,但今天对于任何可以使用 Java 8 的人来说,最好的答案是使用java.time.Period 和/或Duration。见Basil Bourque’s answer below

标签: java date time timedelta java.util.date


【解决方案1】:

由于日期可以包含小时和分钟,因此最终结果将四舍五入,这将导致值不正确。例如,您计算 今天下午 22:00后天上午 00:00 之间的差值,所以最终结果将是 1,因为实际上它是 1.08 或 smth 差异,然后在调用 TimeUnit.MILLISECONDS.toDays(..) 时会向下取整。这就是为什么你需要考虑到这一点,所以在我的解决方案中,我从一天中的毫秒数中减去剩余的毫秒数。另外,如果你想计算结束日期,你需要+1。

import java.util.Date;
import java.util.concurrent.TimeUnit;

public static long getDaysBetween(Date date1, Date date2, boolean includeEndDate) {
        long millisInDay = 60 * 60 * 24 * 1000;
        long difference = Math.abs(date1.getTime() - date2.getTime());
        long add = millisInDay - (difference % millisInDay);//is used to calculate true number of days, because by default hours, minutes are also counted

        return TimeUnit.MILLISECONDS.toDays(difference + add) + (includeEndDate ? 1 : 0);
    }

测试:

Date date1 = new Date(121, Calendar.NOVEMBER, 27); //2021 Nov 27
Date date2 = new Date(121, Calendar.DECEMBER, 29); //2021 Dec 29
System.out.println( getDaysBetween(date1, date2, false) ); //32 days difference
System.out.println( getDaysBetween(date1, date2, true) ); //33 days difference

【讨论】:

  • 这不太正确,因为一天并不总是 24 小时。没有人应该再使用DateCalendar
  • @OleV.V.一天有几个小时?
  • 这取决于。在哪个时区的哪一天?最常见的是 24。有时是 23 或 25 或不是整数。例如,今年 10 月 31 日在克里斯蒂安斯塔德是 25 小时。
  • 我现在才注意到您正在使用 3-arg Date 构造函数。啊!它已被弃用近 25 年,因为它跨时区运行不可靠。 不要!无论如何,我同意您的观点,即您的代码应该给出 32 天和 33 天。当我运行它时,我得到 33 和 34。你的代码不像你声称的那样工作。
【解决方案2】:

注意:startDate 和 endDates 是 -> java.util.Date

import org.joda.time.Duration;
import org.joda.time.Interval;
// Use .getTime() unless it is a joda DateTime object
Interval interval = new Interval(startDate.getTime(), endDate.getTime());
Duration period = interval.toDuration();
//gives the number of days elapsed between start 
period.getStandardDays();

和结束日期

类似于天,你也可以得到小时、分钟和秒

period.getStandardHours();
period.getStandardMinutes();
period.getStandardSeconds();

【讨论】:

    【解决方案3】:

    如果您想修复跨越夏令时边界的日期范围的问题(例如,一个日期在夏季时间,另一个日期在冬季时间),您可以使用它来获得天数差异

    public static long calculateDifferenceInDays(Date start, Date end, Locale locale) {
        Calendar cal = Calendar.getInstance(locale);
    
        cal.setTime(start);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        long startTime = cal.getTimeInMillis();
    
        cal.setTime(end);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        long endTime = cal.getTimeInMillis();
    
        // calculate the offset if one of the dates is in summer time and the other one in winter time
        TimeZone timezone = cal.getTimeZone();
        int offsetStart = timezone.getOffset(startTime);
        int offsetEnd = timezone.getOffset(endTime);
        int offset = offsetEnd - offsetStart;
    
        return TimeUnit.MILLISECONDS.toDays(endTime - startTime + offset);
    }
    

    【讨论】:

      【解决方案4】:

      看看Joda Time,这是一个改进的 Java 日期/时间 API,应该可以在 Scala 中正常工作。

      【讨论】:

      • 在 Java 8 中被 java.time (JSR-310) 取代
      【解决方案5】:

      不幸的是,JDK Date API 严重损坏。我推荐使用Joda Time library

      Joda Time有时间概念Interval

      Interval interval = new Interval(oldTime, new Instant());
      

      编辑:顺便说一下,Joda 有两个概念:Interval 表示两个时间点之间的时间间隔(表示上午 8 点到 10 点之间的时间),Duration 表示没有实际时间的时间长度时间界限(例如代表两个小时!)

      如果您只关心时间比较,大多数Date 实现(包括JDK 之一)实现了Comparable 接口,它允许您使用Comparable.compareTo()

      【讨论】:

      • 顺便说一句——你的意思是Comparable.compareTo(),而不是Comparable.compare()
      • Joda-Time 有三个类以各种方式描绘时间跨度:Interval、Duration 和 Period。这个正确的答案讨论了前两个。有关 Period 的信息,请参阅 my answer
      • Java 8 有一个新的日期和时间 API,如 joda。
      • Java 8 中的新 java.time 包受到 Joda-Time 的启发,但不是替代品。每个都有其优点和缺点。幸运的是,您不必在它们之间做出选择。只要您小心使用import 语句,就可以充分利用它们的优势。有关 java.time 的示例,请参见 this other answer
      • 仅供参考,Joda-Time 项目现在位于maintenance mode,团队建议迁移到java.time 类。见Tutorial by Oracle
      【解决方案6】:
      Days d = Days.daysBetween(startDate, endDate);
      int days = d.getDays();
      

      https://www.joda.org/joda-time/faq.html#datediff

      【讨论】:

      【解决方案7】:

      简单的差异(没有库)

      /**
       * Get a diff between two dates
       * @param date1 the oldest date
       * @param date2 the newest date
       * @param timeUnit the unit in which you want the diff
       * @return the diff value, in the provided unit
       */
      public static long getDateDiff(Date date1, Date date2, TimeUnit timeUnit) {
          long diffInMillies = date2.getTime() - date1.getTime();
          return timeUnit.convert(diffInMillies,TimeUnit.MILLISECONDS);
      }
      

      然后你能打电话吗:

      getDateDiff(date1,date2,TimeUnit.MINUTES);
      

      以分钟为单位获取 2 个日期的差异。

      TimeUnitjava.util.concurrent.TimeUnit,一个从 nanos 到 days 的标准 Java 枚举。


      人类可读的差异(没有库)

      public static Map<TimeUnit,Long> computeDiff(Date date1, Date date2) {
      
          long diffInMillies = date2.getTime() - date1.getTime();
      
          //create the list
          List<TimeUnit> units = new ArrayList<TimeUnit>(EnumSet.allOf(TimeUnit.class));
          Collections.reverse(units);
      
          //create the result map of TimeUnit and difference
          Map<TimeUnit,Long> result = new LinkedHashMap<TimeUnit,Long>();
          long milliesRest = diffInMillies;
      
          for ( TimeUnit unit : units ) {
      
              //calculate difference in millisecond 
              long diff = unit.convert(milliesRest,TimeUnit.MILLISECONDS);
              long diffInMilliesForUnit = unit.toMillis(diff);
              milliesRest = milliesRest - diffInMilliesForUnit;
      
              //put the result in the map
              result.put(unit,diff);
          }
      
          return result;
      }
      

      http://ideone.com/5dXeu6

      输出类似于Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0},单位已排序。

      您只需将该地图转换为用户友好的字符串。


      警告

      上面的代码 sn-ps 计算两个瞬间之间的简单差异。它可能会在夏令时切换期间导致问题,如this post 中所述。这意味着如果您计算没有时间的日期之间的差异,您可能会丢失日期/小时。

      在我看来,日期差异有点主观,尤其是在日子里。你可以:

      • 计算24小时经过的时间:day+1 - day = 1 day = 24h

      • 计算经过的时间,注意夏令时:day+1 - day = 1 = 24h(但使用午夜时间和夏令时可能是 0 天和 23h)

        李>
      • 计算 day switches 的数量,这意味着天+1 下午 1 点 - 天 11 点 = 1 天,即使经过的时间只有 2 小时(如果有夏令时,则为 1 小时:p)

      如果您对日期差异的定义与第一种情况匹配,我的回答是有效的

      使用 JodaTime

      如果您使用 JodaTime,您可以通过以下方式获得 2 个即时(由 Millies 支持的 ReadableInstant)日期的差异:

      Interval interval = new Interval(oldInstant, new Instant());
      

      但您也可以获取本地日期/时间的差异:

      // returns 4 because of the leap year of 366 days
      new Period(LocalDate.now(), LocalDate.now().plusDays(365*5), PeriodType.years()).getYears() 
      
      // this time it returns 5
      new Period(LocalDate.now(), LocalDate.now().plusDays(365*5+1), PeriodType.years()).getYears() 
      
      // And you can also use these static methods
      Years.yearsBetween(LocalDate.now(), LocalDate.now().plusDays(365*5)).getYears()
      

      【讨论】:

      • 哇,你真的可以教老狗新把戏!以前从未听说过这个,对于将日期差异转换为有意义的字符串非常有用。
      • @SebastienLorber TimeUnit 有没有办法计算差异? "闹钟设置为从现在起 3 天 4 小时 12 分钟"。
      • 很棒的东西确实节省了我很多时间。感谢您的帮助。
      • 我浪费了 3 天的时间来处理 Java Dates,我只是把它扔掉了很多并用这个代替了。谢谢。
      • 这是我上stackoverflow以来见过的最好的答案! :D:D:D
      【解决方案8】:

      在浏览完所有其他答案之后,为了保持 Java 7 Date 类型,但使用 Java 8 diff 方法更加精确/标准,

      public static long daysBetweenDates(Date d1, Date d2) {
          Instant instant1 = d1.toInstant();
          Instant instant2 = d2.toInstant();
          long diff = ChronoUnit.DAYS.between(instant1, instant2);
          return diff;
      }
      

      【讨论】:

        【解决方案9】:

        tl;博士

        将您过时的java.util.Date 对象转换为它们的替代对象java.time.Instant。然后将经过的时间计算为Duration

        Duration d = 
            Duration.between(                   // Calculate the span of time between two moments as a number of hours, minutes, and seconds.
                myJavaUtilDate.toInstant() ,    // Convert legacy class to modern class by calling new method added to the old class.
                Instant.now()                   // Capture the current moment in UTC. About two and a half hours later in this example.
            )
        ;
        

        d.toString(): PT2H34M56S

        d.toMinutes(): 154

        d.toMinutesPart(): 34

        ISO 8601 格式:PnYnMnDTnHnMnS

        明智的标准ISO 8601 将时间跨度的简明文本表示定义为年、月、日、小时等。标准将这样的跨度称为duration。格式为PnYnMnDTnHnMnS,其中P 表示“期间”,T 将日期部分与时间部分分开,中间是数字后跟一个字母。

        例子:

        • P3Y6M4DT12H30M5S
          三年六个月四天十二小时三十分钟五秒
        • PT4H30M
          四个半小时

        java.time

        Java 8 中内置的 java.time 框架取代了麻烦的旧 java.util.Date/java.util.Calendar 类。新类的灵感来自非常成功的Joda-Time 框架,该框架旨在作为其继任者,在概念上相似但经过重新架构。由JSR 310 定义。由ThreeTen-Extra 项目扩展。请参阅Tutorial

        时刻

        Instant 类代表UTC 中时间轴上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

        Instant instant = Instant.now() ;  // Capture current moment in UTC.
        

        最好避免使用遗留类,例如Date/Calendar。但是,如果您必须与尚未更新为 java.time 的旧代码进行互操作,请来回转换。调用添加到旧类的新转换方法。如需从java.util.Date 移动到Instant,请致电Date::toInstant

        Instant instant = myJavaUtilDate.toInstant() ;  // Convert from legacy `java.util.Date` class to modern `java.time.Instant` class.
        

        时间跨度

        java.time 类将这种将时间跨度表示为年、月、日、小时、分钟、秒的想法分成两半:

        • Period 表示年、月、日
        • Duration 表示天、小时、分钟、秒

        这是一个例子。

        ZoneId zoneId = ZoneId.of ( "America/Montreal" );
        ZonedDateTime now = ZonedDateTime.now ( zoneId );
        ZonedDateTime future = now.plusMinutes ( 63 );
        Duration duration = Duration.between ( now , future );
        

        转储到控制台。

        PeriodDuration 都使用 ISO 8601 标准来生成其值的字符串表示形式。

        System.out.println ( "now: " + now + " to future: " + now + " = " + duration );
        

        现在:2015-11-26T00:46:48.016-05:00[美国/蒙特利尔] 到未来:2015-11-26T00:46:48.016-05:00[美国/蒙特利尔] = PT1H3M

        Java 9 向 Duration 添加了方法来获取天部分、小时部分、分钟部分和秒部分。

        您可以获得整个 Duration 中的总天数或小时数或分钟数或秒数或毫秒数或纳秒数。

        long totalHours = duration.toHours();
        

        在 Java 9 中,Duration class gets new methods 用于返回天、小时、分钟、秒、毫秒/纳秒的各个部分。调用to…Part 方法:toDaysPart()toHoursPart() 等。

        ChronoUnit

        如果您只关心更简单的较大时间粒度,例如“经过的天数”,请使用 ChronoUnit 枚举。

        long daysElapsed = ChronoUnit.DAYS.between( earlier , later );
        

        另一个例子。

        Instant now = Instant.now();
        Instant later = now.plus( Duration.ofHours( 2 ) );
        …
        long minutesElapsed = ChronoUnit.MINUTES.between( now , later );
        

        120


        关于java.time

        java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

        Joda-Time 项目现在位于maintenance mode,建议迁移到 java.time。

        要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

        从哪里获得 java.time 类?

        ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可能会在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore


        乔达时间

        更新:Joda-Time 项目现在位于maintenance mode,团队建议迁移到java.time 类。我保留这部分完整的历史记录。

        Joda-Time 库默认使用 ISO 8601。它的Period 类解析并生成这些 PnYnMnDTnHnMnS 字符串。

        DateTime now = DateTime.now(); // Caveat: Ignoring the important issue of time zones.
        Period period = new Period( now, now.plusHours( 4 ).plusMinutes( 30));
        System.out.println( "period: " + period );
        

        渲染:

        period: PT4H30M
        

        【讨论】:

        • 有趣的是,你用“tl; dr”开始了一个与问题无关的文字墙。问题不在于如何从 X 之前或以 X 时间单位获取时间戳,而是如何获取两个日期之间的增量。就是这样。
        • @Buffalo 我不明白你的评论。我的答案涉及PeriodDurationChronoUnit,这三个类的目的是确定时间点之间的增量。请指出我的“文字墙”的哪些部分是不相关的。你说问题要求“日期之间的增量”是不正确的:问题要求Date 对象之间的增量,这是日期时间时刻,而不是“日期”,尽管该类的命名很不幸。跨度>
        • 您的评论中没有任何内容可以处理两个现有的 java.util.Date 对象。我尝试在我的代码中使用各种 sn-ps,但没有发现使用 java.util.Date 对象。
        • @Buffalo 够公平的,很好的批评。我在“tl;dr”部分添加了从java.util.Date 转换为Instant 的代码(只需调用.toInstant)。我添加了Moment 部分来解释。您提到了一对Date 对象,但实际上问题只使用了一个现有的Date 对象然后捕获当前时刻,所以我也这样做了。感谢您的反馈!提示:放弃使用Date - 那些遗留类真的是一团糟。
        【解决方案10】:

        您可以通过多种方式找到日期和时间之间的差异。我知道的最简单的方法之一是:

              Calendar calendar1 = Calendar.getInstance();
              Calendar calendar2 = Calendar.getInstance();
              calendar1.set(2012, 04, 02);
              calendar2.set(2012, 04, 04);
              long milsecs1= calendar1.getTimeInMillis();
              long milsecs2 = calendar2.getTimeInMillis();
              long diff = milsecs2 - milsecs1;
              long dsecs = diff / 1000;
              long dminutes = diff / (60 * 1000);
              long dhours = diff / (60 * 60 * 1000);
              long ddays = diff / (24 * 60 * 60 * 1000);
        
              System.out.println("Your Day Difference="+ddays);
        

        打印语句只是一个示例 - 您可以按照自己喜欢的方式对其进行格式化。

        【讨论】:

        • @manoj kumar bardhan:欢迎来到堆栈溢出:正如您所见,问题和答案已有多年历史。您的答案应该比现有答案更多地添加到问题/答案中。
        • 由于夏令时转换而有 23 或 25 小时的日子呢?
        【解决方案11】:

        由于这里的所有答案都是正确的,但使用的是旧版 java 或 joda 或类似的 3rd 方库,我将放弃另一种方式,在 Java 8 及更高版本中使用新的java.time 类。见Oracle Tutorial

        使用LocalDateChronoUnit

        LocalDate d1 = LocalDate.of(2017, 5, 1);
        LocalDate d2 = LocalDate.of(2017, 5, 18);
        
        long days = ChronoUnit.DAYS.between(d1, d2);
        System.out.println( days );
        

        【讨论】:

          【解决方案12】:

          这是一个在 O(1) 中没有任何依赖关系的正确 Java 7 解决方案。

          public static int countDaysBetween(Date date1, Date date2) {
          
              Calendar c1 = removeTime(from(date1));
              Calendar c2 = removeTime(from(date2));
          
              if (c1.get(YEAR) == c2.get(YEAR)) {
          
                  return Math.abs(c1.get(DAY_OF_YEAR) - c2.get(DAY_OF_YEAR)) + 1;
              }
              // ensure c1 <= c2
              if (c1.get(YEAR) > c2.get(YEAR)) {
                  Calendar c = c1;
                  c1 = c2;
                  c2 = c;
              }
              int y1 = c1.get(YEAR);
              int y2 = c2.get(YEAR);
              int d1 = c1.get(DAY_OF_YEAR);
              int d2 = c2.get(DAY_OF_YEAR);
          
              return d2 + ((y2 - y1) * 365) - d1 + countLeapYearsBetween(y1, y2) + 1;
          }
          
          private static int countLeapYearsBetween(int y1, int y2) {
          
              if (y1 < 1 || y2 < 1) {
                  throw new IllegalArgumentException("Year must be > 0.");
              }
              // ensure y1 <= y2
              if (y1 > y2) {
                  int i = y1;
                  y1 = y2;
                  y2 = i;
              }
          
              int diff = 0;
          
              int firstDivisibleBy4 = y1;
              if (firstDivisibleBy4 % 4 != 0) {
                  firstDivisibleBy4 += 4 - (y1 % 4);
              }
              diff = y2 - firstDivisibleBy4 - 1;
              int divisibleBy4 = diff < 0 ? 0 : diff / 4 + 1;
          
              int firstDivisibleBy100 = y1;
              if (firstDivisibleBy100 % 100 != 0) {
                  firstDivisibleBy100 += 100 - (firstDivisibleBy100 % 100);
              }
              diff = y2 - firstDivisibleBy100 - 1;
              int divisibleBy100 = diff < 0 ? 0 : diff / 100 + 1;
          
              int firstDivisibleBy400 = y1;
              if (firstDivisibleBy400 % 400 != 0) {
                  firstDivisibleBy400 += 400 - (y1 % 400);
              }
              diff = y2 - firstDivisibleBy400 - 1;
              int divisibleBy400 = diff < 0 ? 0 : diff / 400 + 1;
          
              return divisibleBy4 - divisibleBy100 + divisibleBy400;
          }
          
          
          public static Calendar from(Date date) {
          
              Calendar c = Calendar.getInstance();
              c.setTime(date);
          
              return c;
          }
          
          
          public static Calendar removeTime(Calendar c) {
          
              c.set(HOUR_OF_DAY, 0);
              c.set(MINUTE, 0);
              c.set(SECOND, 0);
              c.set(MILLISECOND, 0);
          
              return c;
          }
          

          【讨论】:

            【解决方案13】:

            由于问题是用 Scala 标记的,

            import scala.concurrent.duration._
            val diff = (System.currentTimeMillis() - oldDate.getTime).milliseconds
            val diffSeconds = diff.toSeconds
            val diffMinutes = diff.toMinutes
            val diffHours = diff.toHours
            val diffDays = diff.toDays
            

            【讨论】:

              【解决方案14】:

              另一个纯 Java 变体:

              public boolean isWithin30Days(Calendar queryCalendar) {
              
                  // 1. Take the date you are checking, and roll it back N days
                  Calendar queryCalMinus30Days = Calendar.getInstance();
                  queryCalMinus30Days.setTime(queryCalendar.getTime());
                  queryCalMinus30Days.add(Calendar.DATE, -30); // subtract 30 days from the calendar
              
                  // 2. Get respective milliseconds for the two Calendars: now & queryCal minus N days 
                  long nowL = Calendar.getInstance().getTimeInMillis();
                  long queryCalMinus30DaysL = queryCalMinus30Days.getTimeInMillis();
              
                  // 3. if nowL is still less than the queryCalMinus30DaysL, it means queryCalendar is more than 30 days into future
                  boolean isWithin30Days = nowL >= queryCalMinus30DaysL;
              
                  return isWithin30Days;
              }
              

              感谢此处的起始代码:https://stackoverflow.com/a/30207726/2162226

              【讨论】:

                【解决方案15】:

                您可以尝试早期版本的 Java。

                 public static String daysBetween(Date createdDate, Date expiryDate) {
                
                        Calendar createdDateCal = Calendar.getInstance();
                        createdDateCal.clear();
                        createdDateCal.setTime(createdDate);
                
                        Calendar expiryDateCal = Calendar.getInstance();
                        expiryDateCal.clear();
                        expiryDateCal.setTime(expiryDate);
                
                
                        long daysBetween = 0;
                        while (createdDateCal.before(expiryDateCal)) {
                            createdDateCal.add(Calendar.DAY_OF_MONTH, 1);
                            daysBetween++;
                        }
                        return daysBetween+"";
                    }
                

                【讨论】:

                  【解决方案16】:

                  使用 Java 8+ 中内置的 java.time 框架:

                  ZonedDateTime now = ZonedDateTime.now();
                  ZonedDateTime oldDate = now.minusDays(1).minusMinutes(10);
                  Duration duration = Duration.between(oldDate, now);
                  System.out.println("ISO-8601: " + duration);
                  System.out.println("Minutes: " + duration.toMinutes());
                  

                  输出:

                  ISO-8601:PT24H10M

                  分钟:1450

                  有关详细信息,请参阅 Oracle TutorialISO 8601 标准。

                  【讨论】:

                  • 这个,或者使用Period而不是Duration作为日期。
                  • 不错 :) 我更喜欢这个
                  【解决方案17】:

                  在阅读了这个问题的许多答案和 cmets 之后,我的印象是要么必须使用 Joda 时间,要么要考虑夏令时等的一些特殊性。因为我不想做这些,我最终写了几行代码来计算两个日期之间的差异,而不使用任何与日期或时间相关的 Java 类。

                  在下面的代码中,年月日的数字与现实生活中的数字相同。例如 2015 年 12 月 24 日,年 = 2015,月 = 12,日 = 24。

                  我想分享此代码以防其他人想使用它。有 3 种方法: 1) 找出给定年份是否是闰年的方法 2) 计算与给定年份的 1 月 1 日相关的给定日期的方法 3) 计算日期数量的方法使用方法 2(结束日期的数字减去开始日期的数字)的任意两个日期之间的天数。

                  方法如下:

                  1)

                  public static boolean isLeapYear (int year) {
                      //Every 4. year is a leap year, except if the year is divisible by 100 and not by 400
                      //For example 1900 is not a leap year but 2000 is
                  
                      boolean result = false;
                  
                      if (year % 4 == 0) {
                          result = true;
                      }
                      if (year % 100 == 0) {
                          result = false;
                      }
                      if (year % 400 == 0) {
                          result = true;
                      }
                  
                      return result;
                  
                  }
                  

                  2)

                  public static int daysGoneSince (int yearZero, int year, int month, int day) {
                      //Calculates the day number of the given date; day 1 = January 1st in the yearZero
                  
                      //Validate the input
                      if (year < yearZero || month < 1 || month > 12 || day < 1 || day > 31) {
                          //Throw an exception
                          throw new IllegalArgumentException("Too many or too few days in month or months in year or the year is smaller than year zero");
                      }
                      else if (month == 4 || month == 6 || month == 9 || month == 11) {//Months with 30 days
                          if (day == 31) {
                              //Throw an exception
                              throw new IllegalArgumentException("Too many days in month");
                          }
                      }
                      else if (month == 2) {//February 28 or 29
                          if (isLeapYear(year)) {
                              if (day > 29) {
                                  //Throw an exception
                                  throw new IllegalArgumentException("Too many days in month");
                              }
                          }
                          else if (day > 28) {
                              //Throw an exception
                              throw new IllegalArgumentException("Too many days in month");
                          }
                      }
                  
                      //Start counting days
                      int days = 0;
                  
                      //Days in the target month until the target day
                      days = days + day;
                  
                      //Days in the earlier months in the target year
                      for (int i = 1; i < month; i++) {
                          switch (i) {
                              case 1: case 3: case 5:
                              case 7: case 8: case 10:
                              case 12:
                                  days = days + 31;
                                  break;
                              case 2:
                                  days = days + 28;
                                  if (isLeapYear(year)) {
                                      days = days + 1;
                                  }
                                  break;
                              case 4: case 6: case 9: case 11:
                                  days = days + 30;
                                  break;
                          }
                      }
                  
                      //Days in the earlier years
                      for (int i = yearZero; i < year; i++) {
                          days = days + 365;
                          if (isLeapYear(i)) {
                              days = days + 1;
                          }
                      }
                  
                      return days;
                  
                  }
                  

                  3)

                  public static int dateDiff (int startYear, int startMonth, int startDay, int endYear, int endMonth, int endDay) {
                  
                      int yearZero;
                  
                      //daysGoneSince presupposes that the first argument be smaller or equal to the second argument
                      if (10000 * startYear + 100 * startMonth + startDay > 10000 * endYear + 100 * endMonth + endDay) {//If the end date is earlier than the start date
                          yearZero = endYear;
                      }
                      else {
                          yearZero = startYear;
                      }
                  
                      return daysGoneSince(yearZero, endYear, endMonth, endDay) - daysGoneSince(yearZero, startYear, startMonth, startDay);
                  
                  }
                  

                  【讨论】:

                    【解决方案18】:

                    在java中有一个简单的方法来做到这一点

                    //创建一个实用方法

                    public long getDaysBetweenDates(Date d1, Date d2){
                    return TimeUnit.MILLISECONDS.toDays(d1.getTime() - d2.getTime());
                    }
                    

                    此方法将返回两个日期之间的天数。您可以使用默认的 java 日期格式,也可以轻松地从任何日期格式转换。

                    【讨论】:

                    • 这假定一天中的毫秒数是恒定的。这并不总是正确的。
                    • 是的,正如@pr1001 提到的。如果您在今天 22:00 和后天 00:00 进行测量会怎样?您将有 1.08,这将导致 1 天的差异,但实际上假设为 2!
                    【解决方案19】:

                    只需将以下方法与两个 Date 对象一起使用。如果你想传递当前日期,只需传递new Date() 作为第二个参数,因为它是用当前时间初始化的。

                    public String getDateDiffString(Date dateOne, Date dateTwo)
                    {
                        long timeOne = dateOne.getTime();
                        long timeTwo = dateTwo.getTime();
                        long oneDay = 1000 * 60 * 60 * 24;
                        long delta = (timeTwo - timeOne) / oneDay;
                    
                        if (delta > 0) {
                            return "dateTwo is " + delta + " days after dateOne";
                        }
                        else {
                            delta *= -1;
                            return "dateTwo is " + delta + " days before dateOne";
                         }
                    }
                    

                    另外,除了天数,如果你还想要其他参数差异,请使用下面的sn-p,

                    int year = delta / 365;
                    int rest = delta % 365;
                    int month = rest / 30;
                    rest = rest % 30;
                    int weeks = rest / 7;
                    int days = rest % 7;
                    

                    PS 代码完全取自 SO 答案。

                    【讨论】:

                    • 很高兴看到您参与 StackOverflow。请在您发布的代码示例中添加一些讨论。解释你的代码是如何工作的,你的答案与其他答案有何不同,指出任何棘手的概念,链接到文档等等。 StackOverflow 不仅仅是一个代码 sn-p 库。
                    【解决方案20】:
                    public static String getDifferenceBtwTime(Date dateTime) {
                    
                        long timeDifferenceMilliseconds = new Date().getTime() - dateTime.getTime();
                        long diffSeconds = timeDifferenceMilliseconds / 1000;
                        long diffMinutes = timeDifferenceMilliseconds / (60 * 1000);
                        long diffHours = timeDifferenceMilliseconds / (60 * 60 * 1000);
                        long diffDays = timeDifferenceMilliseconds / (60 * 60 * 1000 * 24);
                        long diffWeeks = timeDifferenceMilliseconds / (60 * 60 * 1000 * 24 * 7);
                        long diffMonths = (long) (timeDifferenceMilliseconds / (60 * 60 * 1000 * 24 * 30.41666666));
                        long diffYears = (long)(timeDifferenceMilliseconds / (1000 * 60 * 60 * 24 * 365));
                    
                        if (diffSeconds < 1) {
                            return "one sec ago";
                        } else if (diffMinutes < 1) {
                            return diffSeconds + " seconds ago";
                        } else if (diffHours < 1) {
                            return diffMinutes + " minutes ago";
                        } else if (diffDays < 1) {
                            return diffHours + " hours ago";
                        } else if (diffWeeks < 1) {
                            return diffDays + " days ago";
                        } else if (diffMonths < 1) {
                            return diffWeeks + " weeks ago";
                        } else if (diffYears < 12) {
                            return diffMonths + " months ago";
                        } else {
                            return diffYears + " years ago";
                        }
                    }   
                    

                    【讨论】:

                      【解决方案21】:

                      只是回答最初的问题:

                      将以下代码放入 Long getAge() 等函数中{}

                      Date dahora = new Date();
                      long MillisToYearsByDiv = 1000l *60l * 60l * 24l * 365l;
                      long javaOffsetInMillis = 1990l * MillisToYearsByDiv;
                      long realNowInMillis = dahora.getTime() + javaOffsetInMillis;
                      long realBirthDayInMillis = this.getFechaNac().getTime() + javaOffsetInMillis;
                      long ageInMillis = realNowInMillis - realBirthDayInMillis;
                      
                      return ageInMillis / MillisToYearsByDiv;
                      

                      这里最重要的是在乘法和除法时使用长数字。当然,还有 Java 在其日期计算中应用的偏移量。

                      :)

                      【讨论】:

                        【解决方案22】:

                        以下代码可以为您提供所需的输出:

                        String startDate = "Jan 01 2015";
                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy");
                        LocalDate date = LocalDate.parse(startDate, formatter);
                        
                        String currentDate = "Feb 11 2015";
                        LocalDate date1 = LocalDate.parse(currentDate, formatter);
                        
                        System.out.println(date1.toEpochDay() - date.toEpochDay());
                        

                        【讨论】:

                        • 你能解释一下你的代码是做什么的吗?这个问题似乎是在询问时间增量,乍一看,您的代码似乎会返回一天增量?
                        【解决方案23】:

                        这是另一个示例。这基本上适用于用户定义的模式。

                           public static LinkedHashMap<String, Object> checkDateDiff(DateTimeFormatter dtfObj, String startDate, String endDate)
                           {
                                  Map<String, Object> dateDiffMap = new HashMap<String, Object>();
                                  DateTime start = DateTime.parse(startDate,dtfObj);
                                  DateTime end = DateTime.parse(endDate,dtfObj);
                                  Interval interval = new Interval(start, end);
                                  Period period = interval.toPeriod();
                        
                                  dateDiffMap.put("ISO-8601_PERIOD_FORMAT", period);
                                  dateDiffMap.put("YEAR", period.getYears());
                                  dateDiffMap.put("MONTH", period.getMonths());
                                  dateDiffMap.put("WEEK", period.getWeeks());
                                  dateDiffMap.put("DAY", period.getWeeks());             
                                  dateDiffMap.put("HOUR", period.getHours());
                                  dateDiffMap.put("MINUTE", period.getMinutes());
                                  dateDiffMap.put("SECOND", period.getSeconds());
                        
                                  return dateDiffMap;        
                           }
                        

                        【讨论】:

                        • 感谢您尝试为 StackOverflow 做出贡献。我建议你添加一些讨论或解释。 StackOverflow 不仅仅是一个 sn-p 库。因此,您应该解释 (a) 您的代码是如何工作的,以及 (b) 它如何在此问题上已经发布的许多其他答案之外增加价值。此外,这段代码并不完整,其中的变量如dtfObj 既没有声明也没有赋值。最后,顺便说一句,我提供这个提示:您可以使用第一行左侧的更通用的Map 界面,而不是LinkedHashMap。这是多态性的要点/好处之一。
                        • @BasilBourque :您好,我已经做出了预期的更改,并且肯定会尝试添加更多解释。
                        【解决方案24】:

                        如果你需要一个格式化的返回字符串,比如 “2 Days 03h 42m 07s”,试试这个:

                        public String fill2(int value)
                        {
                            String ret = String.valueOf(value);
                        
                            if (ret.length() < 2)
                                ret = "0" + ret;            
                            return ret;
                        }
                        
                        public String get_duration(Date date1, Date date2)
                        {                   
                            TimeUnit timeUnit = TimeUnit.SECONDS;
                        
                            long diffInMilli = date2.getTime() - date1.getTime();
                            long s = timeUnit.convert(diffInMilli, TimeUnit.MILLISECONDS);
                        
                            long days = s / (24 * 60 * 60);
                            long rest = s - (days * 24 * 60 * 60);
                            long hrs = rest / (60 * 60);
                            long rest1 = rest - (hrs * 60 * 60);
                            long min = rest1 / 60;      
                            long sec = s % 60;
                        
                            String dates = "";
                            if (days > 0) dates = days + " Days ";
                        
                            dates += fill2((int) hrs) + "h ";
                            dates += fill2((int) min) + "m ";
                            dates += fill2((int) sec) + "s ";
                        
                            return dates;
                        }
                        

                        【讨论】:

                        • 哇,既然Joda-Time 提供了已经编写和调试过的Period 类,为什么还要自己动手呢?
                        • 当我只需要 32 行代码时,为什么要下载压缩文件大小为 4.1 MB 的库并将其添加到我的项目中???
                        • 因为 Joda-Time 就像薯片:你不能只吃一个。您将在各处使用 Joda-Time。而且因为 Joda-Time 是经过良好测试和陈旧的代码。而且因为 Joda-Time 解析并生成标准的ISO 8601 Duration 字符串,这对于序列化或报告这些值可能很方便。而且因为 Joda-Time 包含格式化程序来打印这些值的本地化表示。
                        • 并非所有代码都经过测试。 std 以上未定义。
                        • @Basil Bourque:谢谢提示。我已经纠正了来源。这个错误来自翻译成英文。
                        【解决方案25】:

                        我喜欢基于 TimeUnit 的方法,直到我发现它只涵盖了一个 timeunit 的多少个单元在下一个更高单元中的数量是固定的微不足道的情况。当您想知道两者之间有多少个月、多少年等时,这就会分开。

                        这是一种计数方法,效率不如其他方法,但它似乎对我有用,并且也考虑了 DST。

                        public static String getOffsetAsString( Calendar cNow, Calendar cThen) {
                            Calendar cBefore;
                            Calendar cAfter;
                            if ( cNow.getTimeInMillis() < cThen.getTimeInMillis()) {
                                cBefore = ( Calendar) cNow.clone();
                                cAfter = cThen;
                            } else {
                                cBefore = ( Calendar) cThen.clone();
                                cAfter = cNow;
                            }
                            // compute diff
                            Map<Integer, Long> diffMap = new HashMap<Integer, Long>();
                            int[] calFields = { Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND};
                            for ( int i = 0; i < calFields.length; i++) {
                                int field = calFields[ i];
                                long    d = computeDist( cAfter, cBefore, field);
                                diffMap.put( field, d);
                            }
                            final String result = String.format( "%dY %02dM %dT %02d:%02d:%02d.%03d",
                                    diffMap.get( Calendar.YEAR), diffMap.get( Calendar.MONTH), diffMap.get( Calendar.DAY_OF_MONTH), diffMap.get( Calendar.HOUR_OF_DAY), diffMap.get( Calendar.MINUTE), diffMap.get( Calendar.SECOND), diffMap.get( Calendar.MILLISECOND));
                            return result;
                        }
                        
                        private static int computeDist( Calendar cAfter, Calendar cBefore, int field) {
                            cBefore.setLenient( true);
                            System.out.print( "D " + new Date( cBefore.getTimeInMillis()) + " --- " + new Date( cAfter.getTimeInMillis()) + ": ");
                            int count = 0;
                            if ( cAfter.getTimeInMillis() > cBefore.getTimeInMillis()) {
                                int fVal = cBefore.get( field);
                                while ( cAfter.getTimeInMillis() >= cBefore.getTimeInMillis()) {
                                    count++;
                                    fVal = cBefore.get( field);
                                    cBefore.set( field, fVal + 1);
                                    System.out.print( count + "/"  + ( fVal + 1) + ": " + new Date( cBefore.getTimeInMillis()) + " ] ");
                                }
                                int result = count - 1;
                                cBefore.set( field, fVal);
                                System.out.println( "" + result + " at: " + field + " cb = " + new Date( cBefore.getTimeInMillis()));
                                return result;
                            }
                            return 0;
                        }
                        

                        【讨论】:

                          【解决方案26】:
                          public static void main(String[] args) {
                          
                              String dateStart = "01/14/2012 09:29:58";
                              String dateStop = "01/14/2012 10:31:48";
                          
                              SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
                          
                              Date d1 = null;
                              Date d2 = null;
                          
                              try {
                                  d1 = format.parse(dateStart);
                                  d2 = format.parse(dateStop);
                          
                                  DateTime date11 = new DateTime(d1);
                                  DateTime date22 = new DateTime(d2);
                                  int days = Days.daysBetween(date11.withTimeAtStartOfDay(), date22.withTimeAtStartOfDay()).getDays();
                                  int hours = Hours.hoursBetween(date11, date22).getHours() % 24;
                                  int minutes = Minutes.minutesBetween(date11, date22).getMinutes() % 60;
                                  int seconds = Seconds.secondsBetween(date11, date22).getSeconds() % 60;
                                  if (hours > 0 || minutes > 0 || seconds > 0) {
                                      days = days + 1;
                                  }
                          
                                  System.out.println(days);
                          
                              } catch (Exception e) {
                                  e.printStackTrace();
                              }
                          
                          }
                          

                          这也会给出同一天的日期差异

                          【讨论】:

                          • 您能否对您的答案的附加值进行一些解释? this answerthis answer 似乎是多余的。其他答案似乎更好,例如使用 TimeUnit Enum 实用程序类。
                          • 实际上每个实现中的问题是它在同一天的日期时间确实计数为 0...但是这样你可以获得与 1 相同的日期差异,而且它取决于@BasilBourque
                          【解决方案27】:

                          最好的办法是

                          (Date1-Date2)/86 400 000 
                          

                          该数字是一天中的毫秒数。

                          一个日期与另一个日期的差异以毫秒为单位。

                          double 变量中收集答案。

                          【讨论】:

                          • 是的,为什么不呢?我在某些方面不理解问题的重点:OP 在 ms 上有所不同......并且一天中有一定数量的 ms(可以在蓝月亮中进行某些天文调整,但 OP 没有不是说它必须像天文学家需要的那样精确......)
                          【解决方案28】:

                          由于您使用的是 Scala,所以有一个非常好的 Scala 库Lamma。使用 Lamma,您可以使用 - 运算符直接减去日期

                          scala> Date(2015, 5, 5) - 2     // minus days by int
                          res1: io.lamma.Date = Date(2015,5,3)
                          
                          scala> Date(2015, 5, 15) - Date(2015, 5, 8)   // minus two days => difference between two days
                          res2: Int = 7
                          

                          【讨论】:

                            【解决方案29】:

                            以毫秒为单位减去日期有效(如另一篇文章中所述),但您必须在清除日期的时间部分时使用 HOUR_OF_DAY 而不是 HOUR:

                            public static final long MSPERDAY = 60 * 60 * 24 * 1000;
                            ...
                            final Calendar dateStartCal = Calendar.getInstance();
                            dateStartCal.setTime(dateStart);
                            dateStartCal.set(Calendar.HOUR_OF_DAY, 0); // Crucial.
                            dateStartCal.set(Calendar.MINUTE, 0);
                            dateStartCal.set(Calendar.SECOND, 0);
                            dateStartCal.set(Calendar.MILLISECOND, 0);
                            final Calendar dateEndCal = Calendar.getInstance();
                            dateEndCal.setTime(dateEnd);
                            dateEndCal.set(Calendar.HOUR_OF_DAY, 0); // Crucial.
                            dateEndCal.set(Calendar.MINUTE, 0);
                            dateEndCal.set(Calendar.SECOND, 0);
                            dateEndCal.set(Calendar.MILLISECOND, 0);
                            final long dateDifferenceInDays = ( dateStartCal.getTimeInMillis()
                                                              - dateEndCal.getTimeInMillis()
                                                              ) / MSPERDAY;
                            if (dateDifferenceInDays > 15) {
                                // Do something if difference > 15 days
                            }
                            

                            【讨论】:

                            • 这是确定两个日历日期之间差异的最佳方法,无需超出标准库。大多数其他答案将一天视为任意 24 小时。如果要减去而不是添加闰秒,则最终计算可能会由于截断而偏离,因为绝对差可能略小于 MSPERDAY 的整数倍数。
                            【解决方案30】:

                            @Michael Borgwardt 的回答实际上在 Android 中无法正常工作。存在舍入错误。示例 5 月 19 日至 21 日表示 1 天,因为它将 1.99 转换为 1。在转换为 int 之前使用 round。

                            修复

                            int diffInDays = (int)Math.round(( (newerDate.getTime() - olderDate.getTime()) 
                                             / (1000 * 60 * 60 * 24) ))
                            

                            请注意,这适用于 UTC 日期,因此如果您查看当地日期,差异可能是一天。由于夏令时,要让它与本地日期正常工作需要完全不同的方法。

                            【讨论】:

                            • 只要两个时间戳都在同一个时区,那么无论是否从UTC调整本地时区,日期之间的差异都是相同的(调整减去)。有可能对结果进行 DST 更正,但如果你一开始就假设一天是 86400 秒,那真的没关系。
                            • 这不是答案的重点。 Michael Brogwardt 的回答会导致错误的结果,除非您将其四舍五入并在 Android 的 Java SDK 中强制转换为 int。
                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 2013-02-01
                            相关资源
                            最近更新 更多