@Override public double calculateYearFraction(LocalDate firstDate, LocalDate secondDate, ScheduleInfo scheduleInfo) { long actualDays = daysBetween(firstDate, secondDate); int numberOfLeapDays = 0; LocalDate temp = DateAdjusters.nextLeapDay(firstDate); while (temp.isAfter(secondDate) == false) { numberOfLeapDays++; temp = DateAdjusters.nextLeapDay(temp); } return (actualDays - numberOfLeapDays) / 365d; }
@Test(dataProvider = "nextLeapDay") public void test_nextOrSameLeapDay_LocalDate(int year, int month, int day, int expectedYear) { LocalDate date = LocalDate.of(year, month, day); LocalDate test = DateAdjusters.nextOrSameLeapDay().adjust(date); if (month == 2 && day == 29) { assertEquals(test, date); } else { assertEquals(test.getYear(), expectedYear); assertEquals(test.getMonthValue(), 2); assertEquals(test.getDayOfMonth(), 29); } }
/** * Finds the next leap day after the input date. * * @param input the input date * @return the next leap day date */ static LocalDate nextLeapDay(LocalDate input) { // already a leap day, move forward either 4 or 8 years if (input.getMonthValue() == 2 && input.getDayOfMonth() == 29) { return ensureLeapDay(input.getYear() + 4); } // handle if before February 29 in a leap year if (input.isLeapYear() && input.getMonthValue() <= 2) { return LocalDate.of(input.getYear(), 2, 29); } // handle any other date return ensureLeapDay(((input.getYear() / 4) * 4) + 4); }
@Test(dataProvider = "nextLeapDay") public void test_nextOrSameLeapDay_Temporal(int year, int month, int day, int expectedYear) { LocalDate date = LocalDate.of(year, month, day); LocalDate test = (LocalDate) DateAdjusters.nextOrSameLeapDay().adjustInto(date); if (month == 2 && day == 29) { assertEquals(test, date); } else { assertEquals(test.getYear(), expectedYear); assertEquals(test.getMonthValue(), 2); assertEquals(test.getDayOfMonth(), 29); } }
/** * Finds the next leap day on or after the input date. * <p> * If the input date is February 29, the input date is returned unaltered. * Otherwise, the adjuster returns the next occurrence of February 29 after the input date. * * @param input the input date * @return the next leap day date */ static LocalDate nextOrSameLeapDay(LocalDate input) { // already a leap day, return it if (input.getMonthValue() == 2 && input.getDayOfMonth() == 29) { return input; } // handle if before February 29 in a leap year if (input.isLeapYear() && input.getMonthValue() <= 2) { return LocalDate.of(input.getYear(), 2, 29); } // handle any other date return ensureLeapDay(((input.getYear() / 4) * 4) + 4); }
@Override public int calculateDays(LocalDate firstDate, LocalDate secondDate) { long actualDays = daysBetween(firstDate, secondDate); int numberOfLeapDays = 0; LocalDate temp = DateAdjusters.nextLeapDay(firstDate); while (temp.isAfter(secondDate) == false) { numberOfLeapDays++; temp = DateAdjusters.nextLeapDay(temp); } return toIntExact(actualDays) - numberOfLeapDays; } },
@Override public double calculateYearFraction(LocalDate firstDate, LocalDate secondDate, ScheduleInfo scheduleInfo) { // tests show that there is no need to perform an initial check of period less than // or equal one year when using the OpenGamma interpretation of end-of-February rule // calculate the number of whole years back from the end // OpenGamma interpretation: reject ISDA end-of-Feb if 28th Feb, apply simple subtraction from secondDate LocalDate end = secondDate; LocalDate start = secondDate.minusYears(1); int years = 0; while (!start.isBefore(firstDate)) { years++; end = start; start = secondDate.minusYears(years + 1); } // calculate the remaining fraction, including start, excluding end long actualDays = daysBetween(firstDate, end); LocalDate nextLeap = DateAdjusters.nextOrSameLeapDay(firstDate); return years + (actualDays / (nextLeap.isBefore(end) ? 366d : 365d)); }
@Override public double calculateYearFraction(LocalDate firstDate, LocalDate secondDate, ScheduleInfo scheduleInfo) { long actualDays = daysBetween(firstDate, secondDate); LocalDate nextLeap = DateAdjusters.nextLeapDay(firstDate); return actualDays / (nextLeap.isAfter(secondDate) ? 365d : 366d); }
@Test(dataProvider = "nextLeapDay") public void test_nextLeapDay_LocalDate(int year, int month, int day, int expectedYear) { LocalDate date = LocalDate.of(year, month, day); LocalDate test = DateAdjusters.nextLeapDay().adjust(date); assertEquals(test.getYear(), expectedYear); assertEquals(test.getMonthValue(), 2); assertEquals(test.getDayOfMonth(), 29); }
@Test(dataProvider = "nextLeapDay") public void test_nextLeapDay_Temporal(int year, int month, int day, int expectedYear) { LocalDate date = LocalDate.of(year, month, day); LocalDate test = (LocalDate) DateAdjusters.nextLeapDay().adjustInto(date); assertEquals(test.getYear(), expectedYear); assertEquals(test.getMonthValue(), 2); assertEquals(test.getDayOfMonth(), 29); }
@Override public double calculateYearFraction(LocalDate firstDate, LocalDate secondDate, ScheduleInfo scheduleInfo) { long actualDays = daysBetween(firstDate, secondDate); // avoid using ScheduleInfo in this case if (firstDate.equals(secondDate)) { return 0d; } // calculation is based on the end of the schedule period (next coupon date) and annual/non-annual frequency LocalDate nextCouponDate = scheduleInfo.getPeriodEndDate(firstDate); if (scheduleInfo.getFrequency().isAnnual()) { LocalDate nextLeap = DateAdjusters.nextLeapDay(firstDate); return actualDays / (nextLeap.isAfter(nextCouponDate) ? 365d : 366d); } else { return actualDays / (nextCouponDate.isLeapYear() ? 366d : 365d); } }