almanac implements a grammar of schedules, providing the fundamental rules and concepts that allow you to build up a schedule of “events”, such as holidays or weekends. After creating a schedule from the fundamental rules, it can be used to:
Generate dates that fall in the schedule.
Determine if a date is in the schedule or not.
Shift a sequence of dates, stepping over or avoiding dates that fall in the schedule.
You can NOT install the released version of almanac from CRAN with:
And the development version from GitHub with:
An event is a set of rules that define a reoccuring date, such as New Years Day, or the third Monday in November.
Events are created from a family of related functions that all start with one of:
on_*()
before_*()
after_*()
between_*()
For example, on_mday(25)
defines the reoccuring event of the 25th day of the month.
To determine if a date falls on an event, use event_in()
.
event_in("2019-01-25", on_25th)
#> [1] TRUE
event_in("2019-01-23", on_25th)
#> [1] FALSE
event_in("2019-02-25", on_25th)
#> [1] TRUE
Events can be combined by taking an intersection, a union, or a difference. Here, we take an intersection, which should be read as “the 25th of the month and the month of November” to define the event of November 25th.
event_intersect(on_25th, on_month("Nov"))
#> <Intersection>
#> - <On day of the month: 25>
#> - <On month of the year: November>
It’s often easier to use the shortcut operators &
, |
, and -
.
A schedule is a collection of events. You can create them by starting with an empty schedule()
, and then adding events with sch_add()
.
# Labor Day = First Monday in September
on_labor_day <- on_month("Sep") & on_wday("Mon") & on_mweek(1)
# Christmas = December 25th
on_christmas <- on_month("Dec") & on_mday(25)
sch <- schedule() %>%
sch_add(on_labor_day, "Labor Day") %>%
sch_add(on_christmas, "Christmas")
sch
#> <Schedule [2 event(s)]>
#> - Labor Day
#> - Christmas
Check if a date is in a schedule with sch_in()
.
Generate dates in the schedule with sch_seq()
.
sch_seq("2018-01-01", "2020-12-31", sch)
#> [1] "2018-09-03" "2018-12-25" "2019-09-02" "2019-12-25" "2020-09-07"
#> [6] "2020-12-25"
If you have an existing set of dates, you can adjust them relative to the schedule with sch_adjust()
. If a date happens to fall on an event in the schedule, then an adjustment
is applied repeatedly until the next non-event date is found.
x <- as.Date("2019-09-01") + 0:3
x
#> [1] "2019-09-01" "2019-09-02" "2019-09-03" "2019-09-04"
# Labor day is the 2nd of Sept in 2019
x[sch_in(x, sch)]
#> [1] "2019-09-02"
# Every date is left alone except 2019-09-02. The adjustment is applied as
# `x + adjustment` to get to 2019-09-03.
sch_adjust(x, sch, adjustment = days(1))
#> [1] "2019-09-01" "2019-09-03" "2019-09-03" "2019-09-04"
You can shift a set of dates relative to the schedule with sch_jump()
and sch_step()
.
Jumping applies a period shift all at once. You start at x
and end at x + jump
. If, after the jump, the date you landed on is an event, an adjustment is made to find the next non-event date.
christmas_eve <- as.Date("2019-12-24")
# Jump forward 1 day - Lands on the 25th, Christmas
# An adjustment of 1 day is made to shift to the 26th
sch_jump(christmas_eve, jump = days(1), schedule = sch, adjustment = days(1))
#> [1] "2019-12-26"
# Jump forward 2 days - Lands on the 26th
# No adjustment is required
sch_jump(christmas_eve, jump = days(2), schedule = sch, adjustment = days(1))
#> [1] "2019-12-26"
In the above example, jumping forward 2 days might not have the intended result if you wanted to look forward “2 business days”. In those cases, use sch_step()
. This steps 1 day at a time, and after each step checks if the date you are on is an event. If it is, it applies the adjustment
, then continues to the next step.
almanac is a combination of ideas from the date library of QuantLib with the great package from James Laird-Smith, gs. Both gs and almanac are based on a paper written by Martin Fowler, which outlines the grammar for reoccuring events and schedules.
The hope is that gs and almanac will merge, as they currently overlap a large amount.