tl;dr
- Move the start date to first of its year.
- Move the end date to first of its following year.
Half-Open
Joda-Time uses the Half-Open approach to spans of time, where the beginning is inclusive and the ending is exclusive. So a year starts from the January 1 of the year in question and runs up to, but does not include, January 1 of the following year.
So you want dates in 2016-2017 to be two years, move the starting date to the first day of the year, and move the ending date to the first year of its following year.
We convert to LocalDate
as we do not care about time-of-day for this calculation of years.
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.
Then perform your same calculation with Years
.
Years yearsBetween = Years.yearsBetween( start , stop ); // Half-Open, beginning is inclusive while ending is exclusive.
int years = yearsBetween.getYears(); // Plain integer.
Results in 2
for any dates in 2016 (moved to 2016-01-01) and 2017 (moved to 2018-01-01).
java.time
FYI, the Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes.
This work is a bit simpler in java.time. The TemporalAdjuster
interface provides for classes to manipulate date-time values. The TemporalAdjusters
class (note the plural s
) provides several implementations. These make provide for getting the first of the year and the first of the next year.
By Zdt
here, I refer to the ZonedDateTime
objects you might have in hand. This class represents a moment on the timeline in a particular time zone (ZoneId
) with a resolution of nanoseconds. From the ZonedDateTime
object we convert to a LocalDate
, and then apply a pair of TemporalAdjuster
objects to move to first of the year and of the following year.
LocalDate start = yourStartingZdt.toLocalDate().with( TemporalAdjusters.firstDayOfYear() );
LocalDate stop = yourEndingZdt.toLocalDate().with( TemporalAdjusters.firstDayOfNextYear() );
The java.time classes do not include the equivalent of the Joda-Time Years
class. If desired, you can add the ThreeTen-Extra project to your app to extend java.time with additional classes that include a Years
class.
Alternatively, we can get the number of years via the Period
class.
Like Joda-Time, java.time classes including Period
use the Half-Open approach to spans of time. I advise using Half-Open yourself consistently in all your date-time work as it will make your code over all simpler and less error-prone.
Period p = Period.between( start , stop );
int years = p.getYears();
Results in 2
for any dates in 2016 (moved to 2016-01-01) and 2017 (moved to 2018-01-01).
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, .Calendar
, & java.text.SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
- Java SE 8 and SE 9 and later
- Built-in.
- Part of the standard Java API with a bundled implementation.
- Java 9 adds some minor features and fixes.
- Java SE 6 and SE 7
- Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
- Android
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.