/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.util.time;
import org.threeten.bp.LocalDate;
import org.threeten.bp.LocalDateTime;
import org.threeten.bp.Period;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.financial.convention.calendar.Calendar;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.time.Tenor;
import com.opengamma.util.time.Tenor.BusinessDayTenor;
/**
* Utility class for {@link Tenor}s.
*/
public class TenorUtils {
/**
* Adjusts a {@link ZonedDateTime} by a tenor that is backed by a {@link Period}.
* @param date The date to adjust, not null
* @param tenor The tenor, not null
* @return The date adjusted by a tenor
* @throws IllegalStateException If the tenor is not backed by a {@link Period}
*/
public static ZonedDateTime adjustDateByTenor(final ZonedDateTime date, final Tenor tenor) {
ArgumentChecker.notNull(date, "date");
ArgumentChecker.notNull(tenor, "tenor");
ArgumentChecker.isTrue(!tenor.isBusinessDayTenor(), "Business day tenors must have a calendar and initial offset to adjust a date");
return date.plus(tenor.getPeriod());
}
/**
* Adjusts a {@link LocalDateTime} by a tenor that is backed by a {@link Period}.
* @param date The date to adjust, not null
* @param tenor The tenor, not null
* @return The date adjusted by a tenor
* @throws IllegalStateException If the tenor is not backed by a {@link Period}
*/
public static LocalDateTime adjustDateByTenor(final LocalDateTime date, final Tenor tenor) {
ArgumentChecker.notNull(date, "date");
ArgumentChecker.notNull(tenor, "tenor");
ArgumentChecker.isTrue(!tenor.isBusinessDayTenor(), "Business day tenors must have a calendar and initial offset to adjust a date");
return date.plus(tenor.getPeriod());
}
/**
* Adjusts a {@link LocalDate} by a tenor that is backed by a {@link Period}.
* @param date The date to adjust, not null
* @param tenor The tenor, not null
* @return The date adjusted by a tenor
* @throws IllegalStateException If the tenor is not backed by a {@link Period}
*/
public static LocalDate adjustDateByTenor(final LocalDate date, final Tenor tenor) {
ArgumentChecker.notNull(date, "date");
ArgumentChecker.notNull(tenor, "tenor");
ArgumentChecker.isTrue(!tenor.isBusinessDayTenor(), "Business day tenors must have a calendar and initial offset to adjust a date");
return date.plus(tenor.getPeriod());
}
/**
* Adjusts a {@link ZonedDateTime} by a tenor. If the tenor is backed by a {@link BusinessDayTenor}, the calendar and
* spot days are used when adjusting.
* @param date The date to adjust, not null
* @param tenor The tenor, not null
* @param calendar The calendar, not null
* @param spotDays The number of days for spot, greater than or equal to zero
* @return The date adjusted by a tenor
*/
public static ZonedDateTime adjustDateByTenor(final ZonedDateTime date, final Tenor tenor, final Calendar calendar, final int spotDays) {
ArgumentChecker.notNull(date, "date");
ArgumentChecker.notNull(tenor, "tenor");
ArgumentChecker.notNull(calendar, "calendar");
ArgumentChecker.isTrue(spotDays >= 0, "number of spot days must be greater than zero; have {}", spotDays);
if (tenor.isBusinessDayTenor()) {
int offset;
final BusinessDayTenor bdt = tenor.getBusinessDayTenor();
switch (bdt) {
case OVERNIGHT:
offset = 0;
break;
case TOM_NEXT:
offset = 1;
break;
case SPOT_NEXT:
offset = spotDays;
break;
default:
throw new IllegalArgumentException("Did not recognise tenor " + tenor);
}
ZonedDateTime result = date;
int count = 0;
while (count < offset) {
result = result.plusDays(1);
if (calendar.isWorkingDay(result.toLocalDate())) {
count++;
}
}
result = result.plusDays(1);
while (!calendar.isWorkingDay(result.toLocalDate())) {
result = result.plusDays(1);
}
return result;
}
return date.plus(tenor.getPeriod());
}
/**
* Adjusts a {@link LocalDateTime} by a tenor. If the tenor is backed by a {@link BusinessDayTenor}, the calendar and
* spot days are used when adjusting.
* @param date The date to adjust, not null
* @param tenor The tenor, not null
* @param calendar The calendar, not null
* @param spotDays The number of days for spot, greater than or equal to zero
* @return The date adjusted by a tenor
*/
public static LocalDateTime adjustDateByTenor(final LocalDateTime date, final Tenor tenor, final Calendar calendar, final int spotDays) {
ArgumentChecker.notNull(date, "date");
ArgumentChecker.notNull(tenor, "tenor");
ArgumentChecker.notNull(calendar, "calendar");
ArgumentChecker.isTrue(spotDays >= 0, "number of spot days must be greater than zero; have {}", spotDays);
if (tenor.isBusinessDayTenor()) {
int offset;
final BusinessDayTenor bdt = tenor.getBusinessDayTenor();
switch (bdt) {
case OVERNIGHT:
offset = 0;
break;
case TOM_NEXT:
offset = 1;
break;
case SPOT_NEXT:
offset = spotDays;
break;
default:
throw new IllegalArgumentException("Did not recognise tenor " + tenor);
}
LocalDateTime result = date;
int count = 0;
while (count < offset) {
result = result.plusDays(1);
if (calendar.isWorkingDay(result.toLocalDate())) {
count++;
}
}
result = result.plusDays(1);
while (!calendar.isWorkingDay(result.toLocalDate())) {
result = result.plusDays(1);
}
return result;
}
return date.plus(tenor.getPeriod());
}
/**
* Adjusts a {@link LocalDate} by a tenor. If the tenor is backed by a {@link BusinessDayTenor}, the calendar and
* spot days are used when adjusting.
* @param date The date to adjust, not null
* @param tenor The tenor, not null
* @param calendar The calendar, not null
* @param spotDays The number of days for spot, greater than or equal to zero
* @return The date adjusted by a tenor
*/
public static LocalDate adjustDateByTenor(final LocalDate date, final Tenor tenor, final Calendar calendar, final int spotDays) {
ArgumentChecker.notNull(date, "date");
ArgumentChecker.notNull(tenor, "tenor");
ArgumentChecker.notNull(calendar, "calendar");
ArgumentChecker.isTrue(spotDays >= 0, "number of spot days must be greater than zero; have {}", spotDays);
if (tenor.isBusinessDayTenor()) {
int offset;
final BusinessDayTenor bdt = tenor.getBusinessDayTenor();
switch (bdt) {
case OVERNIGHT:
offset = 0;
break;
case TOM_NEXT:
offset = 1;
break;
case SPOT_NEXT:
offset = spotDays;
break;
default:
throw new IllegalArgumentException("Did not recognise tenor " + tenor);
}
LocalDate result = date;
int count = 0;
while (count < offset) {
result = result.plusDays(1);
if (calendar.isWorkingDay(result)) {
count++;
}
}
result = result.plusDays(1);
while (!calendar.isWorkingDay(result)) {
result = result.plusDays(1);
}
return result;
}
return date.plus(tenor.getPeriod());
}
/**
* Add two tenors when it make sense.
* When the two tenors are backed by period, create a tenor backed by adding the periods.
* When one of the tenor is backed by a ZERO period, return the other tenor.
* When both tenors are ON, returns TN.
* In all other cases throw an exception.
* @param tenor1 The first tenor.
* @param tenor2 The second tenor.
* @return The sum tenor.
* @throws IllegalArgumentException as described above.
*/
public static Tenor plus(final Tenor tenor1, final Tenor tenor2) {
if ((!tenor1.isBusinessDayTenor()) && (!tenor2.isBusinessDayTenor())) { // Standard periods
return Tenor.of(tenor1.getPeriod().plus(tenor2.getPeriod()));
}
if (tenor1.equals(Tenor.of(Period.ZERO))) { // First tenor is ZERO
return tenor2;
}
if (tenor2.equals(Tenor.of(Period.ZERO))) { // Second tenor is ZERO
return tenor1;
}
if ((tenor1.equals(Tenor.ON)) && (tenor1.equals(Tenor.ON))) { // Both tenors are ON
return Tenor.TN;
}
throw new IllegalArgumentException("Can not add " + tenor1 + " and " + tenor2);
}
}