计算两个Java日期实例之间的差异

pr1*_*001 416 java time date timedelta java.util.date

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

(new java.util.Date()).getTime() - oldDate.getTime()
Run Code Online (Sandbox Code Playgroud)

但是,这只留下了一个long代表毫秒.是否有更简单,更好的方式来获得时间增量?

Seb*_*ber 525

简单的差异(没有lib)

/**
 * 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);
}
Run Code Online (Sandbox Code Playgroud)

然后你可以打电话:

getDateDiff(date1,date2,TimeUnit.MINUTES);
Run Code Online (Sandbox Code Playgroud)

以分钟为单位获得2个日期的差异.

TimeUnitjava.util.concurrent.TimeUnit一个从纳米到几天的标准Java枚举.


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

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;
}
Run Code Online (Sandbox Code Playgroud)

http://ideone.com/5dXeu6

输出就像Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0}订购单位一样.

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


警告

上面的代码片段计算了两个时刻之间的简单差异.它可以在夏令开关过程中导致问题,就像在解释这个职位.这意味着如果您在没有时间的情况下计算日期之间的差异,则可能会丢失一天/小时.

在我看来,日期差异是一种主观的,特别是在日子里.你可以:

  • 计算24小时经过的时间:天+ 1天= 1天= 24小时

  • 计算经过的时间,照顾夏令时:白天+ 1天= 1 = 24小时(但使用午夜时间和夏令时可能是0天和23小时)

  • 计算数字day switches,即日期+ 1下午1点 - 上午11点= 1天,即使经过的时间仅为2小时(如果有夏令时则​​为1小时:p)

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

随着JodaTime

如果您正在使用JodaTime,您可以获得2个瞬间(毫秒支持的ReadableInstant)日期的差异:

Interval interval = new Interval(oldInstant, new Instant());
Run Code Online (Sandbox Code Playgroud)

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

// 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()
Run Code Online (Sandbox Code Playgroud)

  • @AndrewS谢谢,它是`java.util.concurrent.TimeUnit` (8认同)

not*_*oop 186

Date遗憾的是,JDK API被严重破坏.我建议使用Joda Time库.

Joda Time有一个时间间隔的概念:

Interval interval = new Interval(oldTime, new Instant());
Run Code Online (Sandbox Code Playgroud)

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

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

  • Java 8中新的[`java.time`](http://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html)软件包受Joda-Time的启发,但是不是直接替代品.每个都有其优点和缺点.幸运的是,您无需在它们之间进行选择.只要您小心使用`import`语句,就可以使用每个优势.有关java.time的示例,请参阅[此其他答案](http://stackoverflow.com/a/23184523/642706). (11认同)
  • 仅供参考,[Joda-Time](http://www.joda.org/joda-time/)项目现在处于[维护模式](https://en.wikipedia.org/wiki/Maintenance_mode),团队建议迁移到[java.time](http://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html)类.请参阅[Oracle教程](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). (6认同)

Mic*_*rdt 150

int diffInDays = (int)( (newerDate.getTime() - olderDate.getTime()) 
                 / (1000 * 60 * 60 * 24) )
Run Code Online (Sandbox Code Playgroud)

请注意,这适用于UTC日期,因此如果您查看本地日期,差异可能是休息日.并且由于夏令时,需要采用完全不同的方法使其与本地日期一起正常工作.

  • @scottb:本地日期的问题是你可以有夏令时,这意味着有些日子有23或25小时,可能会弄乱结果. (8认同)
  • 这实际上在Android中无法正常工作.存在舍入错误.示例19到21月5日说是1天,因为它施放1.99到1.在转换为int之前使用round. (6认同)
  • 这是最好最简单的答案.当计算两个日期之间的差异时,本地时区相互减去......所以正确的答案(作为双精度)简单地由((double)(newer.getTime() - older.getTime())/给出(86400.0*1000.0); ...作为一个双倍,你也有分数天,可以很容易地转换为HH:MM:ss. (4认同)

Jon*_*eet 53

您需要更清楚地定义问题.您可以将两个Date对象之间的毫秒数除以24小时内的毫秒数,例如......但是:

  • 这不会考虑时区 - Date始终是UTC
  • 这不会考虑夏令时(例如,可能只有23小时的天数)
  • 即使在UTC内,有多少天在8月16日晚上11点到8月18日凌晨2点?它只有27个小时,所以这意味着有一天?或者它应该是三天,因为它涵盖三个日期?


Vit*_*nko 52

使用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());
Run Code Online (Sandbox Code Playgroud)

输出:

ISO-8601:PT24H10M

会议纪要:1450

有关详细信息,请参阅Oracle教程ISO 8601标准.

  • 这个,或者使用`Period`而不是`Duration`来表示日期. (5认同)

Bas*_*que 42

TL;博士

将过时的java.util.Date对象转换为替换对象java.time.Instant.然后将经过的时间计算为a 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.
    )
;
Run Code Online (Sandbox Code Playgroud)

d.toString():PT2H34M56S

d.toMinutes():154

d.toMinutesPart():34

ISO 8601格式: PnYnMnDTnHnMnS

明智的标准ISO 8601将时间跨度的简明文本表示定义为若干年,月,日,小时等.标准称这样的跨度为持续时间.格式是"Period" PnYnMnDTnHnMnSP意思,将T日期部分与时间部分分开,中间是数字后跟一个字母.

例子:

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

java.time

Java 8及更高版本中内置的java.time框架取代了麻烦的旧java.util.Date/ java.util.Calendar类.新课程的灵感来自非常成功的Joda-Time框架,旨在作为其继承者,在概念上类似但重新设计.由JSR 310定义.由ThreeTen-Extra项目扩展.请参阅教程.

时刻

Instant级表示时间轴上的时刻UTC,分辨率为纳秒(最多9个(9)小数的位数).

Instant instant = Instant.now() ;  // Capture current moment in UTC.
Run Code Online (Sandbox Code Playgroud)

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

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

时间跨度

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

这是一个例子.

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

转储到控制台.

二者PeriodDurationISO 8601标准,用于产生其值的字符串表示.

System.out.println ( "now: " + now + " to future: " + now + " = " + duration );
Run Code Online (Sandbox Code Playgroud)

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

Java 9添加Duration了获取日期部分,小时部分,分钟部分和秒部分的方法.

您可以获得整个持续时间中的总天数或小时数,分钟数或秒数或毫秒数或纳秒数.

long totalHours = duration.toHours();
Run Code Online (Sandbox Code Playgroud)

在Java 9中,Duration该类获得了返回日,小时,分钟,秒,毫秒/纳秒等各个部分的新方法.调用to…Part方法:toDaysPart(),toHoursPart()等等.

ChronoUnit

如果您只关心更简单的更大粒度的时间,例如"已过去的天数",请使用ChronoUnit枚举.

long daysElapsed = ChronoUnit.DAYS.between( earlier , later );
Run Code Online (Sandbox Code Playgroud)

另一个例子.

Instant now = Instant.now();
Instant later = now.plus( Duration.ofHours( 2 ) );
…
long minutesElapsed = ChronoUnit.MINUTES.between( now , later );
Run Code Online (Sandbox Code Playgroud)

120


关于java.time

java.time框架是建立在Java 8和更高版本.这些类取代麻烦的老传统日期时间类,如java.util.Date,Calendar,和SimpleDateFormat.

现在处于维护模式Joda-Time项目建议迁移到java.time.

要了解更多信息,请参阅Oracle教程.并搜索Stack Overflow以获取许多示例和解释.规范是JSR 310.

从哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time.该项目是未来可能添加到java.time的试验场.您可以在此比如找到一些有用的类Interval,YearWeek,YearQuarter,和更多.


乔达时间

更新:Joda-Time项目现在处于维护模式,团队建议迁移到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 );
Run Code Online (Sandbox Code Playgroud)

呈现:

period: PT4H30M
Run Code Online (Sandbox Code Playgroud)


Adr*_*cha 34

Days d = Days.daysBetween(startDate, endDate);
int days = d.getDays();
Run Code Online (Sandbox Code Playgroud)

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

  • 仅供参考,[Joda-Time](http://www.joda.org/joda-time/) 项目现在处于 [维护模式](https://en.wikipedia.org/wiki/Maintenance_mode),具有团队建议迁移到 [java.time](http://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html) 类。 (2认同)

Mic*_*rdt 24

一个稍微简单的替代方案:

System.currentTimeMillis() - oldDate.getTime()
Run Code Online (Sandbox Code Playgroud)

至于"更好":嗯,你究竟需要什么?将持续时间表示为小时数和天数等的问题在于,由于日期的复杂性,它可能导致不准确和错误的期望(例如,由于夏令时,天数可能为23或25小时).


zas*_*nyy 22

使用毫秒方法可能会导致某些区域设置出现问题.

让我们看一下,例如,03/24/2007和03/25/2007两个日期之间的差异应该是1天;

但是,使用毫秒路线,如果你在英国运行,你将获得0天!

/** Manual Method - YIELDS INCORRECT RESULTS - DO NOT USE**/  
/* This method is used to find the no of days between the given dates */  
public long calculateDays(Date dateEarly, Date dateLater) {  
   return (dateLater.getTime() - dateEarly.getTime()) / (24 * 60 * 60 * 1000);  
} 
Run Code Online (Sandbox Code Playgroud)

更好的实现方法是使用java.util.Calendar

/** 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;  
}  
Run Code Online (Sandbox Code Playgroud)

  • 你能否相信这段代码的原作者和第三句,其[博客文章的历史可以追溯到2007年](http://tripoverit.blogspot.com/2007_07_01_archive.html)? (19认同)
  • @marcolopes - 你错了 - 因为日历月是零.我确定你的意思是3月/ 4月,但你测试的是4月/ 6月31日.为了安全写它像 - >新GregorianCalendar(2014,Calendar.MARCH,1).... (5认同)

JDG*_*ide 21

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

      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);
Run Code Online (Sandbox Code Playgroud)

print语句只是一个例子 - 您可以按照自己喜欢的方式对其进行格式化.

  • @ manoj kumar bardhan:欢迎堆叠溢出:正如您所看到的问题和答案已经岁月.你的答案应该比现有答案增加更多的问题/答案. (5认同)
  • 由于DST过渡而导致23或25小时的日子怎么样? (3认同)
  • -1因为你没有考虑闰年. (3认同)

ΦXo*_*a ツ 18

由于这里的所有答案都是正确的,但使用遗留的java或第三方库,如joda或类似的,我将在Java 8及更高版本中使用新的java.time类.请参阅Oracle教程.

使用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 );
Run Code Online (Sandbox Code Playgroud)


小智 9

减去以毫秒为单位的日期(如另一篇文章中所述),但在清除日期的时间部分时,您必须使用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
}
Run Code Online (Sandbox Code Playgroud)


Rob*_*b H 8

看看Joda Time,这是一个改进的Java日期/时间API,应该可以正常使用Scala.


sle*_*ske 8

如果您不想使用JodaTime或类似的,最好的解决方案可能是:

final static long MILLIS_PER_DAY = 24 * 3600 * 1000;
long msDiff= date1.getTime() - date2.getTime();
long daysDiff = Math.round(msDiff / ((double)MILLIS_PER_DAY));
Run Code Online (Sandbox Code Playgroud)

每天的毫秒数并不总是相同(因为夏令时和闰秒),但它非常接近,并且至少由于夏令时的偏差会在较长时间内抵消.因此,分割然后舍入将给出正确的结果(至少只要使用的本地日历不包含除DST和闰秒之外的奇怪时间跳跃).

请注意,这仍然假设date1并且date2设置为一天的同一时间.正如Jon Skeet所指出的那样,对于一天中的不同时间,您首先必须定义"日期差异"的含义.


Boz*_*zho 5

int daysDiff = (date1.getTime() - date2.getTime()) / MILLIS_PER_DAY;
Run Code Online (Sandbox Code Playgroud)

  • -1 这是错误的,除非 Date 实例是从 UTC 时间派生的。见乔恩斯基特的回答。 (5认同)
  • @sleske 你不对。他已经通过使用`Date`失去了时区信息,所以这个比较是考虑到情况下可以实现的最好的。 (5认同)

use*_*158 5

让我展示Joda Interval和Days之间的区别:

DateTime start = new DateTime(2012, 2, 6, 10, 44, 51, 0);
DateTime end = new DateTime(2012, 2, 6, 11, 39, 47, 1);
Interval interval = new Interval(start, end);
Period period = interval.toPeriod();
System.out.println(period.getYears() + " years, " + period.getMonths() + " months, " + period.getWeeks() + " weeks, " + period.getDays() + " days");
System.out.println(period.getHours() + " hours, " + period.getMinutes() + " minutes, " + period.getSeconds() + " seconds ");
//Result is:
//0 years, 0 months, *1 weeks, 1 days*
//0 hours, 54 minutes, 56 seconds 

//Period can set PeriodType,such as PeriodType.yearMonthDay(),PeriodType.yearDayTime()...
Period p = new Period(start, end, PeriodType.yearMonthDayTime());
System.out.println(p.getYears() + " years, " + p.getMonths() + " months, " + p.getWeeks() + " weeks, " + p.getDays() + "days");
System.out.println(p.getHours() + " hours, " + p.getMinutes() + " minutes, " + p.getSeconds() + " seconds ");
//Result is:
//0 years, 0 months, *0 weeks, 8 days*
//0 hours, 54 minutes, 56 seconds 
Run Code Online (Sandbox Code Playgroud)


Ing*_*ngo 5

如果你需要一个格式化的返回字符串,如"2天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;
}
Run Code Online (Sandbox Code Playgroud)

  • 为什么我应该下载一个压缩的FileSize为4.1 MB的库并将其添加到我的项目中,当我只需要32行代码??? (8认同)
  • 哇,为什么在[Joda-Time](http://www.joda.org/joda-time/)提供[期间]时自行推出(http://www.joda.org/joda-time/apidocs/ org/joda/time/Period.html)类已经编写和调试过吗? (2认同)

Shr*_*thy 5

注意: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();
Run Code Online (Sandbox Code Playgroud)

和结束日期

类似于几天,您还可以获取小时,分钟和秒

period.getStandardHours();
period.getStandardMinutes();
period.getStandardSeconds();
Run Code Online (Sandbox Code Playgroud)


gen*_* b. 5

在浏览完所有其他答案后,为了保留 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;
}
Run Code Online (Sandbox Code Playgroud)