-->

Java Time period in decimal number of years

2020-05-06 07:55发布

问题:

If I calculate the difference between 2 LocalDate's in java.time using:

Period p = Period.between(testDate, today);

Then I get an output with the number of years, months, days like:

Days = 9
Months = 6
Years = 18

Does anyone know a clean way to represent that as a decimal type value (ie, above would be something around 18.5...)?

回答1:

I would avoid using Period, and instead just calculate the difference in days:

float years = testDate1.until(today, ChronoUnit.DAYS) / 365.2425f;


回答2:

You mentioned in one of your comments that you need quarter year precision if you need the current quarter you can use IsoFields.QUARTER_YEARS:

double yearAndQuarter = testDate.until(today, IsoFields.QUARTER_YEARS) / 4.0;

This way you will actually use the time api, always get the correct result and @Mike won't have to loathe anything.



回答3:

Please do not do this.

Representing the difference between two dates as a 'number of years' multiplier is problematic because the average length of a year between two dates is dependent on which dates you are comparing. It's easy to get this wrong, and it's much harder to come up with all the test cases necessary to prove you got it right.

Most programmers should never perform date/time calculations manually. You are virtually guaranteed to get it wrong. Seriously, there are so many ways things can go horribly wrong. Only a handful of programmers on the planet fully understand the many subtleties involved. The fact that you are asking this question proves that you are not one of them, and that's okay--neither am I. You, along with the vast majority of us, should rely on a solid Date/Time API like java.util.time.

If you really need a single numeric value, then the safest option I can think of is to use the number of days, because the LocalDate API can calculate that number for you:

long differenceInDays = testDate.until(today, ChronoUnit.DAYS)

Note that this difference is only valid for the two dates used to produce it. The round-trip conversion is straightforward:

LocalDate today = testDate.plus(differenceInDays, ChronoUnit.DAYS)

Do not attempt to manually convert a Period with year, month, and day components into a whole number of days. The correct answer depends on the dates involved, which is why we want to let the LocalDate API calculate it for us.


When precision isn't important

Based on your comments, precision isn't an issue for you, because you only want to display someone's age to the nearest quarter-year or so. You aren't trying to represent an exact difference in time; only an approximate one, with a rather large margin for error. You also don't need to be able to perform any round-trip calculations. This changes things considerably.

An approximation like @VGR's should be more than adequate for these purposes: the 'number of years' should be accurate to within 3 days (< 0.01 years) unless people start living hundreds of thousands of years, in which case you can switch to double ;).

@Oleg's approach also works quite well, and will give you a date difference in whole quarters, which you can divide by 4 to convert to years. This is probably the easiest solution to get right, as you won't need to round or truncate the result. This is, I think, the closest you will get to a direct solution from java.util.time. The Java Time API (and date/time APIs in general) are designed for correctness: they'll give you whole units, but they usually avoid giving you fractional approximations due to the inherent error involved in floating-point types (there are exceptions, like .NET's System.TimeSpan).

However, if your goal is to present someone's age for human users, and you want greater precision than whole years, I think 18 years, 9 months (or an abbreviated form like 18 yr, 9 mo) is a better choice than 18.75 years.