Title: | Tools for Working with Recurrence Rules |
---|---|
Description: | Provides tools for defining recurrence rules and recurrence sets. Recurrence rules are a programmatic way to define a recurring event, like the first Monday of December. Multiple recurrence rules can be combined into larger recurrence sets. A full holiday and calendar interface is also provided that can generate holidays within a particular year, can detect if a date is a holiday, can respect holiday observance rules, and allows for custom holidays. |
Authors: | Davis Vaughan [aut, cre], Posit Software, PBC [cph, fnd] |
Maintainer: | Davis Vaughan <[email protected]> |
License: | MIT + file LICENSE |
Version: | 1.0.0.9000 |
Built: | 2024-12-04 03:26:14 UTC |
Source: | https://github.com/davisvaughan/almanac |
This family of adj_*()
functions encode business logic for common
date adjustments. If x
falls on an event date, it is adjusted according to
the function's adjustment rule. Otherwise it is left untouched.
adj_following()
Choose the first non-event date after x
.
adj_preceding()
Choose the first non-event date before x
.
adj_modified_following()
Choose the first non-event date after x
, unless it falls in a
different month, in which case the first non-event date before x
is
chosen instead.
adj_modified_preceding()
Choose the first non-event date before x
, unless it falls in a
different month, in which case the first non-event date after x
is
chosen instead.
adj_nearest()
Choose the nearest non-event date to x
. If the closest preceding and
following non-event dates are equally far away, the following non-event
date is chosen.
adj_none()
Performs no adjustment and returns x
unchanged.
adj_following(x, rschedule) adj_preceding(x, rschedule) adj_modified_following(x, rschedule) adj_modified_preceding(x, rschedule) adj_nearest(x, rschedule) adj_none(x, rschedule)
adj_following(x, rschedule) adj_preceding(x, rschedule) adj_modified_following(x, rschedule) adj_modified_preceding(x, rschedule) adj_nearest(x, rschedule) adj_none(x, rschedule)
x |
A vector of dates. |
rschedule |
An rschedule, such as an rrule, runion, rintersect, or rsetdiff. |
An adjusted vector of Dates.
# A Saturday x <- as.Date("1970-01-03") on_weekends <- weekly() %>% recur_on_weekends() # Adjust forward to Monday adj_following(x, on_weekends) # Adjust backwards to Friday adj_preceding(x, on_weekends) # Adjust to nearest non-event date adj_nearest(x, on_weekends) adj_nearest(x + 1, on_weekends) # Sundays, one of which is at the end of the month sundays <- as.Date(c("2020-05-24", "2020-05-31")) # Adjust forward, unless that takes us into a new month, in which case we # adjust backwards. adj_modified_following(sundays, on_weekends) # Saturdays, one of which is at the beginning of the month saturdays <- as.Date(c("2020-08-01", "2020-08-08")) # Adjust backwards, unless that takes us into a new month, in which # case we adjust forwards adj_modified_preceding(saturdays, on_weekends)
# A Saturday x <- as.Date("1970-01-03") on_weekends <- weekly() %>% recur_on_weekends() # Adjust forward to Monday adj_following(x, on_weekends) # Adjust backwards to Friday adj_preceding(x, on_weekends) # Adjust to nearest non-event date adj_nearest(x, on_weekends) adj_nearest(x + 1, on_weekends) # Sundays, one of which is at the end of the month sundays <- as.Date(c("2020-05-24", "2020-05-31")) # Adjust forward, unless that takes us into a new month, in which case we # adjust backwards. adj_modified_following(sundays, on_weekends) # Saturdays, one of which is at the beginning of the month saturdays <- as.Date(c("2020-08-01", "2020-08-08")) # Adjust backwards, unless that takes us into a new month, in which # case we adjust forwards adj_modified_preceding(saturdays, on_weekends)
alma_events()
retrieves all of the events in the rschedule's
event set.
alma_events(rschedule, ..., year = NULL)
alma_events(rschedule, ..., year = NULL)
rschedule |
An rschedule, such as an rrule, runion, rintersect, or rsetdiff. |
... |
These dots are for future extensions and must be empty. |
year |
An optional integer vector of years to limit the returned events to. |
A Date vector of events.
rrule <- daily(since = "1970-01-01", until = "1970-01-05") alma_events(rrule) on_christmas <- yearly() %>% recur_on_month_of_year("Dec") %>% recur_on_day_of_month(25) alma_events(on_christmas, year = c(2020, 2022))
rrule <- daily(since = "1970-01-01", until = "1970-01-05") alma_events(rrule) on_christmas <- yearly() %>% recur_on_month_of_year("Dec") %>% recur_on_day_of_month(25) alma_events(on_christmas, year = c(2020, 2022))
alma_in()
checks if x
is in the event set of dates defined by the
rschedule.
alma_in(x, rschedule)
alma_in(x, rschedule)
x |
A vector of dates. |
rschedule |
An rschedule, such as an rrule, runion, rintersect, or rsetdiff. |
A logical vector the same size as x
.
rrule <- weekly() %>% recur_on_day_of_week("Thursday") # A Thursday and Friday x <- as.Date("1970-01-01") + 0:1 alma_in(x, rrule) # Every month, on the 2nd day of the month rrule2 <- monthly() %>% recur_on_day_of_month(2) # Make a larger set of multiple rules rb <- runion(rrule, rrule2) alma_in(x, rb)
rrule <- weekly() %>% recur_on_day_of_week("Thursday") # A Thursday and Friday x <- as.Date("1970-01-01") + 0:1 alma_in(x, rrule) # Every month, on the 2nd day of the month rrule2 <- monthly() %>% recur_on_day_of_month(2) # Make a larger set of multiple rules rb <- runion(rrule, rrule2) alma_in(x, rb)
alma_next()
generates the next event after x
.
alma_previous()
generates the previous event before x
.
alma_next(x, rschedule, inclusive = FALSE) alma_previous(x, rschedule, inclusive = FALSE)
alma_next(x, rschedule, inclusive = FALSE) alma_previous(x, rschedule, inclusive = FALSE)
x |
A vector of dates. |
rschedule |
An rschedule, such as an rrule, runion, rintersect, or rsetdiff. |
inclusive |
If |
A Date vector the same size as x
.
on_12th <- monthly() %>% recur_on_day_of_month(12) on_monday <- weekly() %>% recur_on_day_of_week("Monday") # On the 12th of the month, or on Mondays rb <- runion(on_12th, on_monday) alma_next(c("2019-01-01", "2019-01-11"), rb) alma_previous(c("2019-01-01", "2019-01-11"), rb)
on_12th <- monthly() %>% recur_on_day_of_month(12) on_monday <- weekly() %>% recur_on_day_of_week("Monday") # On the 12th of the month, or on Mondays rb <- runion(on_12th, on_monday) alma_next(c("2019-01-01", "2019-01-11"), rb) alma_previous(c("2019-01-01", "2019-01-11"), rb)
alma_search()
retrieves all events between from
and to
.
alma_search(from, to, rschedule, inclusive = TRUE)
alma_search(from, to, rschedule, inclusive = TRUE)
from , to
|
Dates defining the range to look for events. |
rschedule |
An rschedule, such as an rrule, runion, rintersect, or rsetdiff. |
inclusive |
If |
A Date vector of all events between from
and to
.
on_12th <- monthly() %>% recur_on_day_of_month(12) on_monday <- weekly() %>% recur_on_day_of_week("Monday") # On the 12th of the month, or on Mondays rb <- runion(on_12th, on_monday) alma_search("2019-01-01", "2019-01-31", rb)
on_12th <- monthly() %>% recur_on_day_of_month(12) on_monday <- weekly() %>% recur_on_day_of_week("Monday") # On the 12th of the month, or on Mondays rb <- runion(on_12th, on_monday) alma_search("2019-01-01", "2019-01-31", rb)
alma_seq()
generates a sequence of all dates between from
and to
,
skipping any events defined by the rschedule
.
alma_seq(from, to, rschedule, inclusive = TRUE)
alma_seq(from, to, rschedule, inclusive = TRUE)
from , to
|
Dates defining the range to look for events. |
rschedule |
An rschedule, such as an rrule, runion, rintersect, or rsetdiff. |
inclusive |
If |
A vector of dates in the range of [from, to]
, with all events in the
rschedule
removed.
on_weekends <- weekly() %>% recur_on_weekends() # Generate a sequence of all non-weekend dates in Jan-2000 alma_seq("2000-01-01", "2000-01-31", on_weekends)
on_weekends <- weekly() %>% recur_on_weekends() # Generate a sequence of all non-weekend dates in Jan-2000 alma_seq("2000-01-01", "2000-01-31", on_weekends)
alma_step()
is useful for shifting dates by "n business days".
alma_step()
steps over a sequence of dates 1 day at a time, for n
days.
After each step, an adjustment is applied to shift to the next non-event
date.
If n
is positive, adj_following()
is called.
If n
is negative, adj_preceding()
is called.
If n
is zero, it was arbitrarily decided to call adj_following()
to
roll to the next available non-event date.
alma_step(x, n, rschedule)
alma_step(x, n, rschedule)
x |
A vector of dates. |
n |
The number of days to step. Can be negative to step backwards. |
rschedule |
An rschedule, such as an rrule, runion, rintersect, or rsetdiff. |
Imagine you are on a Friday and want to shift forward 2 days using an
rrule that marks weekends as events. alma_step()
works like this:
Step forward 1 day to Saturday.
Apply an adjustment of adj_following()
, which rolls forward to Monday.
Step forward 1 day to Tuesday.
Apply an adjustment of adj_following()
, but no adjustment is required.
This lends itself naturally to business logic. Two business days from Friday is Tuesday.
A Date vector the same size as x
shifted by n
steps.
# Make a rrule for weekends on_weekends <- weekly() %>% recur_on_weekends() # "Step forward by 2 business days" # 2019-09-13 is a Friday. # Here we: # - Step 1 day to Saturday # - Adjust to Monday # - Step 1 day to Tuesday alma_step("2019-09-13", 2, on_weekends) # If Monday, 2019-09-16, was a recurring holiday, we could create # a custom runion and step over that too. on_09_16 <- yearly() %>% recur_on_month_of_year(9) %>% recur_on_day_of_month(16) rb <- runion(on_09_16, on_weekends) alma_step("2019-09-13", 2, rb)
# Make a rrule for weekends on_weekends <- weekly() %>% recur_on_weekends() # "Step forward by 2 business days" # 2019-09-13 is a Friday. # Here we: # - Step 1 day to Saturday # - Adjust to Monday # - Step 1 day to Tuesday alma_step("2019-09-13", 2, on_weekends) # If Monday, 2019-09-16, was a recurring holiday, we could create # a custom runion and step over that too. on_09_16 <- yearly() %>% recur_on_month_of_year(9) %>% recur_on_day_of_month(16) rb <- runion(on_09_16, on_weekends) alma_step("2019-09-13", 2, rb)
almanac_since()
represents the default since
date used in almanac. It
defaults to 1900-01-01
, a Monday.
almanac_until()
represents the default until
date used in almanac. It
defaults to 2100-01-01
, a Friday.
The choice of since
and until
are somewhat arbitrary, but should generate
a useful event set range for most rschedules. If you need to adjust the
defaults, then you should supply the since
and until
arguments directly
to the rrule generators, like yearly()
and weekly()
.
The since
default is particularly important for weekly recurrence rules,
where the since
date represents the anchor point to begin counting from.
See recur_on_day_of_week()
for examples of how to adjust this.
almanac_since() almanac_until()
almanac_since() almanac_until()
almanac_since() almanac_until()
almanac_since() almanac_until()
cal_events()
returns a data frame of holiday name / event date pairs for
a calendar. It is similar to alma_events()
, but returns information about
the name of the holiday and has specialized behavior related to observed
dates when filtering by year
.
cal_events(x, ..., year = NULL, observed = FALSE)
cal_events(x, ..., year = NULL, observed = FALSE)
x |
An rcalendar. |
... |
These dots are for future extensions and must be empty. |
year |
An integer vector of years to filter for. |
observed |
When filtering for specific |
A two column data frame:
name
is a character vector of holiday names.
date
is a Date vector of holiday event dates.
on_weekends <- weekly() %>% recur_on_weekends() # New Year's Day, observed on the nearest weekday if it falls on a weekend on_new_years <- hol_new_years_day() %>% hol_observe(on_weekends, adj_nearest) # Christmas, observed on the nearest weekday if it falls on a weekend on_christmas <- hol_christmas() %>% hol_observe(on_weekends, adj_nearest) cal <- rcalendar(on_new_years, on_christmas) cal # In 2010, Christmas fell on a Saturday and was adjusted backwards cal_events(cal, year = 2010) # In 2011, New Year's fell on a Saturday and was adjusted backwards. # Note that the returned date is in 2010, even though we requested holidays # for 2011, because most people would consider the actual New Year's date of # 2011-01-01 part of the 2011 set of holidays, even though it was observed in # 2010. cal_events(cal, year = 2011) # If you want to filter by the observed date, set `observed = TRUE`, which # will move the New Year's Day that was observed in 2010 to the 2010 result cal_events(cal, year = 2010, observed = TRUE) cal_events(cal, year = 2011, observed = TRUE)
on_weekends <- weekly() %>% recur_on_weekends() # New Year's Day, observed on the nearest weekday if it falls on a weekend on_new_years <- hol_new_years_day() %>% hol_observe(on_weekends, adj_nearest) # Christmas, observed on the nearest weekday if it falls on a weekend on_christmas <- hol_christmas() %>% hol_observe(on_weekends, adj_nearest) cal <- rcalendar(on_new_years, on_christmas) cal # In 2010, Christmas fell on a Saturday and was adjusted backwards cal_events(cal, year = 2010) # In 2011, New Year's fell on a Saturday and was adjusted backwards. # Note that the returned date is in 2010, even though we requested holidays # for 2011, because most people would consider the actual New Year's date of # 2011-01-01 part of the 2011 set of holidays, even though it was observed in # 2010. cal_events(cal, year = 2011) # If you want to filter by the observed date, set `observed = TRUE`, which # will move the New Year's Day that was observed in 2010 to the 2010 result cal_events(cal, year = 2010, observed = TRUE) cal_events(cal, year = 2011, observed = TRUE)
cal_match()
matches a date in x
to a holiday in rcalendar
and returns
the corresponding holiday name, or NA
if it doesn't exist in the calendar.
If a date corresponds to multiple holidays, the holiday that was added to the calendar first is returned.
This function is intended to be similar to base::match()
.
cal_match(x, rcalendar)
cal_match(x, rcalendar)
x |
A date vector to match. |
rcalendar |
A calendar to look for holiday matches in. |
A character vector the same size as x
.
cal <- rcalendar( hol_christmas(), hol_halloween(), hol_new_years_day(), hol_us_presidents_day() ) x <- as.Date(c( "2019-01-02", "2019-12-25", "2018-02-19", "2018-02-20", "2020-10-31" )) cal_match(x, cal)
cal <- rcalendar( hol_christmas(), hol_halloween(), hol_new_years_day(), hol_us_presidents_day() ) x <- as.Date(c( "2019-01-02", "2019-12-25", "2018-02-19", "2018-02-20", "2020-10-31" )) cal_match(x, cal)
cal_names()
returns the names of the holidays in a calendar.
cal_names(x)
cal_names(x)
x |
An rcalendar. |
A character vector of holiday names.
x <- rcalendar(hol_christmas(), hol_new_years_day()) cal_names(x)
x <- rcalendar(hol_christmas(), hol_new_years_day()) cal_names(x)
cal_us_federal()
is an example calendar that represents the federal
holidays in the United States. It makes no attempt to be historically
accurate, but instead represents the currently recognized federal holidays.
The calendar represents the observed dates of each holiday, rather than the
actual dates of each holiday (i.e. if a holiday falls on a Saturday, it is
federally observed on the preceding Friday).
Refer to the source code of cal_us_federal()
to get a feel for how to
build your own personal calendar.
cal_us_federal(since = NULL, until = NULL)
cal_us_federal(since = NULL, until = NULL)
since |
A lower bound on the event set to generate. Defaults to |
until |
An upper bound on the event set to generate. Defaults to |
An rcalendar.
cal <- cal_us_federal() # All 2023 holidays cal_events(cal, year = 2023) # Notice that for 2028, `cal_events()` knows that you probably want to # treat New Year's Day as a 2028 holiday even though it will observed in # 2027 (because it will be a Saturday and will be rolled back to being # observed on Friday) cal_events(cal, year = 2028) # Were any of these dates on a holiday? x <- as.Date(c( "2023-11-10", "2023-10-05", "2023-06-19", "2023-05-29", "2023-05-28" )) alma_in(x, cal) # Which one? cal_match(x, cal)
cal <- cal_us_federal() # All 2023 holidays cal_events(cal, year = 2023) # Notice that for 2028, `cal_events()` knows that you probably want to # treat New Year's Day as a 2028 holiday even though it will observed in # 2027 (because it will be a Saturday and will be rolled back to being # observed on Friday) cal_events(cal, year = 2028) # Were any of these dates on a holiday? x <- as.Date(c( "2023-11-10", "2023-10-05", "2023-06-19", "2023-05-29", "2023-05-28" )) alma_in(x, cal) # Which one? cal_match(x, cal)
cal_add()
adds an rholiday to an rcalendar.
cal_remove()
removes an rholiday from an rcalendar by name, either by
specifying a character name or an rholiday object with the same name.
cal_add(x, rholiday) cal_remove(x, what)
cal_add(x, rholiday) cal_remove(x, what)
x |
An rcalendar. |
rholiday |
An rholiday to add to the rcalendar. |
what |
The name of a holiday to remove from the rcalendar, or an rholiday object with the corresponding name that you'd like to remove. |
A new rcalendar with the holiday added or removed.
cal <- rcalendar( hol_christmas(), hol_halloween(), hol_new_years_day(), hol_us_presidents_day() ) # Can't forget Easter! cal %>% cal_add(hol_easter()) # Didn't actually need Halloween cal %>% cal_remove(hol_halloween()) # Can remove by name or by object cal %>% cal_remove("Halloween")
cal <- rcalendar( hol_christmas(), hol_halloween(), hol_new_years_day(), hol_us_presidents_day() ) # Can't forget Easter! cal %>% cal_add(hol_easter()) # Didn't actually need Halloween cal %>% cal_remove(hol_halloween()) # Can remove by name or by object cal %>% cal_remove("Halloween")
cal_next()
generates the next holiday after x
.
cal_previous()
generates the previous holiday before x
.
If no holiday exists before/after x
, a missing row is generated.
cal_next(x, rcalendar, ..., inclusive = FALSE) cal_previous(x, rcalendar, ..., inclusive = FALSE)
cal_next(x, rcalendar, ..., inclusive = FALSE) cal_previous(x, rcalendar, ..., inclusive = FALSE)
x |
A vector of dates. |
rcalendar |
An rcalendar. |
... |
These dots are for future extensions and must be empty. |
inclusive |
If |
A two column data frame, like cal_events()
, which is the same size as x
and contains either the next or previous holiday relative to x
.
x <- as.Date(c("2023-04-11", "2023-08-10", "2021-05-06")) cal <- cal_us_federal() cal_next(x, cal) cal_previous(x, cal)
x <- as.Date(c("2023-04-11", "2023-08-10", "2021-05-06")) cal <- cal_us_federal() cal_next(x, cal) cal_previous(x, cal)
These three functions allow you to tweak existing holidays created by
rholiday()
so that they more properly align with business calendars. The
resulting holidays can then be added into an rcalendar()
.
hol_observe()
adjusts a holiday based on when it is actually observed.
For example, many holidays that occur on a Saturday are actually observed
on the preceding Friday or following Monday.
hol_offset()
creates a new holiday by offsetting it from an existing
one. For example, Boxing Day is the day after Christmas, and the observance
of Boxing Day may be dependent on the observance of Christmas (i.e. if
Christmas is Sunday, it may be observed on Monday, so Boxing Day would be
observed on Tuesday).
hol_rename()
renames an existing holiday. This is typically useful after
a call to hol_offset()
, since it doesn't rename the holiday but you may
want to give it a different name.
hol_observe(x, adjust_on, adjustment) hol_offset(x, by) hol_rename(x, name)
hol_observe(x, adjust_on, adjustment) hol_offset(x, by) hol_rename(x, name)
x |
An rholiday. |
adjust_on |
An rschedule that determines when the |
adjustment |
An adjustment function to apply to problematic dates. Typically one
of the pre-existing adjustment functions, like A custom adjustment function must have two arguments |
by |
A single integer to offset by. |
name |
A new name for the holiday. |
on_weekends <- weekly() %>% recur_on_weekends() # Christmas, adjusted to nearest Friday or Monday if it falls on a weekend on_christmas <- hol_christmas() %>% hol_observe(on_weekends, adj_nearest) # Boxing Day is the day after Christmas. # If observed Christmas is a Friday, then observed Boxing Day should be Monday. # If observed Christmas is a Monday, then observed Boxing Day should be Tuesday. on_boxing_day <- on_christmas %>% hol_offset(1) %>% hol_observe(on_weekends, adj_following) %>% hol_rename("Boxing Day") christmas_dates <- alma_events(on_christmas, year = 2010:2015) boxing_day_dates <- alma_events(on_boxing_day, year = 2010:2015) data.frame( christmas = christmas_dates, boxing_day = boxing_day_dates, christmas_weekday = lubridate::wday(christmas_dates, label = TRUE), boxing_day_weekday = lubridate::wday(boxing_day_dates, label = TRUE) )
on_weekends <- weekly() %>% recur_on_weekends() # Christmas, adjusted to nearest Friday or Monday if it falls on a weekend on_christmas <- hol_christmas() %>% hol_observe(on_weekends, adj_nearest) # Boxing Day is the day after Christmas. # If observed Christmas is a Friday, then observed Boxing Day should be Monday. # If observed Christmas is a Monday, then observed Boxing Day should be Tuesday. on_boxing_day <- on_christmas %>% hol_offset(1) %>% hol_observe(on_weekends, adj_following) %>% hol_rename("Boxing Day") christmas_dates <- alma_events(on_christmas, year = 2010:2015) boxing_day_dates <- alma_events(on_boxing_day, year = 2010:2015) data.frame( christmas = christmas_dates, boxing_day = boxing_day_dates, christmas_weekday = lubridate::wday(christmas_dates, label = TRUE), boxing_day_weekday = lubridate::wday(boxing_day_dates, label = TRUE) )
This page lists a number of pre-created holidays that can be added to a
calendar created with rcalendar()
. This list makes no attempt to be
comprehensive. If you need to create your own holiday, you can do so with
rholiday()
.
It also makes no attempt to be historically accurate, i.e. Juneteenth was
created in 2021, but hol_us_juneteenth()
will generate event dates before
that. Because rholiday()
takes an arbitrary rschedule object, you can
always create an rschedule that is historically accurate and use that
instead.
hol_christmas(since = NULL, until = NULL) hol_christmas_eve(since = NULL, until = NULL) hol_easter(since = NULL, until = NULL) hol_good_friday(since = NULL, until = NULL) hol_halloween(since = NULL, until = NULL) hol_new_years_day(since = NULL, until = NULL) hol_new_years_eve(since = NULL, until = NULL) hol_st_patricks_day(since = NULL, until = NULL) hol_valentines_day(since = NULL, until = NULL) hol_us_election_day(since = NULL, until = NULL) hol_us_fathers_day(since = NULL, until = NULL) hol_us_independence_day(since = NULL, until = NULL) hol_us_indigenous_peoples_day(since = NULL, until = NULL) hol_us_juneteenth(since = NULL, until = NULL) hol_us_labor_day(since = NULL, until = NULL) hol_us_martin_luther_king_junior_day(since = NULL, until = NULL) hol_us_memorial_day(since = NULL, until = NULL) hol_us_mothers_day(since = NULL, until = NULL) hol_us_presidents_day(since = NULL, until = NULL) hol_us_thanksgiving(since = NULL, until = NULL) hol_us_veterans_day(since = NULL, until = NULL)
hol_christmas(since = NULL, until = NULL) hol_christmas_eve(since = NULL, until = NULL) hol_easter(since = NULL, until = NULL) hol_good_friday(since = NULL, until = NULL) hol_halloween(since = NULL, until = NULL) hol_new_years_day(since = NULL, until = NULL) hol_new_years_eve(since = NULL, until = NULL) hol_st_patricks_day(since = NULL, until = NULL) hol_valentines_day(since = NULL, until = NULL) hol_us_election_day(since = NULL, until = NULL) hol_us_fathers_day(since = NULL, until = NULL) hol_us_independence_day(since = NULL, until = NULL) hol_us_indigenous_peoples_day(since = NULL, until = NULL) hol_us_juneteenth(since = NULL, until = NULL) hol_us_labor_day(since = NULL, until = NULL) hol_us_martin_luther_king_junior_day(since = NULL, until = NULL) hol_us_memorial_day(since = NULL, until = NULL) hol_us_mothers_day(since = NULL, until = NULL) hol_us_presidents_day(since = NULL, until = NULL) hol_us_thanksgiving(since = NULL, until = NULL) hol_us_veterans_day(since = NULL, until = NULL)
since |
A lower bound on the event set to generate. Defaults to |
until |
An upper bound on the event set to generate. Defaults to |
Note that relative holidays, such as New Year's Eve, which is 1 day before
New Year's Day, aren't pre-created in a way that allows you to define
observance rules for them that depend on the observance rules of the holiday
they are relative to. If you need to do this, you should start with the base
holiday, here hol_new_years_day()
, and use hol_observe()
and
hol_offset()
on that to generate a New Year's Eve holiday that matches
your required observance rules. See the examples of hol_offset()
for more
information.
on_christmas <- hol_christmas() on_christmas # These are like any other rschedule object alma_events(on_christmas, year = 2020:2025) # But they can also be added into an rcalendar cal <- rcalendar( on_christmas, hol_halloween(), hol_new_years_day(), hol_us_presidents_day() ) cal # Which gives you access to a number of `cal_*()` functions cal_events(cal, year = 2020:2022)
on_christmas <- hol_christmas() on_christmas # These are like any other rschedule object alma_events(on_christmas, year = 2020:2025) # But they can also be added into an rcalendar cal <- rcalendar( on_christmas, hol_halloween(), hol_new_years_day(), hol_us_presidents_day() ) cal # Which gives you access to a number of `cal_*()` functions cal_events(cal, year = 2020:2022)
new_rschedule()
is a developer focused tool that is not required for
normal usage of almanac. It is only exported to allow other packages
to construct new rschedule objects that work with almanac functions
prefixed with alma_*()
, like alma_in()
.
rschedule_events()
is a generic function that rschedule subclasses must
provide a method for. rschedule_events()
should return a Date vector
containing the complete ordered set of events in the event set of
that rschedule.
new_rschedule(..., class) rschedule_events(x)
new_rschedule(..., class) rschedule_events(x)
... |
Named data fields. |
class |
A required subclass. |
x |
An object that subclasses rschedule. |
An rschedule is an abstract class that rrule and rset both inherit from.
The sole functionality of rschedule classes is to provide a method for
rschedule_events()
.
For new_rschedule()
, a new rschedule subclass.
For rschedule_events()
, a Date vector of events.
events <- as.Date("1970-01-01") static <- new_rschedule( events = events, class = "static_rschedule" ) # You have to register an `rschedule_events()` method first! try(alma_events(static))
events <- as.Date("1970-01-01") static <- new_rschedule( events = events, class = "static_rschedule" ) # You have to register an `rschedule_events()` method first! try(alma_events(static))
radjusted()
creates a new adjusted rschedule on top of an existing one. The
new rschedule contains the same event dates as the existing rschedule,
except when they intersect with the dates in the event set of the
rschedule, adjust_on
. In those cases, an adjustment
is applied to the
problematic dates to shift them to valid event dates.
This is most useful when creating corporate holiday rschedules. For example,
Christmas always falls on December 25th, but if it falls on a Saturday,
your company might observe Christmas on the previous Friday. If it falls
on a Sunday, you might observe it on the following Monday. In this case,
you could construct an rschedule for a recurring event of December 25th,
and a second rschedule for weekends. When Christmas falls on a weekend,
you would apply an adjustment of adj_nearest()
to get the observance date.
radjusted(rschedule, adjust_on, adjustment)
radjusted(rschedule, adjust_on, adjustment)
rschedule |
An rschedule, such as an rrule, runion, rintersect, or rsetdiff. |
adjust_on |
An rschedule that determines when the |
adjustment |
An adjustment function to apply to problematic dates. Typically one
of the pre-existing adjustment functions, like A custom adjustment function must have two arguments |
An adjusted rschedule.
since <- "2000-01-01" until <- "2010-01-01" on_christmas <- yearly(since = since, until = until) %>% recur_on_month_of_year("Dec") %>% recur_on_day_of_month(25) # All Christmas dates, with no adjustments alma_events(on_christmas) on_weekends <- weekly(since = since, until = until) %>% recur_on_weekends() # Now all Christmas dates that fell on a weekend are # adjusted either forwards or backwards, depending on which # non-event date was closer on_adj_christmas <- radjusted(on_christmas, on_weekends, adj_nearest) alma_events(on_adj_christmas)
since <- "2000-01-01" until <- "2010-01-01" on_christmas <- yearly(since = since, until = until) %>% recur_on_month_of_year("Dec") %>% recur_on_day_of_month(25) # All Christmas dates, with no adjustments alma_events(on_christmas) on_weekends <- weekly(since = since, until = until) %>% recur_on_weekends() # Now all Christmas dates that fell on a weekend are # adjusted either forwards or backwards, depending on which # non-event date was closer on_adj_christmas <- radjusted(on_christmas, on_weekends, adj_nearest) alma_events(on_adj_christmas)
rcalendar()
creates a calendar filled with holidays created from one of the
existing hol_*()
holidays (such as hol_christmas()
) or from a manually
generated holiday created using rholiday()
. That calendar can then be used
as an rschedule with any other alma_*()
function (like alma_in()
), or
with one of the specialized calendar functions, like cal_match()
or
cal_events()
.
rcalendar(...)
rcalendar(...)
... |
One or more holidays created from |
on_earth_day <- yearly() %>% recur_on_month_of_year("April") %>% recur_on_day_of_month(22) %>% rholiday("Earth Day") cal <- rcalendar( hol_christmas(), on_earth_day, hol_us_independence_day() ) cal cal_events(cal, year = 2020:2022) # Lookup holiday name based on date, if it exists cal_match(c("2021-12-25", "2021-12-26"), cal) # Find next holiday alma_next("2021-12-26", cal)
on_earth_day <- yearly() %>% recur_on_month_of_year("April") %>% recur_on_day_of_month(22) %>% rholiday("Earth Day") cal <- rcalendar( hol_christmas(), on_earth_day, hol_us_independence_day() ) cal cal_events(cal, year = 2020:2022) # Lookup holiday name based on date, if it exists cal_match(c("2021-12-25", "2021-12-26"), cal) # Find next holiday alma_next("2021-12-26", cal)
rcustom()
creates an rschedule from manually defined event dates. This can
be useful when combined with runion()
and rsetdiff()
if you have a set of
fixed event dates to forcibly include or exclude from an rschedule.
rcustom(events)
rcustom(events)
events |
A vector of event dates. |
A custom rschedule.
include <- rcustom("2019-07-05") exclude <- rcustom("2019-07-04") independence_day <- yearly() %>% recur_on_month_of_year("July") %>% recur_on_day_of_month(4) # Remove forcibly excluded day independence_day <- rsetdiff(independence_day, exclude) # Add forcibly included day independence_day <- runion(independence_day, include) alma_search("2018-01-01", "2020-12-31", independence_day)
include <- rcustom("2019-07-05") exclude <- rcustom("2019-07-04") independence_day <- yearly() %>% recur_on_month_of_year("July") %>% recur_on_day_of_month(4) # Remove forcibly excluded day independence_day <- rsetdiff(independence_day, exclude) # Add forcibly included day independence_day <- runion(independence_day, include) alma_search("2018-01-01", "2020-12-31", independence_day)
recur_for_count()
controls the total number of events in the recurrence
set.
recur_for_count(x, n)
recur_for_count(x, n)
x |
A recurrence rule. |
n |
The number of times to recur for. |
Remember that the number of times the occurrence has occurred is counted
from the since
date and is limited by the until
date! Adjust them as
necessary to get your desired results.
An updated rrule.
# Using the default `since` date daily_since_epoch_for_5 <- daily() %>% recur_for_count(5) alma_search("1969-12-31", "1970-01-25", daily_since_epoch_for_5) # Changing the `since` date daily_since_2019_for_5 <- daily(since = "2019-01-01") %>% recur_for_count(5) alma_search("2018-12-31", "2019-01-25", daily_since_2019_for_5) # In the case of "impossible" dates, such as 2019-02-31 and 2019-04-31 in the # example below, they are not added to the total count. Only true event # dates are counted. on_31_for_5 <- monthly(since = "2019-01-01") %>% recur_on_day_of_month(31) %>% recur_for_count(5) alma_search("2019-01-01", "2020-01-01", on_31_for_5)
# Using the default `since` date daily_since_epoch_for_5 <- daily() %>% recur_for_count(5) alma_search("1969-12-31", "1970-01-25", daily_since_epoch_for_5) # Changing the `since` date daily_since_2019_for_5 <- daily(since = "2019-01-01") %>% recur_for_count(5) alma_search("2018-12-31", "2019-01-25", daily_since_2019_for_5) # In the case of "impossible" dates, such as 2019-02-31 and 2019-04-31 in the # example below, they are not added to the total count. Only true event # dates are counted. on_31_for_5 <- monthly(since = "2019-01-01") %>% recur_on_day_of_month(31) %>% recur_for_count(5) alma_search("2019-01-01", "2020-01-01", on_31_for_5)
recur_on_day_of_month()
recurs on a specific day of the month.
recur_on_day_of_month(x, day)
recur_on_day_of_month(x, day)
x |
A recurrence rule. |
day |
The days of the month on which to recur. Negative values are allowed,
which specify |
If the day of the month doesn't exist for that particular month, then it
is ignored. For example, if recur_on_day_of_month(30)
is set, then it will
never generate an event in February.
An updated rrule.
# When used with a yearly or monthly frequency, `recur_on_day_of_month()` # expands the number of days in the event set. on_yearly <- yearly() on_yearly_day_of_month_1_to_2 <- on_yearly %>% recur_on_day_of_month(1:2) start <- "1999-01-01" end <- "2000-06-30" alma_search(start, end, on_yearly) alma_search(start, end, on_yearly_day_of_month_1_to_2) # When used with a daily frequency, `recur_on_day_of_month()` limits the # number of days in the event set. on_daily <- daily() on_daily_day_of_month_1_to_2 <- on_daily %>% recur_on_day_of_month(1:2) length(alma_search(start, end, on_daily)) length(alma_search(start, end, on_daily_day_of_month_1_to_2)) # Using a negative value is a powerful way to look back from the end of the # month. This is particularly useful because months don't have the same # number of days. on_last_of_month <- monthly() %>% recur_on_day_of_month(-1) alma_search(start, end, on_last_of_month) # If you want particular days of the week at the end of the month, you # could use something like this, which checks if the end of the month # is also a Friday. on_last_of_month_that_is_also_friday <- on_last_of_month %>% recur_on_day_of_week("Friday") alma_search(start, end, on_last_of_month_that_is_also_friday) # But you probably wanted this, which takes the last friday of the month, # on whatever day that lands on on_last_friday_of_month <- monthly() %>% recur_on_day_of_week("Friday", nth = -1) alma_search(start, end, on_last_friday_of_month)
# When used with a yearly or monthly frequency, `recur_on_day_of_month()` # expands the number of days in the event set. on_yearly <- yearly() on_yearly_day_of_month_1_to_2 <- on_yearly %>% recur_on_day_of_month(1:2) start <- "1999-01-01" end <- "2000-06-30" alma_search(start, end, on_yearly) alma_search(start, end, on_yearly_day_of_month_1_to_2) # When used with a daily frequency, `recur_on_day_of_month()` limits the # number of days in the event set. on_daily <- daily() on_daily_day_of_month_1_to_2 <- on_daily %>% recur_on_day_of_month(1:2) length(alma_search(start, end, on_daily)) length(alma_search(start, end, on_daily_day_of_month_1_to_2)) # Using a negative value is a powerful way to look back from the end of the # month. This is particularly useful because months don't have the same # number of days. on_last_of_month <- monthly() %>% recur_on_day_of_month(-1) alma_search(start, end, on_last_of_month) # If you want particular days of the week at the end of the month, you # could use something like this, which checks if the end of the month # is also a Friday. on_last_of_month_that_is_also_friday <- on_last_of_month %>% recur_on_day_of_week("Friday") alma_search(start, end, on_last_of_month_that_is_also_friday) # But you probably wanted this, which takes the last friday of the month, # on whatever day that lands on on_last_friday_of_month <- monthly() %>% recur_on_day_of_week("Friday", nth = -1) alma_search(start, end, on_last_friday_of_month)
recur_on_day_of_week()
recurs on a specific day of the week.
recur_on_weekends()
and recur_on_weekdays()
are helpers for
recurring on weekends and weekdays.
recur_on_day_of_week(x, day, ..., nth = NULL) recur_on_weekdays(x) recur_on_weekends(x)
recur_on_day_of_week(x, day, ..., nth = NULL) recur_on_weekdays(x) recur_on_weekends(x)
x |
A recurrence rule. |
day |
Days of the week to recur on. Integer values must be from |
... |
These dots are for future extensions and must be empty. |
nth |
Limit to the n-th occurrence of the |
Multiple week day values are allowed, and nth
will be applied to
all of them. If you want to apply different nth
values to different
days of the week, call recur_on_day_of_week()
twice with different day
values.
It is particularly important to pay attention to the since
date when using
weekly rules. The day of the week to use comes from the since
date, which,
by default, is a Monday (1900-01-01
). See almanac_since()
for more
information.
An updated rrule.
# Using default `since` (1900-01-01, a Monday) on_weekly_mondays <- weekly() start <- "1999-01-01" # <- a Friday end <- "1999-03-01" # This finds the first Monday, and then continues from there alma_search(start, end, on_weekly_mondays) # We start counting from a Friday here on_weekly_fridays <- weekly(since = start) alma_search(start, end, on_weekly_fridays) # Alternatively, we could use `recur_on_day_of_week()` and force a recurrence # rule on Friday on_forced_friday <- on_weekly_mondays %>% recur_on_day_of_week("Friday") alma_search(start, end, on_forced_friday) # At monthly frequencies, you can use n-th values to look for particular # week day events on_first_friday_in_month <- monthly() %>% recur_on_day_of_week("Fri", nth = 1) alma_search(start, end, on_first_friday_in_month) # Negative values let you look from the back on_last_friday_in_month <- monthly() %>% recur_on_day_of_week("Fri", nth = -1) alma_search(start, end, on_last_friday_in_month) # At yearly frequencies, this looks for the first sunday of the year on_first_sunday_in_year <- yearly() %>% recur_on_day_of_week("Sunday", nth = 1) alma_search(start, end, on_first_sunday_in_year) # Last week day of the month last_weekday_of_month <- monthly() %>% # Last occurrence of each weekday in the month recur_on_day_of_week(c("Mon", "Tue", "Wed", "Thu", "Fri"), nth = -1) %>% # Now choose the last one of those in each month recur_on_position(-1) alma_search(start, end, last_weekday_of_month)
# Using default `since` (1900-01-01, a Monday) on_weekly_mondays <- weekly() start <- "1999-01-01" # <- a Friday end <- "1999-03-01" # This finds the first Monday, and then continues from there alma_search(start, end, on_weekly_mondays) # We start counting from a Friday here on_weekly_fridays <- weekly(since = start) alma_search(start, end, on_weekly_fridays) # Alternatively, we could use `recur_on_day_of_week()` and force a recurrence # rule on Friday on_forced_friday <- on_weekly_mondays %>% recur_on_day_of_week("Friday") alma_search(start, end, on_forced_friday) # At monthly frequencies, you can use n-th values to look for particular # week day events on_first_friday_in_month <- monthly() %>% recur_on_day_of_week("Fri", nth = 1) alma_search(start, end, on_first_friday_in_month) # Negative values let you look from the back on_last_friday_in_month <- monthly() %>% recur_on_day_of_week("Fri", nth = -1) alma_search(start, end, on_last_friday_in_month) # At yearly frequencies, this looks for the first sunday of the year on_first_sunday_in_year <- yearly() %>% recur_on_day_of_week("Sunday", nth = 1) alma_search(start, end, on_first_sunday_in_year) # Last week day of the month last_weekday_of_month <- monthly() %>% # Last occurrence of each weekday in the month recur_on_day_of_week(c("Mon", "Tue", "Wed", "Thu", "Fri"), nth = -1) %>% # Now choose the last one of those in each month recur_on_position(-1) alma_search(start, end, last_weekday_of_month)
recur_on_day_of_year()
recurs on a specific day of the year.
recur_on_day_of_year(x, day)
recur_on_day_of_year(x, day)
x |
A recurrence rule. |
day |
Days of the year to recur on. Values must be from |
An updated rrule.
library(lubridate, warn.conflicts = FALSE) on_5th_day_of_year <- yearly() %>% recur_on_day_of_year(5) alma_search("1999-01-01", "2000-12-31", on_5th_day_of_year) # Notice that if you use a `since` date that has a day of the year # after the specified one, it rolls to the next year on_5th_day_of_year2 <- yearly(since = "1999-01-06") %>% recur_on_day_of_year(5) alma_search("1999-01-01", "2000-12-31", on_5th_day_of_year2) # Negative values select from the back, which is useful in leap years leap_year(as.Date("2000-01-01")) last_day_of_year <- yearly() %>% recur_on_day_of_year(-1) last_day_of_year_bad <- yearly() %>% recur_on_day_of_year(365) alma_search("1999-01-01", "2000-12-31", last_day_of_year) alma_search("1999-01-01", "2000-12-31", last_day_of_year_bad)
library(lubridate, warn.conflicts = FALSE) on_5th_day_of_year <- yearly() %>% recur_on_day_of_year(5) alma_search("1999-01-01", "2000-12-31", on_5th_day_of_year) # Notice that if you use a `since` date that has a day of the year # after the specified one, it rolls to the next year on_5th_day_of_year2 <- yearly(since = "1999-01-06") %>% recur_on_day_of_year(5) alma_search("1999-01-01", "2000-12-31", on_5th_day_of_year2) # Negative values select from the back, which is useful in leap years leap_year(as.Date("2000-01-01")) last_day_of_year <- yearly() %>% recur_on_day_of_year(-1) last_day_of_year_bad <- yearly() %>% recur_on_day_of_year(365) alma_search("1999-01-01", "2000-12-31", last_day_of_year) alma_search("1999-01-01", "2000-12-31", last_day_of_year_bad)
recur_on_easter()
is a special helper to recur on Easter. Easter is
particularly difficult to construct a recurrence rule for.
recur_on_easter(x, offset = NULL)
recur_on_easter(x, offset = NULL)
x |
A recurrence rule. |
offset |
Deprecated in favor of using An offset in terms of a number of days on either side of Easter to recur on. This offset must still fall within the same year, otherwise the date will be silently ignored. |
An updated rrule.
on_easter <- yearly() %>% recur_on_easter() # Rather than: if (FALSE) { on_easter_monday <- yearly() %>% recur_on_easter(1) } # Please use: on_easter_monday <- roffset(on_easter, 1) alma_search("1999-01-01", "2001-01-01", on_easter) both <- runion(on_easter, on_easter_monday) alma_search("1999-01-01", "2001-01-01", both)
on_easter <- yearly() %>% recur_on_easter() # Rather than: if (FALSE) { on_easter_monday <- yearly() %>% recur_on_easter(1) } # Please use: on_easter_monday <- roffset(on_easter, 1) alma_search("1999-01-01", "2001-01-01", on_easter) both <- runion(on_easter, on_easter_monday) alma_search("1999-01-01", "2001-01-01", both)
recur_on_interval()
adjusts the interval of the base frequency of the
recurrence rule. For example, a monthly()
rule with an interval of 2 would
become "every other month".
recur_on_interval(x, n)
recur_on_interval(x, n)
x |
A recurrence rule. |
n |
The interval on which to recur. |
An updated rrule.
# The default interval is 1 on_monthly <- monthly(since = "1999-01-01") alma_search("1999-01-01", "1999-06-01", on_monthly) # Adjust to every other month on_every_other_month <- on_monthly %>% recur_on_interval(2) alma_search("1999-01-01", "1999-06-01", on_every_other_month) # Note that the frequency is limited to "every other month", but you # can still have multiple events inside a single month on_every_other_month_on_day_25_or_26 <- on_every_other_month %>% recur_on_day_of_month(25:26) alma_search("1999-01-01", "1999-06-01", on_every_other_month_on_day_25_or_26)
# The default interval is 1 on_monthly <- monthly(since = "1999-01-01") alma_search("1999-01-01", "1999-06-01", on_monthly) # Adjust to every other month on_every_other_month <- on_monthly %>% recur_on_interval(2) alma_search("1999-01-01", "1999-06-01", on_every_other_month) # Note that the frequency is limited to "every other month", but you # can still have multiple events inside a single month on_every_other_month_on_day_25_or_26 <- on_every_other_month %>% recur_on_day_of_month(25:26) alma_search("1999-01-01", "1999-06-01", on_every_other_month_on_day_25_or_26)
recur_on_month_of_year()
recurs on a specific month of the year.
recur_on_month_of_year(x, month)
recur_on_month_of_year(x, month)
x |
A recurrence rule. |
month |
Months of the year to mark as events. Integer values must be between
|
An updated rrule.
# There is a big difference between adding this rule to a `yearly()` # or `monthly()` frequency, and a `daily()` frequency. # Limit from every day to every day in February on_feb_daily <- daily() %>% recur_on_month_of_year("Feb") # Limit from 1 day per month to 1 day in February on_feb_monthly <- monthly() %>% recur_on_month_of_year("Feb") start <- "1999-01-01" end <- "2001-01-01" alma_search(start, end, on_feb_daily) alma_search(start, end, on_feb_monthly)
# There is a big difference between adding this rule to a `yearly()` # or `monthly()` frequency, and a `daily()` frequency. # Limit from every day to every day in February on_feb_daily <- daily() %>% recur_on_month_of_year("Feb") # Limit from 1 day per month to 1 day in February on_feb_monthly <- monthly() %>% recur_on_month_of_year("Feb") start <- "1999-01-01" end <- "2001-01-01" alma_search(start, end, on_feb_daily) alma_search(start, end, on_feb_monthly)
recur_on_position()
let's you have fine tuned control over which element
of the set to select within the base frequency.
recur_on_position(x, n)
recur_on_position(x, n)
x |
A recurrence rule. |
n |
The positions to select within an intrafrequency set. Negative numbers select from the end of the set. |
An updated rrule.
library(lubridate, warn.conflicts = FALSE) start <- "1999-01-01" end <- "1999-05-01" # You might want the last day of the month that is either a # Sunday or a Monday, but you don't want to return both. # This would return both: on_last_monday_and_sunday <- monthly() %>% recur_on_day_of_week(c("Monday", "Sunday"), nth = -1) alma_search(start, end, on_last_monday_and_sunday) # To return just the last one, you would select the last value in # the set, which is computed on a per month basis on_very_last_monday_or_sunday <- on_last_monday_and_sunday %>% recur_on_position(-1) alma_search(start, end, on_very_last_monday_or_sunday) wday(alma_search(start, end, on_very_last_monday_or_sunday), label = TRUE)
library(lubridate, warn.conflicts = FALSE) start <- "1999-01-01" end <- "1999-05-01" # You might want the last day of the month that is either a # Sunday or a Monday, but you don't want to return both. # This would return both: on_last_monday_and_sunday <- monthly() %>% recur_on_day_of_week(c("Monday", "Sunday"), nth = -1) alma_search(start, end, on_last_monday_and_sunday) # To return just the last one, you would select the last value in # the set, which is computed on a per month basis on_very_last_monday_or_sunday <- on_last_monday_and_sunday %>% recur_on_position(-1) alma_search(start, end, on_very_last_monday_or_sunday) wday(alma_search(start, end, on_very_last_monday_or_sunday), label = TRUE)
recur_on_week_of_year()
recurs on a specific week of the year.
recur_on_week_of_year(x, week)
recur_on_week_of_year(x, week)
x |
A recurrence rule. |
week |
Weeks of the year to recur on. Integer values must be between
|
Weekly rules are implemented according to the ISO-8601 standard. This
requires that the first week of a year is the first one containing at least
4 days of the new year. Additionally, the week will start on the week day
specified by recur_with_week_start()
, which defaults to Monday.
An updated rrule.
# Weekly rules are a bit tricky because they are implemented to comply # with ISO-8601 standards, which require that the first week of the year # is when there are at least 4 days in that year, and the week starts on # the week day specified by `recur_with_week_start()` (Monday by default). on_first_week <- yearly() %>% recur_on_week_of_year(1) # In 2017: # - Look at dates 1-4 # - 2017-01-02 is a Monday, so start the first week here alma_search("2017-01-01", "2017-01-25", on_first_week) # In 2015: # - Look at dates 1-4 # - None of these are Monday, so the start of the week is # in the previous year # - Look at 2014 and find the last Monday, 2014-12-29. This is the start of # the first week in 2015. alma_search("2014-12-25", "2015-01-25", on_first_week) # Say we want the start of the week to be Sunday instead of Monday! # In 2015: # - Look at dates 1-4 # - 2015-01-04 is a Sunday, so start the first week here on_first_week_sun <- yearly() %>% recur_on_week_of_year(1) %>% recur_with_week_start("Sunday") alma_search("2014-12-25", "2015-01-25", on_first_week_sun)
# Weekly rules are a bit tricky because they are implemented to comply # with ISO-8601 standards, which require that the first week of the year # is when there are at least 4 days in that year, and the week starts on # the week day specified by `recur_with_week_start()` (Monday by default). on_first_week <- yearly() %>% recur_on_week_of_year(1) # In 2017: # - Look at dates 1-4 # - 2017-01-02 is a Monday, so start the first week here alma_search("2017-01-01", "2017-01-25", on_first_week) # In 2015: # - Look at dates 1-4 # - None of these are Monday, so the start of the week is # in the previous year # - Look at 2014 and find the last Monday, 2014-12-29. This is the start of # the first week in 2015. alma_search("2014-12-25", "2015-01-25", on_first_week) # Say we want the start of the week to be Sunday instead of Monday! # In 2015: # - Look at dates 1-4 # - 2015-01-04 is a Sunday, so start the first week here on_first_week_sun <- yearly() %>% recur_on_week_of_year(1) %>% recur_with_week_start("Sunday") alma_search("2014-12-25", "2015-01-25", on_first_week_sun)
recur_with_week_start()
controls the week day that represents the start of
the week. This is important for rules that use recur_on_week_of_year()
.
The default day of the week to start on is Monday.
recur_with_week_start(x, day)
recur_with_week_start(x, day)
x |
A recurrence rule. |
day |
Day of the week to start the week on. Must be an integer value in
|
An updated rrule.
# Weekly rules are a bit tricky because they are implemented to comply # with ISO-8601 standards, which require that the first week of the year # is when there are at least 4 days in that year, and the week starts on # the week day specified by `recur_with_week_start()` (Monday by default). on_first_week <- yearly() %>% recur_on_week_of_year(1) # In 2017: # - Look at dates 1-4 # - 2017-01-02 is a Monday, so start the first week here alma_search("2017-01-01", "2017-01-25", on_first_week) # In 2015: # - Look at dates 1-4 # - None of these are Monday, so the start of the week is # in the previous year # - Look at 2014 and find the last Monday, 2014-12-29. This is the start of # the first week in 2015. alma_search("2014-12-25", "2015-01-25", on_first_week) # Say we want the start of the week to be Sunday instead of Monday! # In 2015: # - Look at dates 1-4 # - 2015-01-04 is a Sunday, so start the first week here on_first_week_sun <- yearly() %>% recur_on_week_of_year(1) %>% recur_with_week_start("Sunday") alma_search("2014-12-25", "2015-01-25", on_first_week_sun)
# Weekly rules are a bit tricky because they are implemented to comply # with ISO-8601 standards, which require that the first week of the year # is when there are at least 4 days in that year, and the week starts on # the week day specified by `recur_with_week_start()` (Monday by default). on_first_week <- yearly() %>% recur_on_week_of_year(1) # In 2017: # - Look at dates 1-4 # - 2017-01-02 is a Monday, so start the first week here alma_search("2017-01-01", "2017-01-25", on_first_week) # In 2015: # - Look at dates 1-4 # - None of these are Monday, so the start of the week is # in the previous year # - Look at 2014 and find the last Monday, 2014-12-29. This is the start of # the first week in 2015. alma_search("2014-12-25", "2015-01-25", on_first_week) # Say we want the start of the week to be Sunday instead of Monday! # In 2015: # - Look at dates 1-4 # - 2015-01-04 is a Sunday, so start the first week here on_first_week_sun <- yearly() %>% recur_on_week_of_year(1) %>% recur_with_week_start("Sunday") alma_search("2014-12-25", "2015-01-25", on_first_week_sun)
rholiday()
is used to create custom holidays. It wraps up a holiday name
and its corresponding rschedule
into a holiday object with special
properties.
Holiday objects can be tweaked with hol_rename()
, hol_observe()
, and
hol_offset()
, and they can be added to a calendar with rcalendar()
.
rholiday(rschedule, name)
rholiday(rschedule, name)
rschedule |
The recurrence schedule that determines when the holiday occurs. |
name |
The name of the holiday. This serves as a unique identifier when adding
multiple holidays to an |
on_christmas <- yearly() %>% recur_on_month_of_year("Dec") %>% recur_on_day_of_month(25) # Bundle a holiday name with its recurrence schedule to create a holiday rholiday(on_christmas, "Christmas") # This is how the built in holiday objects are created hol_christmas()
on_christmas <- yearly() %>% recur_on_month_of_year("Dec") %>% recur_on_day_of_month(25) # Bundle a holiday name with its recurrence schedule to create a holiday rholiday(on_christmas, "Christmas") # This is how the built in holiday objects are created hol_christmas()
roffset()
creates a new rschedule with events that are offset from an
existing rschedule by a certain amount. This can be useful when generating
relative events like "the day after Christmas."
roffset(rschedule, by)
roffset(rschedule, by)
rschedule |
An rschedule to offset. |
by |
A single integer to offset by. |
An offset rschedule.
on_christmas <- yearly() %>% recur_on_month_of_year("Dec") %>% recur_on_day_of_month(25) on_day_after_christmas <- roffset(on_christmas, by = 1) alma_search("2018-01-01", "2023-01-01", on_day_after_christmas) # Now what if you want the observed holiday representing the day after # Christmas? on_weekends <- weekly() %>% recur_on_weekends() # Adjust Christmas to the nearest weekday on_christmas <- radjusted(on_christmas, on_weekends, adj_nearest) # Offset by 1 and then adjust that to the following weekday. # We never adjust backwards because that can coincide with the observed day # for Christmas. on_day_after_christmas <- on_christmas %>% roffset(by = 1) %>% radjusted(on_weekends, adj_following) # Note that: # - A Christmas on Friday the 24th resulted in a day after Christmas of # Monday the 27th # - A Christmas on Monday the 26th resulted in a day after Christmas of # Tuesday the 27th christmas <- alma_search("2018-01-01", "2023-01-01", on_christmas) day_after_christmas <- alma_search("2018-01-01", "2023-01-01", on_day_after_christmas) lubridate::wday(christmas, label = TRUE) lubridate::wday(day_after_christmas, label = TRUE)
on_christmas <- yearly() %>% recur_on_month_of_year("Dec") %>% recur_on_day_of_month(25) on_day_after_christmas <- roffset(on_christmas, by = 1) alma_search("2018-01-01", "2023-01-01", on_day_after_christmas) # Now what if you want the observed holiday representing the day after # Christmas? on_weekends <- weekly() %>% recur_on_weekends() # Adjust Christmas to the nearest weekday on_christmas <- radjusted(on_christmas, on_weekends, adj_nearest) # Offset by 1 and then adjust that to the following weekday. # We never adjust backwards because that can coincide with the observed day # for Christmas. on_day_after_christmas <- on_christmas %>% roffset(by = 1) %>% radjusted(on_weekends, adj_following) # Note that: # - A Christmas on Friday the 24th resulted in a day after Christmas of # Monday the 27th # - A Christmas on Monday the 26th resulted in a day after Christmas of # Tuesday the 27th christmas <- alma_search("2018-01-01", "2023-01-01", on_christmas) day_after_christmas <- alma_search("2018-01-01", "2023-01-01", on_day_after_christmas) lubridate::wday(christmas, label = TRUE) lubridate::wday(day_after_christmas, label = TRUE)
These functions allow you to create a recurrence rule with a specified
frequency. They are the base elements for all recurrence rules. To add
to them, use one of the recur_*()
functions.
daily()
Recur on a daily frequency.
weekly()
Recur on a weekly frequency.
monthly()
Recur on a monthly frequency.
yearly()
Recur on a yearly frequency.
daily(since = NULL, until = NULL) weekly(since = NULL, until = NULL) monthly(since = NULL, until = NULL) yearly(since = NULL, until = NULL)
daily(since = NULL, until = NULL) weekly(since = NULL, until = NULL) monthly(since = NULL, until = NULL) yearly(since = NULL, until = NULL)
since |
The lower bound on the event set. Depending on the final recurrence rule, pieces of information from this anchor date might be used to generate a complete recurrence rule. |
until |
The upper bound on the event set. |
By default, since == "1900-01-01"
and until == "2100-01-01"
, which should
capture most use cases well while still being performant. You may need to
adjust these dates if you want events outside this range. See
almanac_since()
and almanac_until()
for more information.
In terms of speed, it is generally more efficient if you adjust the since
and until
date to be closer to the first date in the sequence of dates
that you are working with. For example, if you are working with dates in the
range of 2019 and forward, adjust the since
date to be 2019-01-01
for a
significant speed boost.
As the anchor date, events are often calculated relative to this
date. As an example, a rule of "on Monday, every other week" would use
the since
date to find the first Monday to start the recurrence from.
There is no quarterly()
recurrence frequency, but this can be accomplished
with monthly() %>% recur_on_interval(3)
. The month to start the quarterly
interval from will be pulled from the since
date inside monthly()
. The
default will use a quarterly rule starting in January since the default
since
date is 1900-01-01
. See the examples.
A new empty rrule.
rrule <- monthly() %>% recur_on_day_of_month(25) alma_search("1970-01-01", "1971-01-01", rrule) # Notice that dates before 1900-01-01 are never generated with the defaults! alma_search("1899-01-01", "1901-01-01", rrule) # Adjust the `since` date to get access to these dates rrule_pre_1900 <- monthly(since = "1850-01-01") %>% recur_on_day_of_month(25) alma_search("1899-01-01", "1901-01-01", rrule_pre_1900) # A quarterly recurrence rule can be built from # `monthly()` and `recur_on_interval()` on_first_of_the_quarter <- monthly() %>% recur_on_interval(3) %>% recur_on_day_of_month(1) alma_search("1999-01-01", "2000-04-01", on_first_of_the_quarter) # Alter the starting quarter by altering the `since` date on_first_of_the_quarter_starting_in_feb <- monthly(since = "1998-02-01") %>% recur_on_interval(3) %>% recur_on_day_of_month(1) alma_search( "1999-01-01", "2000-04-01", on_first_of_the_quarter_starting_in_feb )
rrule <- monthly() %>% recur_on_day_of_month(25) alma_search("1970-01-01", "1971-01-01", rrule) # Notice that dates before 1900-01-01 are never generated with the defaults! alma_search("1899-01-01", "1901-01-01", rrule) # Adjust the `since` date to get access to these dates rrule_pre_1900 <- monthly(since = "1850-01-01") %>% recur_on_day_of_month(25) alma_search("1899-01-01", "1901-01-01", rrule_pre_1900) # A quarterly recurrence rule can be built from # `monthly()` and `recur_on_interval()` on_first_of_the_quarter <- monthly() %>% recur_on_interval(3) %>% recur_on_day_of_month(1) alma_search("1999-01-01", "2000-04-01", on_first_of_the_quarter) # Alter the starting quarter by altering the `since` date on_first_of_the_quarter_starting_in_feb <- monthly(since = "1998-02-01") %>% recur_on_interval(3) %>% recur_on_day_of_month(1) alma_search( "1999-01-01", "2000-04-01", on_first_of_the_quarter_starting_in_feb )
Often, a single rrule will be sufficient. However, more complex recurrence objects can be constructed by combining multiple rschedules into a recurrence set.
There are three types of recurrence sets provided in almanac, each of which construct their event sets by performing a set operation on the underlying events of the rschedules in the set.
runion()
takes the union.
rintersect()
takes the intersection.
rsetdiff()
takes the set difference.
runion(...) rintersect(...) rsetdiff(...)
runion(...) rintersect(...) rsetdiff(...)
... |
rschedule objects to add to the set. |
For rsetdiff()
, the event set is created "from left to right" and depends
on the order that the rschedules were added to the set.
A runion, rintersect, or rsetdiff.
since <- "2019-04-01" until <- "2019-05-31" on_weekends <- weekly(since = since, until = until) %>% recur_on_weekends() on_25th <- monthly(since = since, until = until) %>% recur_on_day_of_month(25) # On weekends OR the 25th of the month ru <- runion(on_weekends, on_25th) alma_events(ru) # On weekends AND the 25th of the month ri <- rintersect(on_weekends, on_25th) alma_events(ri) # On weekends AND NOT the 25th of the month rsd1 <- rsetdiff(on_weekends, on_25th) alma_events(rsd1) # On the 25th of the month AND NOT the weekend rsd2 <- rsetdiff(on_25th, on_weekends) alma_events(rsd2)
since <- "2019-04-01" until <- "2019-05-31" on_weekends <- weekly(since = since, until = until) %>% recur_on_weekends() on_25th <- monthly(since = since, until = until) %>% recur_on_day_of_month(25) # On weekends OR the 25th of the month ru <- runion(on_weekends, on_25th) alma_events(ru) # On weekends AND the 25th of the month ri <- rintersect(on_weekends, on_25th) alma_events(ri) # On weekends AND NOT the 25th of the month rsd1 <- rsetdiff(on_weekends, on_25th) alma_events(rsd1) # On the 25th of the month AND NOT the weekend rsd2 <- rsetdiff(on_25th, on_weekends) alma_events(rsd2)
stepper()
returns a function that can be used to add or subtract a
number of days from a Date, "stepping" over events specified by an
rschedule. You supply it the rschedule to step relative to, and then
call the returned function with the number of days to step by.
workdays()
is a convenient stepper for stepping over the weekend.
%s+%
steps forwards.
%s-%
steps backwards.
You must use %s+
and %s-%
to control the stepping. +
and -
will
not work due to limitations in R's S3 dispatch system. Alternatively, you
can call vctrs::vec_arith()
directly, which powers %s+%
with a correct
double dispatch implementation.
stepper(rschedule) x %s+% y x %s-% y workdays(n, since = NULL, until = NULL)
stepper(rschedule) x %s+% y x %s-% y workdays(n, since = NULL, until = NULL)
rschedule |
An rschedule, such as an rrule, runion, rintersect, or rsetdiff. |
x , y
|
Objects to perform step arithmetic on. Typically Dates or steppers. |
n |
The number of days to step. Can be negative to step backwards. |
since |
The lower bound on the event set. Depending on the final recurrence rule, pieces of information from this anchor date might be used to generate a complete recurrence rule. |
until |
The upper bound on the event set. |
Internally, a stepper is just powered by alma_step()
, so feel free to
use that directly.
stepper()
returns a function of 1 argument, n
, that can be used to
step by n
days, relative to the rschedule.
workdays()
return a new stepper object.
%s+%
and %s-%
return a new shifted Date vector.
# A Thursday and Friday x <- as.Date(c("1970-01-01", "1970-01-02")) # Thursday is stepped forward 1 working day to Friday, # and then 1 more working day to Monday. # Friday is stepped forward 1 working day to Monday, # and then 1 more working day to Tuesday x %s+% workdays(2) # --------------------------------------------------------------------------- on_weekends <- weekly() %>% recur_on_weekends() on_christmas <- yearly() %>% recur_on_day_of_month(25) %>% recur_on_month_of_year("Dec") rb <- runion(on_weekends, on_christmas) workday <- stepper(rb) # Friday before Christmas, which was on a Monday friday_before_christmas <- as.Date("2000-12-22") # Steps over the weekend and Christmas to the following Tuesday friday_before_christmas %s+% workday(1) # --------------------------------------------------------------------------- # Christmas in 2005 was on a Sunday, but your company probably "observed" # it on Monday. So when you are on the Friday before Christmas in 2005, # stepping forward 1 working day should go to Tuesday. # We'll adjust the previous rule for Christmas to roll to the nearest # non-weekend day, if it happened to fall on a weekend. on_observed_christmas <- radjusted( on_christmas, adjust_on = on_weekends, adjustment = adj_nearest ) # Note that the "observed" date for Christmas is the 26th alma_search("2005-01-01", "2006-01-01", on_observed_christmas) rb2 <- runion(on_weekends, on_observed_christmas) workday2 <- stepper(rb2) friday_before_christmas_2005 <- as.Date("2005-12-23") # Steps over the weekend and the observed Christmas date # of 2005-12-26 to Tuesday the 27th. friday_before_christmas_2005 %s+% workday2(1)
# A Thursday and Friday x <- as.Date(c("1970-01-01", "1970-01-02")) # Thursday is stepped forward 1 working day to Friday, # and then 1 more working day to Monday. # Friday is stepped forward 1 working day to Monday, # and then 1 more working day to Tuesday x %s+% workdays(2) # --------------------------------------------------------------------------- on_weekends <- weekly() %>% recur_on_weekends() on_christmas <- yearly() %>% recur_on_day_of_month(25) %>% recur_on_month_of_year("Dec") rb <- runion(on_weekends, on_christmas) workday <- stepper(rb) # Friday before Christmas, which was on a Monday friday_before_christmas <- as.Date("2000-12-22") # Steps over the weekend and Christmas to the following Tuesday friday_before_christmas %s+% workday(1) # --------------------------------------------------------------------------- # Christmas in 2005 was on a Sunday, but your company probably "observed" # it on Monday. So when you are on the Friday before Christmas in 2005, # stepping forward 1 working day should go to Tuesday. # We'll adjust the previous rule for Christmas to roll to the nearest # non-weekend day, if it happened to fall on a weekend. on_observed_christmas <- radjusted( on_christmas, adjust_on = on_weekends, adjustment = adj_nearest ) # Note that the "observed" date for Christmas is the 26th alma_search("2005-01-01", "2006-01-01", on_observed_christmas) rb2 <- runion(on_weekends, on_observed_christmas) workday2 <- stepper(rb2) friday_before_christmas_2005 <- as.Date("2005-12-23") # Steps over the weekend and the observed Christmas date # of 2005-12-26 to Tuesday the 27th. friday_before_christmas_2005 %s+% workday2(1)