Java中两个date之间的差异?
我需要find两个date之间的天数 :一个来自报告,另一个来自当前date。 我的片段:
int age=calculateDifference(agingDate, today);
这里calculateDifference
是一个私有方法, agingDate
和today
是Date
对象,只是为了您的澄清。 我已经从一个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.098
到1-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类( Duration
& ChronoUnit
)以现代方式执行此计算。
Days
如果您在代码中经常使用若干天,则可以使用类来replace纯粹的整数。 Days
类可以在ThreeTen-Extra项目中find,它是java.time的一个扩展,也是将来可能添加到java.time的一个certificate。 Days
类提供了一种types安全的方式来表示应用程序中的天数。 这个类包含了ZERO
和ONE
常量。
鉴于问题中陈旧的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:59
和12/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;