/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.financial.convention.businessday; import org.threeten.bp.LocalDate; import org.threeten.bp.ZonedDateTime; import org.threeten.bp.temporal.TemporalAdjuster; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.util.ArgumentChecker; /** * Utilities for managing the business day convention. * <p> * This is a thread-safe static utility class. */ public class BusinessDayDateUtils { /** * Restricted constructor. */ protected BusinessDayDateUtils() { super(); } // ------------------------------------------------------------------------- /** * Calculates the number of days in between two dates with the date count * rule specified by the {@code TemporalAdjuster}. * * @param startDate the start date-time, not null * @param includeStart whether to include the start * @param endDate the end date-time, not null * @param includeEnd whether to include the end * @param convention the date adjuster, not null * @return the number of days between two dates */ public static int getDaysBetween(final ZonedDateTime startDate, final boolean includeStart, final ZonedDateTime endDate, final boolean includeEnd, final TemporalAdjuster convention) { LocalDate date = startDate.toLocalDate(); LocalDate localEndDate = endDate.toLocalDate(); int mult = 1; if (startDate.isAfter(endDate)) { date = endDate.toLocalDate(); localEndDate = startDate.toLocalDate(); mult = -1; } int result = includeStart ? 1 : 0; while (!date.with(convention).equals(localEndDate)) { date = date.with(convention); result++; } return mult * (includeEnd ? result : result - 1); } /** * Add a certain number of working days (defined by the holidayCalendar) to a date * @param startDate The start date * @param workingDaysToAdd working days to add * @param holidayCalendar Defines what is a non-working day * @return a working day */ public static LocalDate addWorkDays(final LocalDate startDate, final int workingDaysToAdd, final Calendar holidayCalendar) { ArgumentChecker.notNull(startDate, "null startDate"); ArgumentChecker.notNull(holidayCalendar, "null holidayCalendar"); int daysLeft = workingDaysToAdd; LocalDate temp = startDate; while (daysLeft > 0) { temp = temp.plusDays(1); if (holidayCalendar.isWorkingDay(temp)) { daysLeft--; } } return temp; } /** * Get the number of business days between two dates * @param firstDate The first date * @param secondDate the second date * @param calendar Calendar defining what is a working day * @return The number of business (working) days between two dates */ public static int getDaysBetween(LocalDate firstDate, LocalDate secondDate, Calendar calendar) { ArgumentChecker.notNull(firstDate, "first date"); ArgumentChecker.notNull(secondDate, "second date"); if (secondDate.isBefore(firstDate)) { throw new OpenGammaRuntimeException("d2 must be on or after d1: have d1 = " + firstDate + " and d2 = " + secondDate); } ArgumentChecker.notNull(calendar, "calendar"); int count = 0; LocalDate date = firstDate; while (date.isBefore(secondDate)) { if (calendar.isWorkingDay(date)) { count++; } date = date.plusDays(1); } return count; } /** * Get the number of business days between two dates. <b>Note:<b> these {@link ZonedDateTime} dates are converted to {@link LocalDate}, so any time-of-day and time zone information is lost * @param firstDate The first date * @param secondDate the second date * @param calendar Calendar defining what is a working day * @return The number of business (working) days between two dates */ public static int getDaysBetween(final ZonedDateTime firstDate, final ZonedDateTime secondDate, final Calendar calendar) { ArgumentChecker.notNull(firstDate, "first date"); ArgumentChecker.notNull(secondDate, "second date"); return getDaysBetween(firstDate.toLocalDate(), secondDate.toLocalDate(), calendar); } /** * Get the number of working days (according to the supplied calendar) inclusive of the final date. * <p> * For example, the number of days between 8/8/2014 (Monday) and 12/8/2014 (Friday) a weekend only calendar is 5 (since Friday is a working day) * @param firstDate The first date * @param secondDate the second date * @param calendar Calendar defining what is a working day * @return The number of business (working) days between two dates, inclusive of the final date */ public static int getWorkingDaysInclusive(LocalDate firstDate, LocalDate secondDate, Calendar calendar) { int res = getDaysBetween(firstDate, secondDate, calendar); if (calendar.isWorkingDay(secondDate)) { res++; } return res; } /** * Get the number of working days (according to the supplied calendar) inclusive of the final date. <b>Note:<b> these {@link ZonedDateTime} dates are converted to {@link LocalDate}, so any * time-of-day and time zone information is lost * <p> * For example, the number of days between 8/8/2014 (Monday) and 12/8/2014 (Friday) a weekend only calendar is 5 (since Friday is a working day) * @param firstDate The first date * @param secondDate the second date * @param calendar Calendar defining what is a working day * @return The number of business (working) days between two dates, inclusive of the final date */ public static int getWorkingDaysInclusive(final ZonedDateTime firstDate, final ZonedDateTime secondDate, final Calendar calendar) { ArgumentChecker.notNull(firstDate, "first date"); ArgumentChecker.notNull(secondDate, "second date"); return getWorkingDaysInclusive(firstDate.toLocalDate(), secondDate.toLocalDate(), calendar); } }