java 自然月_Java按自然月计算两个日期相差的年月日

本文介绍了在Java中如何准确地计算两个日期之间的自然月、年和日的差异,针对自然月的计算,作者提供了多种解决方案,并指出JDK标准库在此功能上的不足。通过分析和测试,作者给出了一个相对复杂的计算方法,以处理各种特殊情况,包括日期边界和不同月份天数的差异。
摘要由CSDN通过智能技术生成

曾经我以为计算两个日期之差很简单,在给我的团队成员分配任务时,也觉得就是调用一个方法的问题,可是当我发现结果老是不对时,才发现原来JDK 提供的API中根本没有这样的方法,我也很恼火,也怪不得不少牛人在博客里怒斥Java标准库中对日期的处理晦涩不堪的现状,想这样的功能提供也是理所应当的,也就说明Date,Calendar中提供的日期处理的功能不够强大,因为已经有开源(Joda,某个知名的Java开源类库,在时间日期的处理上相比Java标准库更加强大且易用,IBM的日期类库中提供了强大的功能),各大论坛中对这个问题争论很多,可是很多都是考虑的比较简单,每个月按30天计算,可能你会说,计算的这么精确吗?答案是在一定的场合下是非常有必要的,在银行,图书馆过期图书计费,网络流量计费等,都是按照自然月来计算的,需要考虑的因素很多,而不是简单的30天,我现在需要的场合是Baby Care这款软件中,要计算Baby的年龄,就是xx years,xx months,xx days,因为很多人需要用这个看看小孩是否满月,是否周岁等,开始为了简单,我们按30天每月,后来有人反馈,计算方法不对,只好让用户选择,是每月按30天计算,还是按自然月计算。

按每月30天计算,论坛中常讨论的方法,,并且似乎也是没有问题的,但是往往计算的结果有时会相差一天:publiclongdiffValue(Date date1, Date date2)

{

//return date1.getTime() / (24*60*60*1000) - date2.getTime() / (24*60*60*1000);

return(date2.getTime()  - date1.getTime() )/86400000;//用立即数,减少乘法计算的开销

}

下面是正确的解法:

问题的关键是过滤掉时分秒,保留日期部分。干的活象低通滤波器,滤掉高频杂波,保留低频信号,/86400000,就是把时分秒全忽略掉了。

解法1:

这篇博客对这个问题分析的比较透彻:http://blog.csdn.net/solomonxu/archive/2007/04/27/1587237.aspxpubliclongdiffer(Date date1, Date date2)

{

//return date1.getTime() / (24*60*60*1000) - date2.getTime() / (24*60*60*1000);

returndate2.getTime() /86400000- date1.getTime() /86400000;//用立即数,减少乘法计算的开销

}

解法2:

http://blog.csdn.net/rmartin/archive/2006/12/22/1452867.aspx;http://butunclebob.com/ArticleS.UncleBob.JavaDates;

把date1,date2都设置为同样的时间,我曾经设置date1为00:00:00,date2为23:59:59秒,在非常情况下,1S之差也会导致计算的天数少1.privateintdaysBetween(Date now, Date returnDate) {

Calendar cNow = Calendar.getInstance();

Calendar cReturnDate = Calendar.getInstance();

cNow.setTime(now);

cReturnDate.setTime(returnDate);

setTimeToMidnight(cNow);

setTimeToMidnight(cReturnDate);

longtodayMs = cNow.getTimeInMillis();

longreturnMs = cReturnDate.getTimeInMillis();

longintervalMs = todayMs - returnMs;

returnmillisecondsToDays(intervalMs);

}

privateintmillisecondsToDays(longintervalMs) {

return(int) (intervalMs / (1000*86400));

}

privatevoidsetTimeToMidnight(Calendar calendar) {

calendar.set(Calendar.HOUR_OF_DAY,0);

calendar.set(Calendar.MINUTE,0);

calendar.set(Calendar.SECOND,0);

}

解法3:SimpleDateFormat myFormatter =newSimpleDateFormat("yyyy-MM-dd");

java.util.Date date= myFormatter.parse("2003-05-1");

java.util.Date mydate= myFormatter.parse("1899-12-30");

longday=(date.getTime()-mydate.getTime())/(24*60*60*1000);

out.println(day);

上面的解法思想都一样,都是忽略的时分秒,相比之下,第一种方法最为简单,可是这些都是每月按30天计算的,自然月计算的方法系统API中没有,总不至于为了使用这个方法,去引用开源的库吧,但是这个问题的处理逻辑也是很复杂的,要考虑的因素很多,往往测试的时候,发现某种特例计算的结果不正确,煞为恼火,Java真不给力~~

基本上花了一下午的时间,去分析,然后画了流程图,写成代码,可能水平有限,方法虽然笨拙,但是还是能用的,如有不正确的地方,欢迎大家指正,也期待有更加简单巧妙的方法。

126e2a168d0404757d5a8ff307fe8e21.png

相应代码:publicstaticint[] getNeturalAge(Calendar calendarBirth,Calendar calendarNow) {

intdiffYears =0, diffMonths, diffDays;

intdayOfBirth = calendarBirth.get(Calendar.DAY_OF_MONTH);

intdayOfNow = calendarNow.get(Calendar.DAY_OF_MONTH);

if(dayOfBirth <= dayOfNow) {

diffMonths = getMonthsOfAge(calendarBirth, calendarNow);

diffDays = dayOfNow - dayOfBirth;

if(diffMonths ==0)

diffDays++;

} else{

if(isEndOfMonth(calendarBirth)) {

if(isEndOfMonth(calendarNow)) {

diffMonths = getMonthsOfAge(calendarBirth, calendarNow);

diffDays = 0;

} else{

calendarNow.add(Calendar.MONTH, -1);

diffMonths = getMonthsOfAge(calendarBirth, calendarNow);

diffDays = dayOfNow + 1;

}

} else{

if(isEndOfMonth(calendarNow)) {

diffMonths = getMonthsOfAge(calendarBirth, calendarNow);

diffDays = 0;

} else{

calendarNow.add(Calendar.MONTH, -1);// 上个月

diffMonths = getMonthsOfAge(calendarBirth, calendarNow);

// 获取上个月最大的一天

intmaxDayOfLastMonth = calendarNow         .getActualMaximum(Calendar.DAY_OF_MONTH);

if(maxDayOfLastMonth > dayOfBirth) {

diffDays = maxDayOfLastMonth - dayOfBirth + dayOfNow;

} else{

diffDays = dayOfNow;

}

}

}

}

// 计算月份时,没有考虑年

diffYears = diffMonths / 12;

diffMonths = diffMonths % 12;

returnnewint[] { diffYears, diffMonths, diffDays };

}/**

* 获取两个日历的月份之差

*

* @param calendarBirth

* @param calendarNow

* @return

*/

publicstaticintgetMonthsOfAge(Calendar calendarBirth,

Calendar calendarNow) {

return(calendarNow.get(Calendar.YEAR) - calendarBirth

.get(Calendar.YEAR))*12+ calendarNow.get(Calendar.MONTH)

- calendarBirth.get(Calendar.MONTH);

}/**

* 判断这一天是否是月底

*

* @param calendar

* @return

*/

publicstaticbooleanisEndOfMonth(Calendar calendar) {

intdayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);

if(dayOfMonth == calendar.getActualMaximum(Calendar.DAY_OF_MONTH))

returntrue;

returnfalse;

}

如果你有更好的方法,欢迎探讨:-)0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值