ChronoUnit.DAYS.between returns wrong number of days between two java.time.LocalDate objects

47 Views Asked by At
public static long getDaysBetween(Record record) {
  if (record == null || record.getRecordedDate() == null) {
   throw new RuntimeException("Unable to extract update date from record");
 }

 LocalDate date = record.getRecordedDate().toLocalDateTime().toLocalDate();
 return ChronoUnit.DAYS.between(date, LocalDate.now());
}

If the Record getRecordedDate() is "2023-11-01", it should return the number of days between Nov 1, 2023 and today (March 19, 2024), but, instead, I get 181!

The number of days between then and now are constantly way off by literal months, as it interprets "2023-11-01" to be "now - 181 days" (like in September)

Record.getRecordedDate() returns java.sql.Timestamp, which I convert to java.time.LocalDate.

What did I do wrong?

All joda-time class libraries are off-limits to us here as are most non-Java-core libraries, so java.time is all I get to use here.

Thanks

2

There are 2 best solutions below

3
Basil Bourque On

Besides your report of flawed data as the apparent cause of your issue, you have other problems in your date handling. Your code ignores the crucial issue of time zone.


returns java.sql.Timestamp

That class is one of the terribly flawed legacy date-time classes. Avoid its use in the first place, if at all possible. Migrate to use only the java.time classes.

If handed a java.sql.Timestamp, immediately convert to its replacement, java.time.Instant.

Instant instant = myJavaSqlTimestamp.toInstant() ;

The Instant class represents a moment, a point on the timeline as seen with an offset from UTC of zero hours-minutes-seconds.

To derive a date from a moment, you must specify a time zone. For any given moment, the date varies around the globe by time zone. A moment can be “tomorrow” in Tokyo Japan while simultaneously “yesterday” in Toledo Ohio US.

ZoneId z = ZoneId.of( "America/Edmonton" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

Both instant and zdt represent the same moment, the same simultaneous point on the timeline, but are seen though two different clocks/calendars.

Extract the date portion.

LocalDate then = zdt.toLocalDate() ;

You wrote:

LocalDate.now()

Again, the date varies around the globe by time zone. If you omit an explicit time zone, the JVM’s current default time zone will be applied implicitly.

I recommend being explicit so the reader is certain of your intentions.

LocalDate today = LocalDate.now( z ) ;

Count elapsed days.

long daysElapsed = java.time.temporal.ChronoUnit.between( then , now ) ;

Tip: Generally avoid LocalDateTime class unless you are very clear on its purpose. That class cannot represent a moment as it lacks the context of time zone or offset-from-UTC.

0
user1544358 On

After doing some further research, the loop going through each Record object was somehow pulling up the wrong Record object, thus, getting the wrong Record getRecordedDate(), and, when fixed, the method works as expected for the days between the correct Record getRecordedDate() and now