Java中两个date之间的差异?

我需要find两个date之间的天数 :一个来自报告,另一个来自当前date。 我的片段:

int age=calculateDifference(agingDate, today); 

这里calculateDifference是一个私有方法, agingDatetodayDate对象,只是为了您的澄清。 我已经从一个Java论坛, 线程1 / 线程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时间库的帮助下完成了这个问题 。

我build议你使用优秀的乔达时间库,而不是有缺陷的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 

我可能为时太晚而不能参加比赛,但是这到底是怎么回事? 🙂

你认为这是一个线程问题? 你如何使用这个方法的输出? 要么

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

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

diff /(24 *等)不考虑Timezone,所以如果你的默认时区有一个DST,它可以closures计算。

这个链接有一个不错的实现。

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

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

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

如果您只需要天数,或者您可以使用ChronoUnit 枚举 。 注意计算方法返回一个long而不是int

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

这取决于你定义的差异。 要比较两个date在午夜,你可以做。

 long day1 = ...; // in milliseconds. long day2 = ...; // in milliseconds. long days = (day2 - day1) / 86400000; 
 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; } } 

解决scheme使用毫秒时间之间的差异,正确舍入DSTdate:

 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 } 

一个注意:当然,date必须在某个时区。

重要的代码:

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

如果你不想轮,你可以使用UTCdate,

问题的解释:(我的代码是在几周内计算增量,但同样的问题适用于天数增量)

这是一个非常合理的实现:

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

但是这个testing会失败:

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

原因是:

2009年3月1日00:00:00美国东部时间2009年= 1,236,571,200,000
星期一2月23日00:00:00美国东部时间2009年= 123536550000
MillisPerWeek = 604,800,000
从而,
(Mar09 – Feb23)/ MillisPerWeek =
1,206,000,000 / 604,800,000 = 1.994 …

但是看日历的人会同意答案是2。

我使用这个function:

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

我的function:

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

看看这个Apache Commons-lang类DateUtils中的getFragmentInDays方法。

基于@ Mad_Troll的回答,我开发了这个方法。

我已经运行了大约30个testing用例,是正确处理子日时间片段的唯一方法。

例如:如果你现在通过+现在+ 1毫秒仍然是同一天。 做1-1-13 23:59:59.0981-1-13 23:59:59.099正确地返回0天; 在这里发布的其他方法的分配不会正确地做到这一点。

值得注意的是它不关心你把它们放在哪个方向,如果你的结束date在你的开始date之前,它会倒数。

 /** * 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使用这些注释来即时执行代码分析

你说它在一个独立的程序中工作得很好,但是当你把这个“包含在我的逻辑中来从报告中读取”的时候,你会得到“非同寻常的价值”。 这表明你的报告有一些值不能正常工作,你的独立程序没有这些值。 我build议一个testing用例,而不是一个独立的程序。 编写一个testing用例,就像独立程序一样,从JUnit的TestCase类inheritance。 现在你可以运行一个非常具体的例子,知道你期望的价值(今天不要给它testing值,因为今天会随着时间而改变)。 如果你把你在独立程序中使用的值,你的testing可能会通过。 这太棒了 – 你希望这些案件继续工作。 现在,从您的报告中添加一个值,这不正确。 您的新testing可能会失败。 找出为什么它失败,修复它,并得到绿色(所有testing通过)。 运行你的报告。 看看还有什么坏的; 写一个testing; 使其通过。 很快你会发现你的报告正在工作。

这个基本function的百行代码?

只是一个简单的方法:

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

ThreeTen-EXTRA

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

Days

如果您在代码中经常使用若干天,则可以使用类来replace纯粹的整数。 Days类可以在ThreeTen-Extra项目中find,它是java.time的一个扩展,也是将来可能添加到java.time的一个certificate。 Days类提供了一种types安全的方式来表示应用程序中的天数。 这个类包含了ZEROONE常量。

鉴于问题中陈旧的java.util.Date对象,首先将它们转换为现代java.time.Instant对象。 旧的date – 时间类有新添加的方法来方便转换为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而初始Days是ThreeTen-Extra中的一个类。

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

你可以在你自己的代码中传递这些Days对象。 您可以通过调用toString来序列化为标准ISO 8601格式的string。 这种PnD格式使用P来标记开始,而D意味着“天”,间隔数天。 生成和parsing表示date时间值的string时,java.time类和ThreeTen-Extra都默认使用这些标准格式。

 String output = days.toString(); 

P3D

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

此代码计算2datestring之间的天数:

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

如果你正在寻找一个解决scheme,返回适当的数字或天之间,例如11/30/2014 23:5912/01/2014 00:01这里的解决scheme使用乔达时间。

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

这个实现将以天数的差异返回1 。 这里发布的大多数解决scheme都会计算两个date之间的差异,以毫秒为单位。 这意味着0将被返回,因为这两个date之间只有2分钟的差异。

我已经写了关于它。 这是计算两个Javadate实例之间差异的重新发布。

 public int getDiffernceInDays(long timeAfter, long timeBefore) { Calendar calendarAfter = Calendar.getInstance(); calendarAfter.setTime(new Date(timeAfter)); Calendar calendarNewAfter = Calendar.getInstance(); calendarNewAfter.set(calendarAfter.get(Calendar.YEAR), calendarAfter.get(Calendar.MONTH), calendarAfter.get(Calendar.DAY_OF_MONTH)); Calendar calendarBefore = Calendar.getInstance(); calendarBefore.setTime(new Date(timeBefore)); Calendar calendarNewBefore = Calendar.getInstance(); calendarNewBefore.set(calendarBefore.get(Calendar.YEAR), calendarBefore.get(Calendar.MONTH), calendarBefore.get(Calendar.DAY_OF_MONTH)); return (int) ((calendarNewAfter.getTime().getTime() - calendarNewBefore.getTime().getTime()) / (24 * 60 * 60 * 1000)); } 

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

乔达与Java利用date

例如,在昨天(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); } 

所以我真的build议你使用乔达时间库。

我这样做了。 这很容易 :)

 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;