To value financial instruments or calculate accrued interest, it is essential to be able to calculate the duration between any two dates.
Counting the number of calendar days or the number of working days between any two dates is an important step in this calculation, and this count must take very precise account of the calendars and conventions in force.
The businessdays module is intended to help the user in this delicate work.
The user will find here an overview of the functionalities available to him, but for a more complete view, he is invited to browse the submodules which are listed in the Imports section below.
But before starting it is appropriate to say a word about certain syntactic choices that were made.
Foreword
nudates module
We systematically used the comparison operators of the nudates module. For example ==~ for equality test, !==~ for non-equality test, and so on.
questionable module
To shorten certain parts of the code we have often favored the syntax of the questionable module over the syntax of the options module. Examples:
- ?T instead of Option[T]
- !x instead of get(x)
- Option binding with the =? operator (see the module documentation for more information)
- Option chaining with the .? operator (see the module documentation for more information)
- y = x.?f can be seen as a shortcut for y = if x.isNone: none(U) else: get(x).f.some
- Preconditions: typeof(x) is Option[T], typeof(y) is Option[U], f: T -> U (notation of the sugar module)
Feature overview
Using calendars
Example:
import src/businessdays # some calendars are natively supported by the system let calendar = newCalendarUSNYSE() # New York Stock Exchange (NYSE) calendar # some arbitrary dates let dt0 = dateTime(2014, mDec, 31) # Wednesday let dt1 = dateTime(2015, mJan, 1) # Thursday let dt2 = dateTime(2015, mJan, 2) # Friday let dt3 = dateTime(2015, mJan, 3) # Saturday let dt4 = dateTime(2015, mJan, 4) # Sunday let dt5 = dateTime(2015, mJan, 5) # Monday let dt6 = dateTime(2015, mJan, 6) # Tuesday # 'dt1' (resp. 'dt2', 'dt3') is a holiday (resp. business day, weekend) doAssert: !calendar.isholiday(dt1) doAssert: !calendar.isbday(dt2) doAssert: !calendar.isweekend(dt3) # all known information about a date can be grouped into a string doAssert: calendar.info(dt4) == "Sunday, January 4, 2015: " & "not a business day, not a holiday, weekend" doAssert: calendar.info(dateTime(2021, mDec, 25)) == "Saturday, December 25, 2021: " & "not a business day, not a holiday, weekend" doAssert: calendar.info(dateTime(2021, mDec, 24)) == "Friday, December 24, 2021: " & "not a business day, holiday, not a weekend" # In 2021, Christmas Day was celebrated on Friday December 24 # on the New York Stock Exchange; it is this day that is # considered a public holiday by the system, not December 25. # nearest business day (in the future or in the past) doAssert: !calendar.nextbday(dt1, forward = true) ==~ dt2 doAssert: !calendar.nextbday(dt1, forward = false) ==~ dt0 # you can shift by one or more business days (forward or backward) doAssert: !calendar.addbdays(dt0, 1) ==~ dt2 doAssert: !calendar.addbdays(dt0, 2) ==~ dt5 doAssert: !calendar.addbdays(dt6, -3) ==~ dt0 # business day conventions are supported let dt11 = dateTime(2011, mApr, 29) # Friday let dt12 = dateTime(2012, mMar, 28) # Wednesday let dt13 = dateTime(2012, mMar, 30) # Friday # 'bdcEndOfMonth' is an item of the 'bdBusinessDayConvention' enumeration doAssert: !calendar.bday(bdcEndOfMonth, dt11) ==~ dt11 doAssert: !calendar.bday(bdcEndOfMonth, dt12) ==~ dt13 # business days between two dates (the 'dateInterval' parameter # allows you to include or exclude 'fromDate' or 'toDate') let bizDays = calendar.bdays(fromDate = dt0, toDate = dt6, dateInterval = BoundedClosed) doAssert: bizdays == @[dt0, dt2, dt5, dt6] doAssert: bizdays.len == 4 # number of business days # 2 public holidays observed in January 2024 let obsHolidays2024January = calendar.observedHolidays(2024, mJan) doAssert: obsHolidays2024January == @[ dateTime(2024, mJan, 1), # New Year's day dateTime(2024, mJan, 15)] # Birthday of Martin Luther King # 10 public holidays observed in 2025 let obsHolidays2025 = calendar.observedHolidays(2025) doAssert: obsHolidays2025 == @[ dateTime(2025, mJan, 1), # New Year's day dateTime(2025, mJan, 20), # Birthday of Martin Luther King dateTime(2025, mFeb, 17), # Washington's Birthday dateTime(2025, mApr, 18), # Good Friday dateTime(2025, mMay, 26), # Memorial Day dateTime(2025, mJun, 19), # Juneteenth National Independence Day dateTime(2025, mJul, 4), # Independence Day dateTime(2025, mSep, 1), # Labor Day dateTime(2025, mNov, 27), # Thanksgiving Day dateTime(2025, mDec, 25)] # Christmas Day # to speed up subsequent calculations, business days can # be stored in a sequence that can be reused several times let bizDaysNYSE2025 = calendar.bdays(fromDate = dateTime(2025, mJan, 1), toDate = dateTime(2025, mDec, 31), dateInterval = BoundedClosed) # 365 calendar days - 104 weekends - 10 observed holidays = # 251 business days doAssert: bizDaysNYSE2025.len == 251 # some calculations with the above sequence let dt250117 = dateTime(2025, mJan, 17) # Friday # January 18, 2025: Saturday (weekend) # January 19, 2025: Sunday (weekend) # January 20, 2025: Birthday of Martin Luther King (holiday) let dt250121 = dateTime(2025, mJan, 21) # Tuesday (business day) let dt250122 = dateTime(2025, mJan, 22) # Wednesday (business day) doAssert: !bizDaysNYSE2025.nextbday(dt250117) ==~ dt250121 doAssert: !bizDaysNYSE2025.addbdays(dt250121, -1) ==~ dt250117 doAssert: !bizDaysNYSE2025.addbdays(dt250117, 2) ==~ dt250122
Day counting
Example:
import src/businessdays ## -------------------------------------------- ## the user will find many more examples in the ## 'daycount' module of the "Imports" section ## -------------------------------------------- # Time interval 1: from 31 January 2008 to 28 February 2008 (leap year) # -------------------------------------------------------------------------- let startDate1 = dateTime(2008, mJan, 31) let endDate1 = dateTime(2008, mFeb, 28) doAssert: yearFraction(startDate1, endDate1, dccActual360) == 28.0/360.0 doAssert: yearFraction(startDate1, endDate1, dccThirtyA360) == 28.0/360.0 doAssert: yearFraction(startDate1, endDate1, dccThirtyU360) == 28.0/360.0 doAssert: yearFraction(startDate1, endDate1, dccThirtyE360) == 28.0/360.0 doAssert: yearFraction(startDate1, endDate1, dccThirtyEPlus360) == 28.0/360.0 doAssert: yearFraction(startDate1, endDate1, dccThirtyG360) == 28.0/360.0 # Day count is 28.0 for all considered day count conventions. # Time interval 2: from 28 February 2007 to 31 March 2007 (non-leap year) # ---------------------------------------------------------------------------- let startDate2 = dateTime(2007, mFeb, 28) let endDate2 = dateTime(2007, mMar, 31) doAssert: yearFraction(startDate2, endDate2, dccActual360) == 31.0/360.0 doAssert: yearFraction(startDate2, endDate2, dccThirtyA360) == 33.0/360.0 doAssert: yearFraction(startDate2, endDate2, dccThirtyU360) == 30.0/360.0 doAssert: yearFraction(startDate2, endDate2, dccThirtyE360) == 32.0/360.0 doAssert: yearFraction(startDate2, endDate2, dccThirtyEPlus360) == 33.0/360.0 doAssert: yearFraction(startDate2, endDate2, dccThirtyG360) == 30.0/360.0 # The actual number of days is 31.0. But depending on # how the convention manages the end of the month, and # the end of the month of February, we can also obtain # 30.0, 32.0 or 33.0 days.
Exports
-
klndrStaticHolidays, klndrTARGET, isholidayEngland, bdays, $, holidayUSMemorialDay, isweekendTARGETCalendar, usInaugurationDay, isholidayUSBondMrkt, holidayWhitSunday, boxingDay, holidayUSNYSEElectionDay, klndrNoHolidayOrWeekend, observedHolidays, USVeteransDay, newCalendarGBEngWls, holidayTargetLabourDay, NewYearsDay, holidayGBSctOrangemensDay, holidayGBEarlyMay, bdcFollowing, bdBusinessCalendar, holidayGBSpring, orangemensDay, newCalendarUSFederalGovt, klndrGBEngWls, isweekend, bdHoliday, stPatricksDay, USJuneteenthIndependenceDay, holidayUSWashingtonBirthday, christmasDay, klndrUSFederalGovt, LabourDay, isholiday, holidayUSChristmasDay, klndrGBNir, holidayUSLaborDay, bdcEndOfMonth, holidayTargetDecember31, holidayGBChristmasDay, BoxingDay, holidayAscension, isholidayTARGETCalendar, december31, ChristmasDay, isholiday, bdCalendar, isholiday, holidayEasterNCo, bdcPredecing, holiday, holidayWhitMonday, OrangemensDay, isbday, holidayUSVeteransDay, isholidayUSNYSE, holidayUSIndependenceDay, StPatricksDay, holidayGoodFriday, klndrUSNYSE, holidayEasterSunday, holidayUSThanksgivingDay, holidayGBSctStAndrewsDay, holidayGBSctJanuary2, observedHolidays, newYearsDay, isholidayNorthIreland, holidayGBNirStPatricksDay, nextMondayIfWeekend, holidayUSColumbusDay, holidayEasterMonday, newCalendarGBSct, observedHolidays, bdcModifFollowingFornight, holidayUSNYSENewYearsDay, newCalendarGBNir, holidayUSNewYearsDay, StAndrewsDay, addbdays, USIndependenceDay, bdBusinessDayConvention, isholidayScotland, bdcModifFollowing, newCalendarUSBondMrkt, newCalendarUSNYSE, stAndrewsDay, holidayGBEngNirWlsSummer, December31, info, holidayTargetChristmasDay, newCalendarWeekendsOnly, holidayGBBoxingDay, nearestWeekday, nextbday, klndrWeekendsOnly, holidayTargetNewYearsDay, labourDay, bday, newCalendarTARGET, newCalendar, holidayGBNewYearsDay, holidayUSInaugurationDay, holidayTargetBoxingDay, holidayUSJuneteenthIndependenceDay, newCalendarStaticHolidays, holidayUSMartinLutherKingBirthday, nextWeekday, klndrGBSct, nextMondayIfSunday, holidayGBSctSummer, newCalendarNoHolidayOrWeekend, isholidayUSFederalGovt, klndrUSBondMrkt, klndrTARGET, holidayUSInaugurationDay, isholidayEngland, bdays, dccBusinessDays252, dccActual364, holidayUSMemorialDay, isweekendTARGETCalendar, usInaugurationDay, holidayWhitSunday, boxingDay, holidayUSNYSEElectionDay, newCalendarWeekendsOnly, isweekend, dccThirtyEPlus360, newCalendarUSBondMrkt, observedHolidays, USVeteransDay, dccActualActualAFB, bday, isholidayUSFederalGovt, addbdays, holidayTargetLabourDay, newCalendarGBEngWls, newYearsDay, holidayAscension, dccActual365A, klndrNoHolidayOrWeekend, holidayGBSctOrangemensDay, newCalendarUSFederalGovt, bdBusinessCalendar, bdcFollowing, BoxingDay, dccActualActual, holidayGBEarlyMay, nextbday, klndrStaticHolidays, dccOneOne, holidayGBBoxingDay, isholidayNorthIreland, isholidayTARGETCalendar, nearestWeekday, holidayGBSpring, orangemensDay, holidayGBSctSummer, bdHoliday, stPatricksDay, holidayUSWashingtonBirthday, christmasDay, labourDay, isholiday, dccActual360, newCalendar, holidayUSChristmasDay, klndrGBNir, dccThirtyG360, holidayUSLaborDay, bdcEndOfMonth, holidayTargetDecember31, holidayGBChristmasDay, december31, dccNL365, dccActual365L, ChristmasDay, isholiday, bdCalendar, isholiday, observedHolidays, holidayEasterNCo, holidayTargetChristmasDay, bdcPredecing, holiday, holidayWhitMonday, isbday, holidayUSVeteransDay, holidayGBNewYearsDay, isholidayUSNYSE, holidayUSIndependenceDay, holidayGoodFriday, isholidayUSBondMrkt, klndrUSNYSE, holidayEasterSunday, holidayUSThanksgivingDay, holidayGBSctStAndrewsDay, OrangemensDay, holidayGBSctJanuary2, dccThirtyU360, NewYearsDay, nextMondayIfWeekend, holidayUSColumbusDay, klndrUSFederalGovt, holidayEasterMonday, observedHolidays, StPatricksDay, holidayGBNirStPatricksDay, dccThirtyA360, bdcModifFollowingFornight, $, nextMondayIfSunday, newCalendarGBNir, bdDayCountConvention, holidayUSNewYearsDay, StAndrewsDay, USIndependenceDay, isholidayScotland, dccActual366, bdcModifFollowing, December31, dccActual36525, klndrUSBondMrkt, newCalendarUSNYSE, stAndrewsDay, holidayGBEngNirWlsSummer, newCalendarGBSct, holidayUSNYSENewYearsDay, yearFraction, dccThirtyE360, USJuneteenthIndependenceDay, bdBusinessDayConvention, klndrGBEngWls, klndrWeekendsOnly, info, LabourDay, newCalendarTARGET, newCalendarNoHolidayOrWeekend, holidayTargetNewYearsDay, holidayTargetBoxingDay, holidayUSJuneteenthIndependenceDay, newCalendarStaticHolidays, holidayUSMartinLutherKingBirthday, nextWeekday, klndrGBSct, dccActual365F, bdays, addbdays, nextbday