getNextBusinessDay(int offset, Date baseDate, int[] excludedDaysOfWeek, String[] excludedDateList)


/**
 * @author Ulrich Krause
 */
package de.eknori.ssjsplus.javascript;

import java.text.DateFormat;
import java.text.ParseException;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;

@SuppressWarnings({ "unchecked", "rawtypes" })
public class BusinessDayCalculator {
	public static final SimpleDateFormat DEFAULT_FORMAT = new SimpleDateFormat(
			"dd/mm/yyyy");

	/**
	 * Use to find out what date is a certain number of days after a base date,
	 * not counting certain days of the week.
	 * 
	 * @param offset
	 *            Number of non-excluded days from the baseDate that the result
	 *            date will be
	 * @param baseDate
	 *            Date to start counting offset from. If specify "null" defaults
	 *            to today
	 * @param excludedDaysOfWeek
	 *            Array of constants from java.util.Calendar object e.g.
	 *            Calendar.SUNDAY, which are excluded from counting towards
	 *            offset for the result
	 * @return The earliest date which is offset days after the baseDate. In
	 *         counting towards this result date, any excludedDaysOfWeek are
	 *         excluded from counting towards the offset
	 */

	public static Date getNextBusinessDay(int offset, Date baseDate,
			int[] excludedDaysOfWeek) {
		return getNextBusinessDay(offset, baseDate, excludedDaysOfWeek,
				(Hashtable) null);
	}

	/**
	 * Use to find out what date is a certain number of days after a base date,
	 * not counting certain days of the week and certain dates ( company holiday etc ).
	 * 
	 * @param offset
	 *            Number of non-excluded days from the baseDate that the result
	 *            date will be
	 * @param baseDate
	 *            Date to start counting offset from. If specify "null" defaults
	 *            to today
	 * @param excludedDaysOfWeek
	 *            Array of constants from java.util.Calendar object e.g.
	 *            Calendar.SUNDAY, which are excluded from counting towards
	 *            offset for the result
	 * @param excludedDateList
	 *            Array of date strings (formatted as
	 *            <CODE>SimpleDateFormat.getDateInstance().format(</CODE>))
	 *            which represent specific dates which should be excluded from
	 *            the counting of days, such as holidays
	 * @return The earliest date which is offset days after the baseDate. In
	 *         counting towards this result date, any excludedDaysOfWeek and excludedDates are
	 *         excluded from counting towards the offset
	 */

	public static Date getNextBusinessDay(int offset, Date baseDate,
			int[] excludedDaysOfWeek, String[] excludedDateList) {
		Hashtable excludedDates = new Hashtable();
		if (excludedDateList != null)
			for (int i = 0; i < excludedDateList.length; i++) {
				try {
					excludedDates.put(
							DateFormat.getDateInstance().parse(
									excludedDateList[i]), "");
				} catch (ParseException err) {
				}
			}
		return getNextBusinessDay(offset, baseDate, excludedDaysOfWeek,
				excludedDates);
	}

	/**
	 * Use to find out what date is a certain number of days after a base date,
	 * not counting certain days.
	 * 
	 * @param offset
	 *            Number of non-excluded days from the baseDate that the result
	 *            date will be
	 * @param baseDate
	 *            Date to start counting offset from. If specify "null" defaults
	 *            to today
	 * @param excludedDaysOfWeek
	 *            Array of constants from java.util.Calendar object e.g.
	 *            Calendar.SUNDAY, which are excluded from counting towards
	 *            offset for the result
	 * @param excludedDateList
	 *            Array of date objects which represent specific dates which
	 *            should be excluded from the counting of days, such as holidays
	 * @return The earliest date which is offset days after the baseDate. In
	 *         counting towards this result date, any excludedDaysOfWeek are
	 *         excluded from counting towards the offset
	 */

	public static Date getNextBusinessDay(int offset, Date baseDate,
			int[] excludedDaysOfWeek, Date[] excludedDateList) {
		Hashtable excludedDates = new Hashtable();
		if (excludedDateList != null)
			for (int i = 0; i < excludedDateList.length; i++)
				excludedDates.put(excludedDateList[i], "");
		return getNextBusinessDay(offset, baseDate, excludedDaysOfWeek,
				excludedDates);
	}

	/**
	 * <P>
	 * Use to find out what date is a certain number of days after a base date,
	 * not counting certain days.
	 * 
	 * <P>
	 * <b>Primary example:</B> Give me the date which is at least 3 business
	 * days from today.
	 * <P>
	 * <b>Usage for example:</B>
	 * <UL>
	 * <LI><CODE>offset</CODE> = 3
	 * <LI><CODE>baseDate</CODE> = today (i.e. <CODE>new Date()</CODE> )
	 * <LI><CODE>excludedDaysOfWeek</CODE> = array with values for Sat and Sun
	 * (i.e. <CODE>{ Calendar.SATURDAY, Calendar.SUNDAY }</CODE> )
	 * <LI><CODE>excludedDates</CODE> = <CODE>Hashtable</CODE> with elements
	 * whose keys are <CODE>java.util.Date</CODE> objects equal to various
	 * business holidays not to be counted towards progress
	 * </UL>
	 * 
	 * @param offset
	 *            Number of non-excluded days from the baseDate that the result
	 *            date will be
	 * @param baseDate
	 *            Date to start counting offset from. If specify "null,"
	 *            defaults to today
	 * @param excludedDaysOfWeek
	 *            Array of constants from java.util.Calendar object e.g.
	 *            Calendar.SUNDAY, which are excluded from counting towards
	 *            offset for the result
	 * @param excludedDates
	 *            Hashtable of java.util.Date objects, which represent specific
	 *            dates (12:00am midnight) which should be excluded from
	 *            counting towards the offset
	 * @return The earliest date which is offset days after the baseDate. In
	 *         counting towards this result date, any excludedDaysOfWeek or
	 *         specifically excludedDates are excluded from counting towards the
	 *         offset
	 */

	public static Date getNextBusinessDay(int offset, Date baseDate,
			int[] excludedDaysOfWeek, Hashtable excludedDates) {
		int i;
		if (offset < 0)
			return null;
		if (baseDate == null) {
			baseDate = new Date();
		}
		Calendar baseCopy = Calendar.getInstance();
		Calendar baseCal = Calendar.getInstance();
		baseCopy.setTime(baseDate);
		baseCal.clear();
		baseCal.set(baseCopy.get(Calendar.YEAR), baseCopy.get(Calendar.MONTH),
				baseCopy.get(Calendar.DATE));

		if (excludedDaysOfWeek == null) {
			excludedDaysOfWeek = new int[0];
		}
		if (excludedDates == null) {
			excludedDates = new Hashtable();
		}
		Hashtable excludedDays = new Hashtable();
		for (i = 0; i < excludedDaysOfWeek.length; i++)
			excludedDays.put(new Integer(excludedDaysOfWeek[i]), "");

		while (isExcluded(baseCal, excludedDays, excludedDates)) {
			baseCal.add(Calendar.DATE, 1);
		}
		Calendar resultCal = Calendar.getInstance();
		resultCal.setTime(baseCal.getTime());

		int daysPerWeek = resultCal.getMaximum(Calendar.DAY_OF_WEEK);
		int daysPerWeekNotExcluded = daysPerWeek - excludedDays.size();
		int wholeWeeksAway = offset / daysPerWeekNotExcluded;
		int remainingoffset = offset % daysPerWeekNotExcluded;

		resultCal.add(Calendar.DATE, wholeWeeksAway * daysPerWeek);

		int nExcludedDates = 0;
		Enumeration exclDates = excludedDates.keys();
		while (exclDates.hasMoreElements()) {
			Date nextExclDate = (Date) exclDates.nextElement();
			Calendar nextExclCal = Calendar.getInstance();
			nextExclCal.setTime(nextExclDate);

			if ((!excludedDays.containsKey(new Integer(nextExclCal
					.get(Calendar.DAY_OF_WEEK))))
					&& (baseCal.before(nextExclCal))
					&& (!resultCal.before(nextExclCal))) {

				nExcludedDates++;
			}
		}

		remainingoffset = remainingoffset + nExcludedDates;
		for (i = 0; i < remainingoffset; i++) {
			do {
				resultCal.add(Calendar.DATE, 1);
			} while (isExcluded(resultCal, excludedDays, excludedDates));
		}

		return resultCal.getTime();
	}

	/**
	 * Tells you if the checkCal day/date is in one of the exclusion lists
	 * 
	 * @param checkCal
	 *            Calendar objects representing the date to check
	 * @param excludedDays
	 *            Hashtable with Integer keys representing days of the week
	 *            which count as excluded. Integer keys are constants from
	 *            Calendar class, e.g. Calendar.SUNDAY
	 * @param excludedDates
	 *            Hashtable with elements with java.util.Date objects as keys
	 *            which represent specific dates which should be excluded. Dates
	 *            should all be set on 12am midnight for correct comparison.
	 * @return True if the checkCal date is one of the days or dates that are to
	 *         be excluded, as specified by the excludedDays and excludedDates
	 *         parameters
	 */
	public static boolean isExcluded(Calendar checkCal, Hashtable excludedDays,
			Hashtable excludedDates) {
		Calendar testCal = Calendar.getInstance();
		testCal.clear();
		testCal.set(checkCal.get(Calendar.YEAR), checkCal.get(Calendar.MONTH),
				checkCal.get(Calendar.DAY_OF_MONTH));
		return ((excludedDays != null && excludedDays.containsKey(new Integer(
				checkCal.get(Calendar.DAY_OF_WEEK)))) || (excludedDates != null && excludedDates
				.containsKey(testCal.getTime())));
	}

	public static void main(String[] args) throws ParseException {
		SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yy");
		Date baseDate = dateFormat.parse("7.5.2012");
		int[] excludedDays = new int[] { Calendar.SATURDAY, Calendar.SUNDAY };
		String[] excludedDates = new String[] { "08.05.2012", "09.05.2012" };
		Date NBD = getNextBusinessDay(1, baseDate, excludedDays, excludedDates);

		System.out.println("getNextBusinessDay: " + NBD);

	}
}
All code submitted to OpenNTF XSnippets, whether submitted as a "Snippet" or in the body of a Comment, is provided under the Apache License Version 2.0. See Terms of Use for full details.
No comments yetLogin first to comment...