1

Is there an existing way in Joda-Time to round up years?

Say you have a DateTime startDate = 10-18-2016 and an DateTime endDate = 12-18-2017. I would like that to round up to 2 years.

Currently, when I do:

Years yearsBetween = Years.yearsBetween(startDate, endDate);

I get yearsBetween = 1. Is there a simple way to fix this using Joda Time or do I need to write a custom method to handle this?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
This 0ne Pr0grammer
  • 2,632
  • 14
  • 57
  • 81

2 Answers2

3

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.

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Well explained answer with exhaustive API coverage, but DOES NOT calc a round up as requested by original poster. Two dates with difference exactly one year or less then one year are calculated as 2 as well. Luckily this behavior is clearly stated in your answer. But for me and everyone seeking a round up the correct one is the answer by Jonathan Doyle. – Gandalf Mar 12 '20 at 15:47
2

This was mentioned in one of the comments above and worked for me:

LocalDate startLocalDate = new LocalDate(startDate)
LocalDate endLocalDate = new LocalDate(endDate).plusYears(1).minusDays(1)

int yearsBetween = Years.yearsBetween(startLocalDate, endLocalDate).Years
  • This is the correct answer for everyone looking for a round up in years. – Gandalf Mar 12 '20 at 15:49
  • @Gandalf How does this code round up? The [Javadoc](https://www.joda.org/joda-time/apidocs/org/joda/time/Years.html#yearsBetween-org.joda.time.ReadableInstant-org.joda.time.ReadableInstant-) says this method counts whole years. That means no rounding. – Basil Bourque Mar 12 '20 at 17:45
  • Is the `.Years` at the end of last line a typo? – Basil Bourque Mar 12 '20 at 17:45
  • @BasilBourque you are right, `yearsBetween` counts whole years. The "rounding" is achieved by this counting in combination with the `.plusYears(1).minusDays(1)` on the `endDate`. `.Years` at the end is a typo. It has to be `.getYears()` instead. – Gandalf Mar 17 '20 at 22:20
  • The same "round up" can be achieved using methods of the `java.time` API which are quite similar to the JodaTime ones. `int yearsBetween = Period.between(startDate, endDate.plusYears(1).minusDays(1)).getYears()` – Gandalf Mar 17 '20 at 22:26