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

简介:

曾经我以为计算两个日期之差很简单,在给我的团队成员分配任务时,也觉得就是调用一个方法的问题,可是当我发现结果老是不对时,才发现原来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:

这篇博客对这个问题分析的比较透彻:

  
  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真不给力~~

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

 

breezy

相应代码:

  
  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. else { 
  20. if (isEndOfMonth(calendarNow)) { 
  21. diffMonths = getMonthsOfAge(calendarBirth, calendarNow); 
  22. diffDays = 0
  23. else { 
  24.                     calendarNow.add(Calendar.MONTH, -1);// 上个月 
  25. diffMonths = getMonthsOfAge(calendarBirth, calendarNow); 
  26. // 获取上个月最大的一天 
  27. int maxDayOfLastMonth = calendarNow         .getActualMaximum(Calendar.DAY_OF_MONTH); 
  28. if (maxDayOfLastMonth > dayOfBirth) { 
  29. diffDays = maxDayOfLastMonth - dayOfBirth + dayOfNow; 
  30. else { 
  31. diffDays = dayOfNow; 
  32. // 计算月份时,没有考虑年 
  33. diffYears = diffMonths / 12
  34. diffMonths = diffMonths % 12
  35. return new int[] { diffYears, diffMonths, diffDays }; 
  
  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.     } 

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










本文转自 breezy_yuan 51CTO博客,原文链接:http://blog.51cto.com/lbrant/451353,如需转载请自行联系原作者
目录
打赏
0
0
0
0
236
分享
相关文章
在Java中将String字符串转换为算术表达式并计算
具体的实现逻辑需要填写在 `Tokenizer`和 `ExpressionParser`类中,这里只提供了大概的框架。在实际实现时 `Tokenizer`应该提供分词逻辑,把输入的字符串转换成Token序列。而 `ExpressionParser`应当通过递归下降的方式依次解析
69 14
|
21天前
|
Java语言按文件创建日期排序及获取最新文件的技术
这段代码实现了文件创建时间的读取、文件列表的获取与排序以及获取最新文件的需求。它具备良好的效率和可读性,对于绝大多数处理文件属性相关的需求来说足够健壮。在实际应用中,根据具体情况,可能还需要进一步处理如访问权限不足、文件系统不支持某些属性等边界情况。
74 14
|
5月前
|
Java计算时间差
这段代码提供了两个方法来计算时间差。`timeDistance` 方法接收两个 `Date` 对象,计算并返回两者之间的天数、小时数和分钟数差异,格式为“X天Y小时Z分钟”。`hourDistance` 方法则接收两个时间字符串,解析后计算并返回两者之间相差的小时数(向上取整)。
186 4
Java 日期与时间处理:精准掌控时间流转
Java 8引入了全新的日期和时间API,解决了旧版`java.util.Date`和`Calendar`类设计不佳、操作繁琐的问题。新API包括`LocalDate`、`LocalTime`和`LocalDateTime`类,操作简洁直观,符合日常思维习惯。同时提供了`Period`和`Duration`处理时间间隔,以及`DateTimeFormatter`进行格式化输出。这些改进使开发者能更高效、准确地处理日期和时间,极大提升了开发效率与代码质量。 (239字符)
134 6
|
7月前
|
java中的常见运算符的计算方式
本文介绍了计算机中二进制数的原码、反码和补码的概念及其转换方式。原码是符号位加真值的绝对值;反码中正数不变,负数其余位取反;补码在反码基础上加1。文章还详细解释了Java中的常见运算符(如按位与、或、异或、移位等)如何基于二进制进行计算,并探讨了使用补码的原因,包括统一符号位处理和扩展表示范围。通过具体代码示例帮助理解这些概念。
141 6
java中的常见运算符的计算方式
如何在Java中计算绝对值
绝对值表示一个数离0的距离,总是非负的。在Java中,可以通过`Math.abs()`函数或`if-else`条件语句来计算绝对值。使用`Math.abs()`可直接将负数转为正数,而`if-else`则根据条件判断是否取反。本文介绍了这两种方法的具体实现步骤和代码示例,并展示了如何通过用户输入获取数值并输出其绝对值。此外,还提供了完整的代码和编译执行的方法。
265 6
如何在Java中计算绝对值
|
8月前
|
java小工具util系列1:日期和字符串转换工具
java小工具util系列1:日期和字符串转换工具
150 26
|
8月前
|
告别SimpleDateFormat:Java 8日期时间API的最佳实践
在Java开发中,处理日期和时间是一个基本而重要的任务。传统的`SimpleDateFormat`类因其简单易用而被广泛采用,但它存在一些潜在的问题,尤其是在多线程环境下。本文将探讨`SimpleDateFormat`的局限性,并介绍Java 8引入的新的日期时间API,以及如何使用这些新工具来避免潜在的风险。
111 5
存算分离与计算向数据移动:深度解析与Java实现
【11月更文挑战第10天】随着大数据时代的到来,数据量的激增给传统的数据处理架构带来了巨大的挑战。传统的“存算一体”架构,即计算资源与存储资源紧密耦合,在处理海量数据时逐渐显露出其局限性。为了应对这些挑战,存算分离(Disaggregated Storage and Compute Architecture)和计算向数据移动(Compute Moves to Data)两种架构应运而生,成为大数据处理领域的热门技术。
312 2
ODPS MR节点跑graph连通分量计算代码报错java heap space如何解决
任务启动命令:jar -resources odps-graph-connect-family-2.0-SNAPSHOT.jar -classpath ./odps-graph-connect-family-2.0-SNAPSHOT.jar ConnectFamily 若是设置参数该如何设置
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问