/*
* jFin, open source derivatives trade processing
*
* Copyright (C) 2005, 2006, 2007 Morgan Brown Consultancy Ltd.
*
* This file is part of jFin.
*
* jFin is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* jFin is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License along
* with jFin; if not, write to the Free Software Foundation, Inc., 51 Franklin
* St, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jfin.date.holiday;
import org.jfin.date.BusinessDayConvention;
import org.jfin.date.Period;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.logging.Logger;
/**
* Abstract template class for all HolidayCalendar implementations.
*
* Provides reference implementations for adjusting Calendars by a business day
* or by a specific amount, adjust Calendars and Periods, and find the business
* days between two dates.
*
* In some implementations of the concrete HolidayCalendar it may be possible to
* create higher performance calculations and adjustments.
*
* @author dmb
*
*/
public abstract class HolidayCalendar
{
private static Logger logger = Logger
.getLogger("org.jfin.date.holiday.HolidayCalendar");
/**
* Returns true if the Calendar falls on a weekend, otherwise false.
*
* @param d
* The Calendar to check
* @return true if the Calendar is a weekend, otherwise false
*/
public abstract boolean isWeekend(Calendar d);
/**
* Returns true if the Calendar falls on a holiday or a weekend, otherwise
* false.
*
* @param d
* The Calendar to check
* @return true if the Calendar is a holiday, otherwise false
*/
public abstract boolean isHoliday(Calendar d);
/**
* Advances the date by offsetAmount business days according to the given
* holiday calendar.
*
* @param calendar
* @param offsetAmount
* The number of business days to offset. Positive is forward,
* negative backwards.
* @return The offset financial date
* @throws HolidayCalendarException
*/
public Calendar advanceBusinessDays(Calendar calendar, int offsetAmount)
throws HolidayCalendarException
{
return advance(calendar, offsetAmount, Calendar.DAY_OF_YEAR,
BusinessDayConvention.UNADJUSTED);
}
/**
*
* @param calendar
* @param offsetAmount
* The amount to offset. Positive is forward, negative backwards.
* @param offsetUnit
* The unit to offset by. Of form Calendar.DATE, Calendar.MONTH,
* etc.
* @param businessDayConvention
* @return The offset financial date
* @throws HolidayCalendarException
*/
public Calendar advance(Calendar calendar, int offsetAmount,
int offsetUnit, BusinessDayConvention businessDayConvention)
throws HolidayCalendarException
{
if (offsetAmount == 0)
{
return adjust(calendar, businessDayConvention);
} else if (offsetUnit == Calendar.DAY_OF_MONTH
|| offsetUnit == Calendar.DATE
|| offsetUnit == Calendar.DAY_OF_YEAR)
{
Calendar d1 = (Calendar) calendar.clone();
if (offsetAmount > 0)
{
while (offsetAmount > 0)
{
d1.add(Calendar.DATE, 1);
while (isHoliday(d1) || isWeekend(d1))
d1.add(Calendar.DATE, 1);
offsetAmount--;
}
} else
{
while (offsetAmount < 0)
{
d1.add(Calendar.DATE, -1);
while (isHoliday(d1) || isWeekend(d1))
d1.add(Calendar.DATE, -1);
offsetAmount++;
}
}
return d1;
} else if (offsetUnit == Calendar.MONTH)
{
Calendar d1 = (Calendar) calendar.clone();
d1.add(Calendar.MONTH, offsetAmount);
return adjust(d1, businessDayConvention, calendar);
} else if (offsetUnit == Calendar.YEAR)
{
Calendar d1 = (Calendar) calendar.clone();
d1.add(Calendar.YEAR, offsetAmount);
return adjust(d1, businessDayConvention, calendar);
} else
{
throw new HolidayCalendarException("Unknown time period");
}
}
/**
* Adjust the dates in the financial periods using the given business day
* convention and holiday calendar.
*
* @param periods
* @param businessDayConvention
* @return The adjusted periods.
* @throws HolidayCalendarException
*/
public List adjust(List periods, BusinessDayConvention businessDayConvention) throws HolidayCalendarException {
List returnPeriods = new ArrayList();
for(T period: periods) {
returnPeriods.add(adjust(period,businessDayConvention));
}
return returnPeriods;
}
/**
* Adjust the dates in the financial period using the given business day
* convention and holiday calendar.
*
* @param period
* @param businessDayConvention
* @return The adjusted periods.
* @throws HolidayCalendarException
*/
public T adjust(T period,
BusinessDayConvention businessDayConvention)
throws HolidayCalendarException
{
logger.fine("Adjusting financial period " + businessDayConvention
+ " by " + this);
T adjustedPeriod = (T)period.clone();
adjustedPeriod.setStartCalendar(adjust(period.getStartCalendar(),businessDayConvention));
adjustedPeriod.setEndCalendar(adjust(period.getEndCalendar(),businessDayConvention));
return adjustedPeriod;
}
/**
* Adjust the financial date using the given business day convention and
* holiday calendar.
*
* @param calendar
* @param businessDayConvention
* @return The adjusted Calendar
* @throws HolidayCalendarException
*/
public Calendar adjust(Calendar calendar,
BusinessDayConvention businessDayConvention)
throws HolidayCalendarException
{
logger.fine("Adjusting date " + calendar + " by "
+ businessDayConvention + " by " + this);
return adjust(calendar, businessDayConvention, null);
}
/**
* Internal method to adjust the financial date using the given business day
* convention and holiday calendar. Keeps hold of the original date so as to
* be able to handle MonthEndReference convention.
*
* @param calendar
* @param businessDayConvention
* @param originalCalendar
* @return The adjusted Calendar
* @throws HolidayCalendarException
*/
private Calendar adjust(Calendar calendar,
BusinessDayConvention businessDayConvention,
Calendar originalCalendar) throws HolidayCalendarException
{
logger.finer("Business Day Convention " + businessDayConvention);
if (businessDayConvention == BusinessDayConvention.UNADJUSTED)
{
logger
.finer("Business Day Convention \"Unadjusted\" returning original date.");
return calendar;
}
Calendar d1 = (Calendar) calendar.clone();
if (businessDayConvention == BusinessDayConvention.FOLLOWING
|| businessDayConvention == BusinessDayConvention.MODIFIED_FOLLOWING)
{
while (isHoliday(d1) || isWeekend(d1))
{
logger.finest(d1 + " is a holiday in " + this
+ " stepping forward.");
d1.add(Calendar.DATE, 1);
}
if (businessDayConvention == BusinessDayConvention.MODIFIED_FOLLOWING)
{
if (d1.get(Calendar.MONTH) != calendar.get(Calendar.MONTH))
{
logger
.finer(d1
+ " moved over month boundary, so have to take previous date from "
+ calendar);
return adjust(calendar, BusinessDayConvention.PRECEDING);
}
}
} else if (businessDayConvention == BusinessDayConvention.PRECEDING
|| businessDayConvention == BusinessDayConvention.MODIFIED_PRECEDING)
{
while (isHoliday(d1) || isWeekend(d1))
{
logger.finest(d1 + " is a holiday in " + this
+ " stepping backward.");
d1.add(Calendar.DATE, -1);
}
if (businessDayConvention == BusinessDayConvention.MODIFIED_PRECEDING
&& d1.get(Calendar.MONTH) != calendar.get(Calendar.MONTH))
{
logger
.finer(d1
+ " moved over month boundary, so have to take FOLLOWING date from "
+ calendar);
return adjust(calendar, BusinessDayConvention.FOLLOWING);
}
} else if(businessDayConvention== BusinessDayConvention.MONTH_END_REFERENCE) {
d1 = (Calendar)calendar.clone();
setToEndOfMonth(d1);
logger.finest("Date moved to " + d1);
logger.finer("Finding last business day in month for "
+ d1);
return adjust(d1, BusinessDayConvention.PRECEDING);
} else {
throw new HolidayCalendarException(
"Unknown business-day convention");
}
logger.finer("Returning calculated date " + d1);
return d1;
}
/**
* Returns whether or not it is the last day of the month
*
* @param calendar The calendar to check
* @return true if it is the last day of the month, otherwise false
*/
public static boolean isLastDayOfMonth(Calendar calendar)
{
return calendar.get(Calendar.DAY_OF_MONTH) == calendar
.getActualMaximum(Calendar.DAY_OF_MONTH);
}
/**
* Sets the day to the last day in this month.
* @param calendar the calendar to modify
* @return The calendar at the last day of the month
*/
public static Calendar setToEndOfMonth(Calendar calendar)
{
calendar.set(Calendar.DAY_OF_MONTH, getDaysInMonth(calendar));
return calendar;
}
/**
* Returns the number of days in the current month.
*
* @return the number of days (28-31)
*/
public static int getDaysInMonth(Calendar calendar)
{
return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
}
/**
* Returns the number of business days between two Calendars.
*
* @param startCalendar
* @param endCalendar
* @return The number of business days
* @throws HolidayCalendarException
*/
public long getBusinessDaysBetween(Calendar startCalendar,
Calendar endCalendar) throws HolidayCalendarException
{
Calendar holdCalendar = (Calendar) startCalendar.clone();
long businessDays = 0;
try
{
while (holdCalendar.before(endCalendar))
{
holdCalendar = advanceBusinessDays(holdCalendar, 1);
businessDays++;
}
return businessDays;
} catch (HolidayCalendarException e)
{
throw new HolidayCalendarException(e);
}
}
}
Ads help maintain this website.