tl;博士
- 将开始日期移至当年的第一天。
- 将结束日期移至其下一年的第一年。
半开
Joda-Time 对时间跨度使用半开放方法,其中开始是包含,结束是专有。因此,一年从相关年份的 1 月 1 日开始,一直持续到但不包括下一年的 1 月 1 日。
因此,您希望 2016-2017 年的日期为两年,将开始日期移至该年的第一天,并将结束日期移至其下一年的第一年。
我们转换为LocalDate,因为我们不关心这种年计算的时间。
LocalDate start = startDate.toLocalDate().withDayOfMonth( 1 ).withMonthOfYear( 1 ); // Move to first of same year.
LocalDate stop = endDate.toLocalDate().withDayOfMonth( 1 ).withMonthOfYear( 1 ).plusYears(1); // Move to first of *following* year.
然后使用Years 执行相同的计算。
Years yearsBetween = Years.yearsBetween( start , stop ); // Half-Open, beginning is inclusive while ending is exclusive.
int years = yearsBetween.getYears(); // Plain integer.
2016 年(移至 2016 年 1 月 1 日)和 2017 年(移至 2018 年 1 月 1 日)的任何日期的结果为 2。
java.time
仅供参考,Joda-Time 项目现在位于maintenance mode,团队建议迁移到java.time 类。
这个工作在java.time中稍微简单一些。 TemporalAdjuster 接口提供类来操作日期时间值。 TemporalAdjusters 类(注意复数 s)提供了几个实现。这些为获得第一年和明年的第一年做好了准备。
这里的Zdt 指的是您可能手头上的ZonedDateTime 对象。此类表示特定时区 (ZoneId) 时间线上的时刻,分辨率为纳秒。从ZonedDateTime 对象我们转换为LocalDate,然后应用一对TemporalAdjuster 对象移动到第一年和第二年。
LocalDate start = yourStartingZdt.toLocalDate().with( TemporalAdjusters.firstDayOfYear() );
LocalDate stop = yourEndingZdt.toLocalDate().with( TemporalAdjusters.firstDayOfNextYear() );
java.time 类不包括 Joda-Time Years 类的等效项。如果需要,您可以将 ThreeTen-Extra 项目添加到您的应用程序中,以使用包含 Years 类的其他类来扩展 java.time。
或者,我们可以通过Period 类获取年数。
与 Joda-Time 一样,包括 Period 在内的 java.time 类使用半开放方法来处理时间跨度。我建议您在所有日期时间工作中始终使用 Half-Open,因为它会使您的代码更加简单且不易出错。
Period p = Period.between( start , stop );
int years = p.getYears();
2016 年(移至 2016 年 1 月 1 日)和 2017 年(移至 2018 年 1 月 1 日)的任何日期的结果为 2。
关于java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、.Calendar 和 java.text.SimpleDateFormat。
Joda-Time 项目现在位于maintenance mode,建议迁移到 java.time。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
从哪里获得 java.time 类?
ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。