Javadate月份差异
我有开始date和结束date。
我需要java中这两个date之间的月数。
例如,从2009年1月29日至2009年2月2日
(它有约会date和2月date)。
它应该返回2。
正如其他人所说,如果有一个图书馆会给你几个月的时间差异,你可以使用它,那么你也可以。
否则,如果y1
和m1
是第一个date的年份和月份,而y2
和m2
是第二个的年份和月份,那么您想要的值是:
(y2 - y1) * 12 + (m2 - m1) + 1;
请注意,即使第二个date在第一个date之后,中期(m2 – m1)也可能为负,但这没关系。
1月份= 0还是1月份= 1月份是不重要的,只要两个date使用相同的基础,年份是自1900年以来的年份,或者是任何年份,都没有关系。 因此,例如,不要混合AD和BCdate,因为不存在0年,因此BC从AD抵消1。
如果以合适的forms提供给您,或者使用日历,则可以直接从date获得y1
等。
除了使用乔达时间似乎是最喜欢的build议,我会提供以下片段:
public static final int getMonthsDifference(Date date1, Date date2) { int m1 = date1.getYear() * 12 + date1.getMonth(); int m2 = date2.getYear() * 12 + date2.getMonth(); return m2 - m1 + 1; }
编辑:自Java 8以来,有一个更加标准的方式来计算相同的差异。 请使用JSR-310 API查看我的替代答案。
我强烈推荐Joda-Time 。
- 它使这种工作非常容易(检查期间 )
- 它不会遇到困扰当前date/时间对象的线程问题(特别是我在考虑格式化程序)
- 这是Java 7中新的Javadate/时间API的基础(所以你正在学习一些将成为标准的东西)
请注意Nick Holt的评论如下。 夏令时改变。
现在, JSR-310已经包含在Java 8及以上版本的SDK中,下面是获取两个date值的月份差异的更为标准的方法:
public static final long getMonthsDifference(Date date1, Date date2) { YearMonth m1 = YearMonth.from(date1.toInstant()); YearMonth m2 = YearMonth.from(date2.toInstant()); return m1.until(m2, ChronoUnit.MONTHS) + 1; }
这具有明确说明计算精度的好处,并且很容易理解计算的意图。
使用乔达时间会是这样的(我比较了今天和2012年12月20日之间的几个月)
import org.joda.time.DateTime ; import org.joda.time.Months; DateTime x = new DateTime().withDate(2009,12,20); // doomsday lol Months d = Months.monthsBetween( new DateTime(), x); int monthsDiff = d.getMonths();
结果:41个月(2009年7月6日起)
应该很容易? 🙂
PS:你也可以使用SimpleDateFormat来转换你的date,如:
Date x = new SimpleDateFormat("dd/mm/yyyy").parse("20/12/2009"); DateTime z = new DateTime(x);
如果你不想使用乔达(无论出于何种原因),你可以将你的date转换为时间戳,然后做两个date之间的毫秒差异,然后计算回到几个月。 但我仍然喜欢使用乔达时间的简单:)
Java 8解决scheme:
@Test public void monthBetween() { LocalDate d1 = LocalDate.of(2013, Month.APRIL, 1); LocalDate d2 = LocalDate.of(2014, Month.APRIL, 1); long monthBetween = ChronoUnit.MONTHS.between(d1, d2); assertEquals(12, monthBetween); }
基于上面提出的答案,我把我自己添加到现有的DateUtils类中:
public static Integer differenceInMonths(Date beginningDate, Date endingDate) { if (beginningDate == null || endingDate == null) { return 0; } Calendar cal1 = new GregorianCalendar(); cal1.setTime(beginningDate); Calendar cal2 = new GregorianCalendar(); cal2.setTime(endingDate); return differenceInMonths(cal1, cal2); } private static Integer differenceInMonths(Calendar beginningDate, Calendar endingDate) { if (beginningDate == null || endingDate == null) { return 0; } int m1 = beginningDate.get(Calendar.YEAR) * 12 + beginningDate.get(Calendar.MONTH); int m2 = endingDate.get(Calendar.YEAR) * 12 + endingDate.get(Calendar.MONTH); return m2 - m1; }
联合unit testing:
public void testDifferenceInMonths() throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); assertEquals(12, DateUtils.differenceInMonths(sdf.parse("2014/03/22"), sdf.parse("2015/03/22")).intValue()); assertEquals(11, DateUtils.differenceInMonths(sdf.parse("2014/01/01"), sdf.parse("2014/12/25")).intValue()); assertEquals(88, DateUtils.differenceInMonths(sdf.parse("2014/03/22"), sdf.parse("2021/07/05")).intValue()); assertEquals(6, DateUtils.differenceInMonths(sdf.parse("2014/01/22"), sdf.parse("2014/07/22")).intValue()); }
Joda Time是一个非常酷的Javadate和时间库,可以帮助您使用Period
实现您想要的function 。
您可以使用日历或Joda时间库。
在Joda时间,您可以使用Days.daysBetween()方法。 然后你可以计算月份差异。 您也可以使用DateTime.getMonthOfYear()并做一个减法(在同一年的date)。
我不得不写这个实现,因为我有自定义的期限,我必须在两个date之内寻找。 在这里你可以定义你自定义的时间段,并把逻辑,进行计算。
这里TimePeriod是一个POJO,它有开始,结束,开始,结束
公共课每月延长期间{
public int getPeriodCount(String startDate, String endDate, int scalar) { int cnt = getPeriods(startDate, endDate, scalar).size(); return cnt; } public List getPeriods(String startDate, String endDate, int scalar) { ArrayList list = new ArrayList(); Calendar startCal = CalendarUtil.getCalendar(startDate); Calendar endCal = CalendarUtil.getCalendar(endDate); while (startCal.compareTo(endCal) <= 0) { TimePeriod period = new TimePeriod(); period.setStartDate(startCal.getTime()); period.setPeriodStartDate(getPeriodStartDate((Calendar) startCal.clone()).getTime()); Calendar periodEndCal = getPeriodEndDate((Calendar) startCal.clone(), scalar); period.setEndDate(endCal.before(periodEndCal) ? endCal.getTime() : periodEndCal.getTime()); period.setPeriodEndDate(periodEndCal.getTime()); periodEndCal.add(Calendar.DATE, 1); startCal = periodEndCal; list.add(period); } return list; } private Calendar getPeriodStartDate(Calendar cal) { cal.set(Calendar.DATE, cal.getActualMinimum(Calendar.DATE)); return cal; } private Calendar getPeriodEndDate(Calendar cal, int scalar) { while (scalar-- > 0) { cal.set(Calendar.DATE, cal.getActualMaximum(Calendar.DATE)); if (scalar > 0) cal.add(Calendar.DATE, 1); } return cal; }
}
它不是最好的,但你可以使用unixtimestamp首先你会发现date的unixtime,然后popup对方
最后你应该把unixtime(sum)转换成String
这是因为类Java
date和日历使用从0-11
到0-11
的月份指数
January = 0 December = 1
build议使用乔达时间 !
TL;博士
ChronoUnit.MONTHS.between( YearMonth.from( LocalDate.of( 2009 , 1 , 29 ) ) , YearMonth.from( LocalDate.of( 2009 , 2 , 2 ) ) )
时区
Roland Tepp的答案很接近,但忽略了时区的关键问题。 确定一个月份和date需要一个时区,因为对于任何特定的时刻,date在不同地区按照地区而不同 。
ZonedDateTime
所以他将java.util.Date
对象转换为java.time.Instant
对象的例子隐式使用了UTC 。 根据定义,这两个类中的任何一个的值都是UTC。 因此,您需要将这些对象调整到所需/预期时区,以便能够提取有意义的date。
ZoneId z = ZoneId.of( "America/Montreal" ); ZonedDateTime zdtStart = myJavaUtilDate1.toInstant().atZone( z ); ZonedDateTime zdtStop = myJavaUtilDate2.toInstant().atZone( z );
YearMonth
既然您想知道您的date范围触及了多less个日历月份,而不是已过去的30天的数量,请转换为YearMonth
对象。
YearMonth start = YearMonth.from( zdtStart ); YearMonth stop = YearMonth.from( zdtStop );
ChronoUnit
通过调用ChronoUnit
enum来计算月份。
long monthsBetween = ChronoUnit.MONTHS.between( start , stop );
1
半开
你想得到2的结果,但是我们在这里得到1。 原因是在date工作中最好的做法是通过半开放方法来定义时间跨度。 在半开放,开始是包容性的,而结局是排他性的 。 我build议你坚持这个定义在整个你的date和时间工作,因为这样做最终是有道理的,消除混淆的含糊之处,并使你的工作更容易精神分析,更容易出错。 但是,如果你坚持自己的定义,只要给结果加上1
可以了,假设你有正数的结果(这意味着你的时间跨度向前而不是向后)。
LocalDate
原来的问题并不清楚,但可能只需要date值而不是date时间值。 如果是这样,请使用LocalDate
类。 LocalDate
类代表没有时间和没有时区的只有date的值。
LocalDate start = LocalDate.of( 2009 , 1 , 29 ) ; LocalDate stop = LocalDate.of( 2009 , 2 , 2 ) ; long monthsBetween = ChronoUnit.MONTHS.between( start , stop );
1
关于java.time
java.time框架内置于Java 8及更高版本中。 这些类取代了麻烦的旧的遗留date时间类,如java.util.Date
, Calendar
和SimpleDateFormat
。
Joda-Time项目现在处于维护模式 ,build议迁移到java.time类。
要了解更多信息,请参阅Oracle教程 。 并search堆栈溢出了很多例子和解释。 规范是JSR 310 。
从何处获取java.time类?
- Java SE 8和SE 9及更高版本
- 内置。
- 带有捆绑实现的标准Java API的一部分。
- Java 9增加了一些小function和修复。
- Java SE 6和SE 7
- 大部分的java.timefunction都被移植到了ThreeTen-Backport中的 Java 6&7中。
- Android的
- ThreeTenABP项目专门针对Android,采用了ThreeTen-Backport (上文提到)。
- 请参阅如何使用ThreeTenABP …。
ThreeTen-Extra项目将java.time扩展到其他类。 这个项目是未来可能增加java.time的一个试验场。 你可能会在这里find一些有用的类,比如Interval
, YearWeek
, YearQuarter
等等 。
为什么不用全时间计算
public static Integer calculateMonthDiff(Date begining, Date end) throws Exception { if (begining.compareTo(end) > 0) { throw new Exception("Beginning date is greater than the ending date"); } if (begining.compareTo(end) == 0) { return 0; } Calendar cEndCheckDate = Calendar.getInstance(); cEndCheckDate.setTime(begining); int add = 0; while (true) { cEndCheckDate.add(Calendar.MONTH, 1); add++; if (cEndCheckDate.getTime().compareTo(end) > 0) { return add - 1; } } }
查找两个date之间月份差异的完整代码片段如下所示:
public String getContractMonth(String contractStart, String contractEnd) { SimpleDateFormat dfDate = new SimpleDateFormat("yyyy-MM-dd"); String months = "0"; try { Date startDate = dfDate.parse(contractStart); Date endDate = dfDate.parse(contractEnd); Calendar startCalendar = Calendar.getInstance(); startCalendar.setTime(startDate); Calendar endCalendar = Calendar.getInstance(); endCalendar.setTime(endDate); int diffYear = endCalendar.get(Calendar.YEAR) - startCalendar.get(Calendar.YEAR); int diffMonth = diffYear * 12 + endCalendar.get(Calendar.MONTH) - startCalendar.get(Calendar.MONTH); months = diffMonth + ""; } catch (ParseException e) { e.printStackTrace(); } catch (java.text.ParseException e) { e.printStackTrace(); } return months; }
下面的逻辑会在几个月内取得你的区别
(endCal.get(Calendar.YEAR)*12+endCal.get(Calendar.MONTH))-(startCal.get(Calendar.YEAR)*12+startCal.get(Calendar.MONTH))