net.objectlab.kit.datecalc.common.AbstractDateCalculator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datecalc-common Show documentation
Show all versions of datecalc-common Show documentation
Common Date Calculator Code
/*
* ObjectLab, http://www.objectlab.co.uk/open is sponsoring the ObjectLab Kit.
*
* Based in London, we are world leaders in the design and development
* of bespoke applications for the securities financing markets.
*
* Click here to learn more
* ___ _ _ _ _ _
* / _ \| |__ (_) ___ ___| |_| | __ _| |__
* | | | | '_ \| |/ _ \/ __| __| | / _` | '_ \
* | |_| | |_) | | __/ (__| |_| |__| (_| | |_) |
* \___/|_.__// |\___|\___|\__|_____\__,_|_.__/
* |__/
*
* www.ObjectLab.co.uk
*
* $Id: AbstractDateCalculator.java 309 2010-03-23 21:01:49Z marchy $
*
* Copyright 2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package net.objectlab.kit.datecalc.common;
import static net.objectlab.kit.datecalc.common.HolidayHandlerType.BACKWARD;
import static net.objectlab.kit.datecalc.common.HolidayHandlerType.FORWARD;
import static net.objectlab.kit.datecalc.common.HolidayHandlerType.MODIFIED_FOLLOWING;
import static net.objectlab.kit.datecalc.common.HolidayHandlerType.MODIFIED_PRECEDING;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Abstract implementation in order to encapsulate all the common functionality
* between Jdk and Joda implementations. It is parametrized on
* but basically Date
and LocalDate
are the only
* viable values for it for now.
*
* @author Marcin Jekot and Benoit Xhenseval
* @author $LastChangedBy: marchy $
* @version $Revision: 309 $ $Date: 2010-03-23 17:01:49 -0400 (Tue, 23 Mar 2010) $
*
* @param
* a representation of a date, typically JDK: Date, Calendar;
* Joda:LocalDate, YearMonthDay
*
*/
public abstract class AbstractDateCalculator implements DateCalculator {
private static final int MONTHS_IN_YEAR = 12;
protected static final int DAYS_IN_WEEK = 7;
private String name;
private E startDate;
private E currentBusinessDate;
private HolidayCalendar holidayCalendar;
private HolidayHandler holidayHandler;
private int currentIncrement = 0;
protected AbstractDateCalculator(final String name, final HolidayCalendar holidayCalendar, final HolidayHandler holidayHandler) {
this.name = name;
if (holidayCalendar != null) {
this.holidayCalendar = new ImmutableHolidayCalendar(holidayCalendar);
} else {
this.holidayCalendar = new ImmutableHolidayCalendar(new DefaultHolidayCalendar());
}
this.holidayHandler = holidayHandler;
}
@SuppressWarnings("unchecked")
public void setHolidayCalendar(final HolidayCalendar calendar) {
if (calendar != null) {
if (calendar instanceof ImmutableHolidayCalendar) {
holidayCalendar = calendar;
} else {
holidayCalendar = new ImmutableHolidayCalendar(calendar);
}
} else {
holidayCalendar = new ImmutableHolidayCalendar(new DefaultHolidayCalendar());
}
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public E getStartDate() {
if (startDate == null) {
startDate = getToday();
}
return startDate;
}
/** Set both start date and current date */
public void setStartDate(final E startDate) {
this.startDate = startDate;
setCurrentBusinessDate(startDate);
}
public E getCurrentBusinessDate() {
if (currentBusinessDate == null) {
currentBusinessDate = getToday();
}
return currentBusinessDate;
}
/**
* move the current date by a given tenor, this means that if a date is
* either a 'weekend' or holiday, it will be skipped acording to the holiday
* handler and not count towards the number of days to move.
*
* @param tenor the tenor.
* @param spotLag
* number of days to "spot" days, this can vary from one market
* to the other.
* @return the current businessCalendar (so one can do
* calendar.moveByTenor(StandardTenor.T_2M).getCurrentBusinessDate();)
*/
public DateCalculator moveByTenor(final Tenor tenor, final int spotLag) {
if (tenor == null) {
throw new IllegalArgumentException("Tenor cannot be null");
}
TenorCode tenorCode = tenor.getCode();
if (tenorCode != TenorCode.OVERNIGHT && tenorCode != TenorCode.TOM_NEXT && spotLag != 0) {
// get to the Spot date first:
moveByBusinessDays(spotLag);
}
int unit = tenor.getUnits();
if (tenorCode == TenorCode.WEEK) {
tenorCode = TenorCode.DAY;
unit *= DAYS_IN_WEEK;
}
if (tenorCode == TenorCode.YEAR) {
tenorCode = TenorCode.MONTH;
unit *= MONTHS_IN_YEAR;
}
return applyTenor(tenorCode, unit);
}
private DateCalculator applyTenor(final TenorCode tenorCode, final int unit) {
DateCalculator calc;
// move by tenor
switch (tenorCode) {
case OVERNIGHT:
calc = moveByDays(1);
break;
case TOM_NEXT: // it would have NOT moved by
calc = moveByDays(1); // calculate Tomorrow
calc = moveByDays(1); // then the next!
break;
case SPOT_NEXT:
calc = moveByDays(1);
break;
case SPOT:
calc = this;
break;
case DAY:
calc = moveByDays(unit);
break;
case MONTH:
calc = moveByMonths(unit);
break;
default:
throw new UnsupportedOperationException("Sorry not yet...");
}
return calc;
}
/**
* Move the current date by a given tenor, please note that all tenors are
* relative to the CURRENT day (and NOT from spot).
*
* @param tenor
* the Tenor to reach.
* @return the current DateCalculator
* @since 1.1.0
*/
public DateCalculator moveByTenor(final Tenor tenor) {
return moveByTenor(tenor, 0);
}
/**
* Calculate a series of Tenor codes in one go based on current day,
* this does NOT change the current business date.
*
* @return list of dates in same order as tenors.
* @since 1.1.0
*/
public List calculateTenorDates(final List tenors) {
return calculateTenorDates(tenors, 0);
}
/**
* Calculate a series of Tenor codes in one go based on SPOT day (calculated
* with the spot lag), this does NOT change the current business date.
*
* @return list of dates in same order as tenors.
* @since 1.1.0
*/
public List calculateTenorDates(final List tenors, final int spotLag) {
final List list = new ArrayList();
if (tenors != null) {
final E date = clone(getCurrentBusinessDate());
for (final Tenor tenor : tenors) {
moveByTenor(tenor, spotLag);
list.add(getCurrentBusinessDate());
setCurrentBusinessDate(date);
}
}
return list;
}
// -----------------------------------------------------------------------
//
// ObjectLab, world leaders in the design and development of bespoke
// applications for the securities financing markets.
// www.ObjectLab.co.uk
//
// -----------------------------------------------------------------------
protected abstract DateCalculator moveByMonths(int months);
public void setHolidayHandler(final HolidayHandler holidayHandler) {
this.holidayHandler = holidayHandler;
}
public String getHolidayHandlerType() {
return (holidayHandler != null ? holidayHandler.getType() : null);
}
/**
* is the given date a non working day?
*/
public boolean isNonWorkingDay(final E date) {
if (date != null && (holidayCalendar.getEarlyBoundary() != null || holidayCalendar.getLateBoundary() != null)) {
checkBoundary(date);
}
return (isWeekend(date) || holidayCalendar.isHoliday(date));
}
/**
* This may throw an {@link IndexOutOfBoundsException} if the date is not within the
* boundaries.
* @param date
*/
protected abstract void checkBoundary(E date);
public boolean isCurrentDateNonWorking() {
if (currentBusinessDate == null) {
currentBusinessDate = getToday();
}
return isNonWorkingDay(currentBusinessDate);
}
public E setCurrentBusinessDate(final E date) {
currentBusinessDate = date;
if (holidayHandler != null && date != null) {
currentBusinessDate = holidayHandler.moveCurrentDate(this);
}
if (date != null && (holidayCalendar.getEarlyBoundary() != null || holidayCalendar.getLateBoundary() != null)) {
checkBoundary(date);
}
return currentBusinessDate;
}
public HolidayHandler getHolidayHandler() {
return holidayHandler;
}
public DateCalculator moveByBusinessDays(final int businessDays) {
checkHolidayValidity(businessDays);
final int numberOfStepsLeft = Math.abs(businessDays);
final int step = (businessDays < 0 ? -1 : 1);
for (int i = 0; i < numberOfStepsLeft; i++) {
moveByDays(step);
}
return this;
}
private void checkHolidayValidity(final int businessDays) {
if (businessDays > 0 && holidayHandler != null && (holidayHandler.getType().equals(BACKWARD) || holidayHandler.getType().equals(MODIFIED_PRECEDING))) {
throw new IllegalArgumentException("A " + MODIFIED_PRECEDING + " or " + BACKWARD + " does not allow positive steps for moveByBusinessDays");
} else if (businessDays < 0 && holidayHandler != null
&& (holidayHandler.getType().equals(FORWARD) || holidayHandler.getType().equals(MODIFIED_FOLLOWING))) {
throw new IllegalArgumentException("A " + MODIFIED_FOLLOWING + " or " + FORWARD + " does not allow negative steps for moveByBusinessDays");
}
}
/**
* Allows DateCalculators to be combined into a new one, the startDate and
* currentBusinessDate will be the ones from the existing calendar (not the
* parameter one). The name will be combined name1+"/"+calendar.getName().
*
* @param calculator
* return the same DateCalculator if calender is null or the
* original calendar (but why would you want to do that?)
* @throws IllegalArgumentException
* if both calendars have different types of HolidayHandlers or
* WorkingWeek;
*/
public DateCalculator combine(final DateCalculator calculator) {
if (calculator == null || calculator == this) {
return this;
}
checkHolidayHandlerValidity(calculator);
final Set newSet = new HashSet();
if (holidayCalendar != null) {
newSet.addAll(holidayCalendar.getHolidays());
}
final HolidayCalendar calendarToCombine = calculator.getHolidayCalendar();
checkBoundaries(calendarToCombine);
if (calendarToCombine.getHolidays() != null) {
newSet.addAll(calendarToCombine.getHolidays());
}
final HolidayCalendar newCal = new DefaultHolidayCalendar(newSet, compareDate(holidayCalendar.getEarlyBoundary(), calendarToCombine
.getEarlyBoundary(), false), compareDate(holidayCalendar.getLateBoundary(), calendarToCombine.getLateBoundary(), true));
final DateCalculator cal = createNewCalculator(getName() + "/" + calculator.getName(), getStartDate(), newCal, holidayHandler);
return cal;
}
private void checkHolidayHandlerValidity(final DateCalculator calculator) {
if (holidayHandler == null && calculator.getHolidayHandlerType() != null || holidayHandler != null
&& !holidayHandler.getType().equals(calculator.getHolidayHandlerType())) {
throw new IllegalArgumentException("Combined Calendars cannot have different handler types");
}
}
private void checkBoundaries(final HolidayCalendar calendarToCombine) {
if (calendarToCombine.getEarlyBoundary() != null && holidayCalendar.getEarlyBoundary() == null || calendarToCombine.getEarlyBoundary() == null
&& holidayCalendar.getEarlyBoundary() != null) {
throw new IllegalArgumentException("Both Calendar to be combined must either have each Early boundaries or None.");
}
if (calendarToCombine.getLateBoundary() != null && holidayCalendar.getLateBoundary() == null || calendarToCombine.getLateBoundary() == null
&& holidayCalendar.getLateBoundary() != null) {
throw new IllegalArgumentException("Both Calendar to be combined must either have each Late boundaries or None.");
}
}
protected abstract E getToday();
protected abstract E compareDate(E date1, E date2, boolean returnEarliest);
protected abstract DateCalculator createNewCalculator(String calcName, E theStartDate, HolidayCalendar holidays, HolidayHandler handler);
/**
* @return Returns the currentIncrement.
*/
public int getCurrentIncrement() {
return currentIncrement;
}
/**
* @param currentIncrement The currentIncrement to set.
*/
public void setCurrentIncrement(final int currentIncrement) {
this.currentIncrement = currentIncrement;
}
/**
* @return Returns the holidayCalendar.
*/
public HolidayCalendar getHolidayCalendar() {
return holidayCalendar;
}
protected abstract E clone(final E date);
}
/*
* ObjectLab, http://www.objectlab.co.uk/open is sponsoring the ObjectLab Kit.
*
* Based in London, we are world leaders in the design and development
* of bespoke applications for the securities financing markets.
*
* Click here to learn more about us
* ___ _ _ _ _ _
* / _ \| |__ (_) ___ ___| |_| | __ _| |__
* | | | | '_ \| |/ _ \/ __| __| | / _` | '_ \
* | |_| | |_) | | __/ (__| |_| |__| (_| | |_) |
* \___/|_.__// |\___|\___|\__|_____\__,_|_.__/
* |__/
*
* www.ObjectLab.co.uk
*/
© 2015 - 2024 Weber Informatics LLC | Privacy Policy