¿Como encontrar el último día hábil del mes en Java?

Quizas a usted alguna vez le toco programar como encontrar el último día hábil del mes. La definición:

Es el último día del mes, que no cae un fin de semana y que no es un día feriado.

Para trabajar este ejemplo utilizaremos fechas de los Estados Unidos, mercado de finanzas. En el caso de los días feriados, vamos a utilizar el calendario de OCC (Options Clearing Corporation). Dependiendo del área y país en donde usted trabaje el calendario de las fechas será distinto (por ejemplo Estados Unidos y Venezuela).

La solución en Java es sencilla, usando la clase Calendar. Después de jugar un poco al final terminé escribiendo esta clase que muestra el último día hábil del mes y le dice a usted si la fecha entrada por el teclado es un día hábil o no:

package com.kodegeek.blog.finance;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.ResourceBundle;
import java.util.Scanner;
import java.util.Set;
import java.util.logging.Logger;

/**
 * Helper date methods tailored for the financial industry.
 * License: LGPL
 * BLOG: http://kodegeek.com
 * @author josevnz@kodegeek.com
 *
 */
public final class DateHelper {

	private final static ResourceBundle BUNDLE;
	private final static Logger log;
	private static final Set holidays;

	static {

		BUNDLE = ResourceBundle.getBundle(DateHelper.class.getName());

		log = Logger.getLogger(DateHelper.class.getName());

		// Populate the holidays set
		holidays = new HashSet();
		for (String dateStr: BUNDLE.getString("com.kodegeek.blog.finance.DateHelper.holidays").split(";\\s*", -1)) {
			try {
				holidays.add(DateFormat.getDateInstance(DateFormat.DEFAULT).parse(dateStr));
			} catch (ParseException e) {
				log.warning(String.format(BUNDLE.getString("com.kodegeek.blog.finance.DateHelper.error.badDate"), dateStr));
				continue;
			}
		}

	}

	/**
	 * Default constructor
	 */
	private DateHelper() {
		// Only static methods go on this class
	}

	/**
	 * Check if a given date is the last business day of the month
	 * @param aDate The date to test
	 * @return True if is the last day of the month, false otherwise
	 */
	public final static boolean isLastBusinessDayOfMonth(final Date aDate) {
		boolean isLastBusinessDay = false;

		// Can do a comparison?
		if (aDate == null) {
			return isLastBusinessDay;
		}

		Calendar calendar = Calendar.getInstance();

		// Current date on a weekend
		calendar.setTime(aDate);
		int day = calendar.get(Calendar.DAY_OF_MONTH);
		int month = calendar.get(Calendar.MONTH);
		int year =  calendar.get(Calendar.YEAR);

		int lastbusinessDayOfMonth = getLastBusinessDayOfMonth(month, year);
		if (lastbusinessDayOfMonth == day) {
			isLastBusinessDay = true;
		}

		return isLastBusinessDay;
	}

	/**
	 * Get the last business day of the month for a given month / year combination
	 * @param month The month
	 * @param year The year
	 * @return The last business day
	 */
	public static int getLastBusinessDayOfMonth(final int month, final int year) {
		int day = -1;
		Calendar calendar = Calendar.getInstance();
		calendar.set(Calendar.MONTH, month);
		calendar.set(Calendar.YEAR, year);
		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));

		// Keep looking backwards until the day is not a weekend or a holiday
		while(true) {
			if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
				calendar.add(Calendar.DAY_OF_MONTH, -1);
				continue;
			} else if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
				calendar.add(Calendar.DAY_OF_MONTH, -2);
				continue;
			} else if (holidays.contains(calendar.getTime())) {
				calendar.add(Calendar.DAY_OF_MONTH, -1);
				continue;
			}
			break;
		} // End while
		day = calendar.get(Calendar.DAY_OF_MONTH);
		return day;
	}

	/**
	 * Unit test, load today date and check if is a end of the month
	 * @param args The date to test from the command line
	 * @throws Exception If the date is invalid for the given local
	 */
	public static void main(String [] args) throws Exception {
		Scanner scan = new Scanner(System.in);
		do {
			String currDateStr = null;
			SimpleDateFormat format = new SimpleDateFormat("yy-M-d");
			try {
				System.out.println("Please enter a date to check (YYYY-MM-DD, Ctrl-C to exit):");
				currDateStr = scan.nextLine();
				Date date = format.parse(currDateStr);

				if (date != null) {
					Calendar calendar = Calendar.getInstance();
					calendar.setTime(date);
					int month = calendar.get(Calendar.MONTH);
					int year =  calendar.get(Calendar.YEAR);
					System.out.println(String.format("Last business day of the month: %d", getLastBusinessDayOfMonth(month, year)));
					System.out.println(String.format("%s %s", currDateStr,isLastBusinessDayOfMonth(date) == true? "Is the last business day": "Is not the last business day"));
				}
			} catch (ParseException pExp) {
				System.err.println(String.format("Ignoring bad date: '%s'", currDateStr));
			}
		} while (true);
	}

}

El resource bundle lo único que contiene son los días feriados:

# Bank holidays per OCC calendar 2009 - http://www.optionsclearing.com/market/infomemos/2008/oct/24961.pdf
com.kodegeek.blog.finance.DateHelper.holidays=Jan 01, 2009;January 19, 2009; Feb 26, 2009;Apr 10, 2009;May 25, 2009;July 3, 2009; Sep 7, 2009; Nov 26, 2009; Dec 25, 2009; Oct 12, 2009; Nov 11, 2009
com.kodegeek.blog.finance.DateHelper.error.badDate=Got bad date '%s' from property

La idea es buscar el último día del mes y de allí contar hacia atrás, esquivando fines de semana y días feriados.

Please enter a date to check (YYYY-MM-DD, Ctrl-C to exit):
2009-02-27
Last business day of the month: 27
2009-02-27 Is the last business day

En una siguiente entrega les mostraré como hacer lo mismo en Ruby.

Blogalaxia:, , ,
Technorati:, , ,
To2blogs:, , ,
Del.icio.us:, , ,

2 thoughts on “¿Como encontrar el último día hábil del mes en Java?

  1. Xydisimo men, ja yo hace tanto no hago java en forma, que ya se me esta olvidando. pero me latio mucho tu clase…la compro pa ievar 😀

    saludos

Comments are closed.