现代答案和概述
a)Java-8(java.time-package)
LocalDate start = LocalDate.of(1996, 2, 29);
LocalDate end = LocalDate.of(2014, 2, 28); // use for age-calculation: LocalDate.now()
long years = ChronoUnit.YEARS.between(start, end);
System.out.println(years); // 17
请注意,该表达式LocalDate.now()
与系统时区隐式相关(用户经常忽略它)。为了清楚起见,通常最好使用重载方法now(ZoneId.of("Europe/Paris"))
指定一个明确的时区(此处以“欧洲/巴黎”为例)。如果请求系统时区,那么我个人的喜好是写信LocalDate.now(ZoneId.systemDefault())
以使与系统时区的关系更清晰。这会花费更多的精力,但会使阅读更容易。
b)乔达时代
请注意,对于上面显示的日期,建议的和接受的Joda-Time-solution产生不同的计算结果(一种罕见的情况),即:
LocalDate birthdate = new LocalDate(1996, 2, 29);
LocalDate now = new LocalDate(2014, 2, 28); // test, in real world without args
Years age = Years.yearsBetween(birthdate, now);
System.out.println(age.getYears()); // 18
我认为这是一个小错误,但Joda小组对此怪异的行为持有不同的看法,并且不想解决此问题(这很奇怪,因为结束日期的月日小于开始日期,因此年份应为少一个)。另请参阅此已解决的问题。
c)java.util.Calendar等
为了进行比较,请参见其他答案。我根本不建议使用这些过时的类,因为考虑到原始问题听起来很简单,在某些特殊情况下,结果代码仍然容易出错和/或过于复杂。在2015年,我们的图书馆确实更好。
d)关于Date4J:
所提出的解决方案很简单,但是在leap年的情况下有时会失败。仅评估一年中的日期是不可靠的。
e)我自己的图书馆Time4J:
这类似于Java-8解决方案。只需更换LocalDate
的PlainDate
和ChronoUnit.YEARS
通过CalendarUnit.YEARS
。但是,“今天”需要明确的时区参考。
PlainDate start = PlainDate.of(1996, 2, 29);
PlainDate end = PlainDate.of(2014, 2, 28);
// use for age-calculation (today):
// => end = SystemClock.inZonalView(EUROPE.PARIS).today();
// or in system timezone: end = SystemClock.inLocalView().today();
long years = CalendarUnit.YEARS.between(start, end);
System.out.println(years); // 17