2015-10-05 4 views
9

При вычислении лет между двумя датами, когда вторая дата рассчитывается с первого (это упрощенный пример того, над чем я работаю), LocalDate и Period, кажется, рассчитывают год несколько иначе.Расчет лет между високосным годом

Например,

LocalDate date = LocalDate.of(1996, 2, 29); 
LocalDate plusYear = date.plusYears(1); 
System.out.println(Period.between(date, plusYear).getYears()); 

в то время как

LocalDate date = LocalDate.of(1996, 3, 29); 
LocalDate plusYear = date.plusYears(1); 
System.out.println(Period.between(date, plusYear).getYears()); 

Несмотря на явно добавлены в год, первый Period возвращение годы, как 0, в то время как второй случай возвращает 1.

Есть ли опрятный способ вокруг этого?

+0

Я считаю, что ваш производственный код на самом деле не добавляет 1 год и пытается подсчитать, сколько лет он только что добавил. Чего вы на самом деле пытаетесь достичь? Дайте нам фактический ввод и ожидаемый результат. – dotvav

+3

@dotvav: Приведенный пример кажется мне совершенно понятным ... Я не вижу в этом ничего плохого. –

+0

Если вы действительно хотите разницу в один год вместо нуля, то просто вычтите цифры года, не принимая во внимание любые месяцы или дни месяца. –

ответ

3

Этот вопрос имеет философский характер и охватывает несколько проблем, таких как измерения времени и условные обозначения формата даты.

LocalDate - это реализация стандарта обмена датами ISO 8601. Java Doc явно указывает, что этот класс не представляет время, но предоставляет только стандартную нотацию даты.

API-интерфейс обеспечивает только простые операции на самой системе, а все вычисления выполняются приращение или Года Месяц или дня данной даты.

Другими словами, при звонке LocalDate.plusYears() вы добавляете концептуальные годы по 365 дней каждый, а не точное количество времени в течение года.

Это День самая низкая единица времени, которую можно добавить к дате, выраженной LocalDate.

В человеческом понимании, Дата не является моментом во времени, но это период.

Начало работы 00h 00m 00s (...) и заканчивается 23h 59m 59s (...).

LocalDate однако позволяет избежать проблемы измерения времени и неопределенности единиц времени человека (часа, дня, месяца, и года все может иметь разную длину) и дату модели обозначение просто как кортеж :

(years, months within a year, days within a month)

рассчитывается с начала эпохи.

В этой интерпретации имеет смысл, что День - наименьшая единица, влияющая на дату.

В качестве примера следующее:

LocalDate date = LocalDate.of(1996, 2, 29); 
LocalDate plusSecond = date.plus(1, ChronoUnit.SECONDS); 

возвращается

java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Seconds 

..., который показывает, что использование LocalDate и добавление количества секунд (или более мелких единиц для привода точности), вам не смог преодолеть ограничение, указанное в вашем вопросе.

Глядя на реализацию, вы обнаружите, что LocalDate.plusYears() после добавления лет, звонки resolvePreviousValid(). Затем этот метод проверяет високосный год и изменяет день поле следующим образом:

day = Math.min(day, IsoChronology.INSTANCE.isLeapYear((long)year)?29:28);

Другими словами, он исправляет его эффективно вычитая 1 день.

Вы можете использовать Year.length(), который возвращает количество дней для данного года и вернет для високоснов. Таким образом, вы можете сделать:

LocalDate plusYear = date.plus(Year.of(date.getYear()).length(), ChronoUnit.DAYS); 

Вы все равно будете работать в следующие странности (вызов Year.length() заменены подсчетами дня для краткости):

LocalDate date = LocalDate.of(1996, 2, 29); 
LocalDate plusYear = date.plus(365, ChronoUnit.DAYS); 
System.out.println(plusYear); 
Period between = Period.between(date, plusYear); 
System.out.println(between.getYears() + "y " + 
        between.getMonths() + "m " + 
        between.getDays() + "d"); 

возвращается

1997-02-28 
0y 11m 30d 

затем

LocalDate date = LocalDate.of(1996, 3, 29); 
LocalDate plusYear = date.plus(365, ChronoUnit.DAYS); 
System.out.println(plusYear); 
Period between = Period.between(date, plusYear); 
System.out.println(between.getYears() + "y " + 
        between.getMonths() + "m " + 
        between.getDays() + "d"); 

возвращает

1997-03-29 
1y 0m 0d 

и, наконец:

LocalDate date = LocalDate.of(1996, 2, 29); 
LocalDate plusYear = date.plus(366, ChronoUnit.DAYS); 
System.out.println(plusYear); 
Period between = Period.between(date, plusYear); 
System.out.println(between.getYears() + "y " + 
        between.getMonths() + "m " + 
        between.getDays() + "d"); 

возвращает:

1997-03-01 
1y 0m 1d 

Пожалуйста, обратите внимание, что перемещение даты по вместо дней увеличился период с 11 месяцев и 30 дней до 1 год и 1 день (2 день больше!).