// bdlt_calendarutil.h -*-C++-*- #ifndef INCLUDED_BDLT_CALENDARUTIL #define INCLUDED_BDLT_CALENDARUTIL #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide common date manipulations requiring a calendar. // //@CLASSES: // bdlt::CalendarUtil: common date manipulations requiring a calendar // //@SEE_ALSO: bdlt_date, bdlt_calendar // //@DESCRIPTION: This component provides a 'struct', 'bdlt::CalendarUtil', that // serves as a namespace for date-manipulation functions that require the use // of a calendar. // // This utility component provides the following (static) methods: //.. // 'addBusinessDaysIfValid' Add an integral number of business days to the // specified original date within the valid range // of the specified calendar. // // 'nthBusinessDayOfMonthOrMaxIfValid' // Determine the 'n'th business day of the // specified year and month subject to a maximum of // the total number of business days in the // specified year and month, based on the provided // calendar. // // 'shiftFollowingIfValid' If original date is not a business day, move // the date forward until it is a business day. // // 'shiftPrecedingIfValid' If original date is not a business day, move // the date backwards until it is a business day. // // 'shiftModifiedFollowingIfValid' // If original date is not a business day, move // the date forward until it is a business day, // unless the date goes into the next month, in // which case the date is moved to the previous // business day. // // 'shiftModifiedPrecedingIfValid' // If original date is not a business day, move // the date backward until it is a business day, // unless the date goes into the previous month, in // which case the date is moved to the next // business day. // // 'shiftIfValid' Shift a date based on a provided convention. // Note that this function delegates its operation // to one of the above shift functions, based on // the date-shifting convention specified. // // 'subtractBusinessDaysIfValid' // Subtract an integral number of business days // from the specified original date within the // valid range of the specified calendar. //.. // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Manipulating Dates with 'CalendarUtil' ///- - - - - - - - - - - - - - - - - - - - - - - - - // Suppose that we want to determine the actual interest payment date in // January 2014 from a US bond that pays on the 20th of each month and uses the // modified-following date-shifting convention. // // We create a calendar, 'calUS', that has the calendar information populated // for the US in 2014. We then use the 'shiftIfValid' function, provided by // 'CalendarUtil', to compute the payment date. // // First, we create a date for January 1, 2014 that corresponds to the nominal // payment date (which happens to be holiday) and a calendar with valid range // from April 20, 2012 through April 20, 2014, typical weekend days, and the // holiday: //.. // const bdlt::Date unadjustedDate(2014, 1, 20); // // const bdlt::Date startDate(2012, 4, 20); // const bdlt::Date endDate(2014, 4, 20); // // bdlt::Calendar calUS(startDate, endDate); // calUS.addWeekendDay(bdlt::DayOfWeek::e_SAT); // calUS.addWeekendDay(bdlt::DayOfWeek::e_SUN); // calUS.addHoliday(unadjustedDate); //.. // Now, we determine the actual payment date by invoking the 'shiftIfValid' // function: //.. // bdlt::Date result; // int status = CalendarUtil::shiftIfValid( // &result, // unadjustedDate, // calUS, // CalendarUtil::e_MODIFIED_FOLLOWING); //.. // Notice that 'e_MODIFIED_FOLLOWING' is specified as an argument to // 'shiftIfValid' to indicate that we want to use the modified-following // date-shifting convention. // // Finally, we verify that the resulting date is correct: //.. // const bdlt::Date expected(2014, 1, 21); // // assert(0 == status); // assert(expected == result); //.. #include <bdlscm_version.h> #include <bdlt_calendar.h> #include <bdlt_calendarreverseiteratoradapter.h> #include <bdlt_date.h> #include <bdlt_dayofweek.h> #include <bsls_assert.h> #include <bsls_review.h> namespace BloombergLP { namespace bdlt { // =================== // struct CalendarUtil // =================== struct CalendarUtil { // This 'struct' provides a namespace for utility functions that operate on // dates in the context of supplied calendars. // TYPES enum ShiftConvention { // Enumeration used to delineate various date-shifting conventions. e_UNADJUSTED, // The date is not adjusted. e_FOLLOWING, // The date is adjusted using // 'shiftFollowingIfValid'. e_PRECEDING, // The date is adjusted using // 'shiftPrecedingIfValid'. e_MODIFIED_FOLLOWING, // The date is adjusted using // 'shiftModifiedFollowingIfValid'. e_MODIFIED_PRECEDING // The date is adjusted using // 'shiftModifiedPrecedingIfValid'. }; // CLASS METHODS static int addBusinessDaysIfValid(bdlt::Date *result, const bdlt::Date& original, const bdlt::Calendar& calendar, int numBusinessDays); // Load, into the specified 'result', the date that is the specified // 'numBusinessDays' chronologically after the specified 'original' // date according to the specified 'calendar'. The resulting date is // chronologically before the 'original' date for negative values of // 'numBusinessDays', the chronologically earliest business day that is // on or after the 'original' date for '0 == numBusinessDays', and // chronologically after the 'original' date for positive values of // 'numBusinessDays'. Return 0 on success, and a non-zero value, // without modifying '*result', if either the 'original' date or the // resulting date is not within the valid range of 'calendar'. Note // that if '0 != numBusinessDays', then the result of // 'addBusinessDaysIfValid(res, orig, cal, numBusinessDays)' is // identical to the result of // 'subtractBusinessDaysIfValid(res, orig, cal, -numBusinessDays)'. static int nthBusinessDayOfMonthOrMaxIfValid( bdlt::Date *result, const bdlt::Calendar& calendar, int year, int month, int n); // Load, into the specified 'result', the date corresponding to the // specified 'n'th business day of the specified 'month' and the // specified 'year' based on the specified 'calendar'. A positive // value of 'n' indicates that counting the number of business days // begins from the first calendar date of the month (inclusive), and a // negative value of 'n' indicates that counting the number of business // days begins from the last calendar date of the month (inclusive). // If there are fewer than 'abs(n)' business days in the month // according to the 'calendar', the business day furthest from the // first date of the month is chosen if 'n > 0', and the business day // furthest from the last date of the month is chosen if 'n < 0'. // Return 0 on success, and a non-zero value, without modifying // '*result', if the entire month specified by 'year' and 'month' is // not within the valid range of the 'calendar' or there are no // business days in the month specified by 'year' and 'month'. The // behavior is undefined unless 'n != 0', '1 <= year <= 9999', and // '1 <= month <= 12'. static int shiftFollowingIfValid(bdlt::Date *result, const bdlt::Date& original, const bdlt::Calendar& calendar); // Load, into the specified 'result', the date of the chronologically // earliest business day that is on or after the specified 'original' // date based on the specified 'calendar'. Return 0 on success, and a // non-zero value, without modifying '*result', if the 'original' date // is not within the valid range of 'calendar' or the following // business day cannot be found within the valid range of 'calendar'. static int shiftIfValid(bdlt::Date *result, const bdlt::Date& original, const bdlt::Calendar& calendar, ShiftConvention convention); // Load, into the specified 'result', the date of the business day that // is derived from the specified 'original' date based on the specified // 'calendar' according to the specified date-shifting 'convention'. // Return 0 on success, and a non-zero value, without modifying // '*result', if a valid business date cannot be found according to the // 'convention' within the valid range of 'calendar'. static int shiftIfValid(bdlt::Date *result, const bdlt::Date& original, const bdlt::Calendar& calendar, ShiftConvention convention, bdlt::DayOfWeek::Enum specialDay, bool extendSpecialDay, ShiftConvention specialConvention); // Load, into the specified 'result', the date of the business day that // is derived from the specified 'original' date based on the specified // 'calendar' according to the specified date-shifting 'convention', // except when the 'original' is either the specified 'specialDay' of // the week, or - if the specified 'extendSpecialDay' is 'true' - one // of the (possibly empty) set of contiguous non-business days // immediately *preceding* a 'specialDay' according to the 'calendar', // in which case the 'result' is determined using the specified // 'specialConvention'. Return 0 on success, and a non-zero value, // without modifying '*result', if a valid business date cannot be // found according to the above algorithm within the valid range of // 'calendar'. Note that this method is useful for computing, for // example, Korean bond coupon payment dates. static int shiftModifiedFollowingIfValid(bdlt::Date *result, const bdlt::Date& original, const bdlt::Calendar& calendar); // Load, into the specified 'result', the date of the chronologically // earliest business day that is on or after the specified 'original' // date, unless a date cannot be found in the same month, in which case // load the chronologically latest business day before the 'original' // date based on the specified 'calendar'. Return 0 on success, and a // non-zero value, without modifying '*result', if the 'original' date // is not within the valid range of 'calendar' or a valid business date // cannot be found according to the above algorithm within the valid // range of 'calendar'. static int shiftModifiedPrecedingIfValid(bdlt::Date *result, const bdlt::Date& original, const bdlt::Calendar& calendar); // Load, into the specified 'result', the date of the chronologically // latest business day that is on or before the specified 'original' // date, unless a date cannot be found in the same month, in which case // load the chronologically earliest business day after the 'original' // date based on the specified 'calendar'. Return 0 on success, and a // non-zero value, without modifying '*result', if the 'original' date // is not within the valid range of 'calendar' or a valid business date // cannot be found according to the above algorithm within the valid // range of 'calendar'. static int shiftPrecedingIfValid(bdlt::Date *result, const bdlt::Date& original, const bdlt::Calendar& calendar); // Load, into the specified 'result', the date of the chronologically // latest business day that is on or before the specified 'original' // date based on the specified 'calendar'. Return 0 on success, and a // non-zero value, without modifying '*result', if the 'original' date // is not within the valid range of 'calendar' or the preceding // business day cannot be found within the valid range of 'calendar'. static int subtractBusinessDaysIfValid( bdlt::Date *result, const bdlt::Date& original, const bdlt::Calendar& calendar, int numBusinessDays); // Load, into the specified 'result', the date that is the specified // 'numBusinessDays' chronologically before the specified 'original' // date according to the specified 'calendar'. The resulting date is // chronologically before the 'original' date for positive values of // 'numBusinessDays', the chronologically latest business day that is // on or before the 'original' date for '0 == numBusinessDays', and // chronologically after the 'original' date for negative values of // 'numBusinessDays'. Return 0 on success, and a non-zero value, // without modifying '*result', if either the 'original' date or the // resulting date is not within the valid range of 'calendar'. Note // that if '0 != numBusinessDays', then the result of // 'subtractBusinessDaysIfValid(res, orig, cal, numBusinessDays)' is // identical to the result of // 'addBusinessDaysIfValid(res, orig, cal, -numBusinessDays)'. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ------------------- // struct CalendarUtil // ------------------- // CLASS METHODS inline int CalendarUtil::shiftFollowingIfValid(bdlt::Date *result, const bdlt::Date& original, const bdlt::Calendar& calendar) { BSLS_ASSERT(result); enum { e_SUCCESS = 0, e_BAD_INPUT = 1, e_OUT_OF_RANGE = 2, e_NOT_FOUND = 3 }; if (!calendar.isInRange(original)) { return e_BAD_INPUT; // RETURN } bdlt::Calendar::BusinessDayConstIterator iter = calendar.beginBusinessDays(original); if (iter != calendar.endBusinessDays()) { *result = *iter; return e_SUCCESS; // RETURN } else { return e_OUT_OF_RANGE; // RETURN } return e_NOT_FOUND; } inline int CalendarUtil::shiftPrecedingIfValid(bdlt::Date *result, const bdlt::Date& original, const bdlt::Calendar& calendar) { BSLS_ASSERT(result); enum { e_SUCCESS = 0, e_BAD_INPUT = 1, e_OUT_OF_RANGE = 2, e_NOT_FOUND = 3 }; if (!calendar.isInRange(original)) { return e_BAD_INPUT; // RETURN } bdlt::Calendar::BusinessDayConstReverseIterator iter = calendar.rbeginBusinessDays(original); if (iter != calendar.rendBusinessDays()) { *result = *iter; return e_SUCCESS; // RETURN } else { return e_OUT_OF_RANGE; // RETURN } return e_NOT_FOUND; } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2018 Bloomberg Finance L.P. // // 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. // ----------------------------- END-OF-FILE ----------------------------------