The concept of business days is pretty straightforward. For most people, business days are the 5 days a week we commute to work.
Questions like:
- What is the next business day?
- What was the previous business day?
- List the next 5 days.
are all pretty straightforward and I expect that the average high school graduate can answer it with the aid of a calendar (assuming it includes holidays).
For some reason, the implementations I’ve seen have been pretty hideous. So here is, in my opinion, an elegant solution that also demonstrates some neat features of KDB/Q.
The first step is to get all the business days in the year. (Depending on your region/industry you might have different days. I’ll use USA business days M-F with 10 government holidays (see US court business days).
/1 calendar year
year:2018.01.01+til 365
holidays:2018.01.01 2018.01.15 2018.02.19 2018.05.28 2018.07.04 2018.09.03 2018.10.08 2018.11.12 2018.11.22 2018.12.25
yearNoWeekend:year where in[;2 3 4 5 6] year mod 7
buisnessYear:yearNoWeekend except holidays
/
Once you have the buisnessYear for the relevant period
—
we need to be aware that the calendar will not work correctly for data we have not loaded
—
We can create a data structure that will allow us to answer all these types of questions.
The data structure we will be using is a sorted dictionary.
We will have a dictionary, nDay (next day) and a pDay function (previous day).
We can look up any date in our dictionary/function to find the next or previous date.
We use a sorted dictionary so that when we look up non buisness days,
q will return the value of the closest next/previous date.
\
/create our dictionary and function,
/fill the last day in the next day list with the first day from 2019
nDay:`s#buisnessYear!`s#2019.01.02^next buisnessYear
pDay:{[nDay;d]p:nDay?d; ?[null p;?[nDay] nDay d;p]}[nDay]/let’s find the next day July 3rd
nDay 2018.07.03
/as expected returns 2018.07.05
nDay 2018.07.04
/as expected also returns 2018.07.05/let’s find the previous day
pDay 2018.07.05
/as expected returns 2018.07.03
pDay 2018.07.04
/as expected returns 2018.07.03/let’s find 5th day after a given date
nDay/[5;2018.07.01]
/as expected we get 2018.07.09
/lets find 5th day previous to a given date
pDay/[5;2018.07.01]
/as expected we get 2018.06.25/We can ask to get the next 5 days
nDay\[5;2018.07.01]
/we get the given day and next 5 days
/2018.07.01 2018.07.02 2018.07.03 2018.07.05 2018.07.06 2018.07.09
/We can ask to get the previous 5 days
pDay\[5;2018.07.01]
/we get the given day and previous 5 days in reverse order
/2018.07.01 2018.06.29 2018.06.28 2018.06.27 2018.06.26 2018.06.25/We can ask for the next days for 5 dates
nDay 2018.01.14 2018.02.18 2018.05.27 2018.09.02 2018.10.07
/ we get 2018.01.16 2018.02.20 2018.05.29 2018.09.04 2018.10.09/We can ask for the previous days for 5 dates
pDay 2018.01.16 2018.02.20 2018.05.29 2018.09.04 2018.10.09
/we get 2018.01.12 2018.02.16 2018.05.25 2018.08.31 2018.10.05
Bonus Section, and the real reason for the title of the essay:
Here is a final piece that might strike you as a bit weird until you untangle it.
In General:
0b=(pDay nDay d)~(nDay pDay d)
In other words, these functions don’t commute. This seems strange since nDay is simply addition by one and pDay is simply subtraction by one.
This was used by the mathematician Peano to show how you can get all the natural numbers. Simply start with 1 and keep adding 1. If you want whole numbers start with 0. If you want integers create the concept of subtraction and you can count your way to negative infinity.
This is called Peano arithmetic and it commutes. You can always get back to where you started by simply adding or subtracting one appropriately. The order in which you do so does not matter.
However, business days exhibit a certain property that prevents them from commuting. Namely, although any calendar day has a next business day, not all days are business days. Which means that non-business days are not represented intuitively. For example, the next business day of Saturday is Monday and previous business day of Monday is Friday, but the previous business day of Saturday is Friday and so the next business day of Friday is Monday. Depending on whether we started with pDay or nDay we get different results.
The missing weekend from the business days illustrates the reason why, in general, floating arithmetic does not commute. There are numbers that cannot be represented by floating point arithmetic which means that sometimes the order of additions and subtractions will make a difference to the final result.