您的位置:首页 > 编程语言 > Java开发

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

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

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

1.public long diffValue(Date date1, Date date2) 2.{ 3. //return date1.getTime() / (24*60*60*1000) - date2.getTime() / (24*60*60*1000); 4. return (date2.getTime() - date1.getTime() )/ 86400000; //用立即数,减少乘法计算的开销 5.}

下面是正确的解法:

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

解法1:

这篇博客对这个问题分析的比较透彻:http://blog.csdn.net/solomonxu/archive/2007/04/27/1587237.aspx

1.public long differ(Date date1, Date date2) 2.{ 3. //return date1.getTime() / (24*60*60*1000) - date2.getTime() / (24*60*60*1000); 4. return date2.getTime() / 86400000 - date1.getTime() / 86400000; //用立即数,减少乘法计算的开销 5.} 6.

解法2:

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

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

1.private int daysBetween(Date now, Date returnDate) { 2. Calendar cNow = Calendar.getInstance(); 3. Calendar cReturnDate = Calendar.getInstance(); 4. cNow.setTime(now); 5. cReturnDate.setTime(returnDate); 6. setTimeToMidnight(cNow);
7. setTimeToMidnight(cReturnDate); 8. long todayMs = cNow.getTimeInMillis(); 9. long returnMs = cReturnDate.getTimeInMillis(); 10. long intervalMs = todayMs - returnMs; 11. return millisecondsToDays(intervalMs); 12. } 13. 14. private int
millisecondsToDays(long intervalMs) { 15. return (int) (intervalMs / (1000 * 86400)); 16. } 17. 18. private void setTimeToMidnight(Calendar calendar) { 19. calendar.set(Calendar.HOUR_OF_DAY, 0); 20. calendar.set(Calendar.MINUTE, 0); 21.
calendar.set(Calendar.SECOND, 0); 22. 23.} 24.

解法3:

1.SimpleDateFormat myFormatter = new SimpleDateFormat("yyyy-MM-dd"); 2.java.util.Date date= myFormatter.parse("2003-05-1"); 3.java.util.Date mydate= myFormatter.parse("1899-12-30"); 4.long day=(date.getTime()-mydate.getTime())/(24*60*60*1000); 5.out.println(day);
6.

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

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

相应代码:

1.public static int[] getNeturalAge(Calendar calendarBirth,Calendar calendarNow) { 2.int diffYears = 0, diffMonths, diffDays; 3.int dayOfBirth = calendarBirth.get(Calendar.DAY_OF_MONTH); 4.int dayOfNow = calendarNow.get(Calendar.DAY_OF_MONTH); 5.if (dayOfBirth
<= dayOfNow) { 6.diffMonths = getMonthsOfAge(calendarBirth, calendarNow); 7.diffDays = dayOfNow - dayOfBirth; 8.if (diffMonths == 0) 9.diffDays++; 10.} else { 11.if (isEndOfMonth(calendarBirth)) { 12.if (isEndOfMonth(calendarNow)) { 13.diffMonths = getMonthsOfAge(calendarBirth,
calendarNow); 14.diffDays = 0; 15.} else { 16. calendarNow.add(Calendar.MONTH, -1); 17.diffMonths = getMonthsOfAge(calendarBirth, calendarNow); 18.diffDays = dayOfNow + 1; 19.} 20.} else { 21.if (isEndOfMonth(calendarNow)) { 22.diffMonths
= getMonthsOfAge(calendarBirth, calendarNow); 23.diffDays = 0; 24.} else { 25. calendarNow.add(Calendar.MONTH, -1);// 上个月 26.diffMonths = getMonthsOfAge(calendarBirth, calendarNow); 27.// 获取上个月最大的一天 28.int maxDayOfLastMonth = calendarNow
.getActualMaximum(Calendar.DAY_OF_MONTH); 29.if (maxDayOfLastMonth > dayOfBirth) { 30.diffDays = maxDayOfLastMonth - dayOfBirth + dayOfNow; 31.} else { 32.diffDays = dayOfNow; 33.} 34.} 35.} 36.} 37.// 计算月份时,没有考虑年 38.diffYears = diffMonths / 12; 39.diffMonths
= diffMonths % 12; 40.return new int[] { diffYears, diffMonths, diffDays }; 41.} 1./** 2.* 获取两个日历的月份之差 3.* 4.* @param calendarBirth 5.* @param calendarNow 6.* @return 7.*/ 8.public static int getMonthsOfAge(Calendar calendarBirth, 9.Calendar calendarNow)
{ 10.return (calendarNow.get(Calendar.YEAR) - calendarBirth 11..get(Calendar.YEAR))* 12+ calendarNow.get(Calendar.MONTH) 12.- calendarBirth.get(Calendar.MONTH); 13. } 1./** 2.* 判断这一天是否是月底 3.* 4.* @param calendar 5.* @return 6.*/ 7.public static
boolean isEndOfMonth(Calendar calendar) { 8.int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); 9.if (dayOfMonth == calendar.getActualMaximum(Calendar.DAY_OF_MONTH)) 10.return true; 11.return false; 12. }

本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2010-12/30509.htm
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: