Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/davisvaughan/almanac-old2

The Grammar of Schedules
https://github.com/davisvaughan/almanac-old2

Last synced: 11 days ago
JSON representation

The Grammar of Schedules

Awesome Lists containing this project

README

        

---
output: github_document
editor_options:
chunk_output_type: console
---

```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)
```

# almanac

[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental)
[![Travis build status](https://travis-ci.org/DavisVaughan/almanac.svg?branch=master)](https://travis-ci.org/DavisVaughan/almanac)
[![Codecov test coverage](https://codecov.io/gh/DavisVaughan/almanac/branch/master/graph/badge.svg)](https://codecov.io/gh/DavisVaughan/almanac?branch=master)

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.

```{r}
library(magrittr)
library(almanac)
```

## Installation

You can NOT install the released version of almanac from [CRAN](https://CRAN.R-project.org) with:

``` r
# NO! install.packages("almanac")
```

And the development version from [GitHub](https://github.com/) with:

``` r
# install.packages("devtools")
devtools::install_github("DavisVaughan/almanac")
```

## Events

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.

```{r}
on_25th <- on_mday(25)
on_25th
```

To determine if a date falls on an event, use `event_in()`.

```{r}
event_in("2019-01-25", on_25th)

event_in("2019-01-23", on_25th)

event_in("2019-02-25", on_25th)
```

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.

```{r}
event_intersect(on_25th, on_month("Nov"))
```

It's often easier to use the shortcut operators `&`, `|`, and `-`.

```{r}
on_nov_25th <- on_25th & on_month("Nov")

on_nov_25th
```

```{r}
event_in("2019-11-24", on_nov_25th)

event_in("2019-11-25", on_nov_25th)
```

## Schedules

A schedule is a collection of events. You can create them by starting with an empty `schedule()`, and then adding events with `sch_add()`.

```{r}
# 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
```

Check if a date is in a schedule with `sch_in()`.

```{r}
sch_in("2019-01-01", sch)

sch_in("2019-12-25", sch)
```

Generate dates in the schedule with `sch_seq()`.

```{r}
sch_seq("2018-01-01", "2020-12-31", sch)
```

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.

```{r}
x <- as.Date("2019-09-01") + 0:3
x

# Labor day is the 2nd of Sept in 2019
x[sch_in(x, sch)]

# 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))
```

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.

```{r}
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))

# Jump forward 2 days - Lands on the 26th
# No adjustment is required
sch_jump(christmas_eve, jump = days(2), schedule = sch, adjustment = days(1))
```

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.

```{r}
# "2 business days from now"

# Step forward 1 day - Lands on the 25th
# An adjustment of 1 day is made to shift to the 26th
# Step forward 1 day - Lands on the 27th
# No adjustment is required
sch_step("2019-12-24", 2, sch)
```

## Acknowledgements

almanac is a combination of ideas from the date library of [QuantLib](https://github.com/lballabio/QuantLib) with the great package from James Laird-Smith, [gs](https://github.com/jameslairdsmith/gs). Both gs and almanac are based on a [paper written by Martin Fowler](https://martinfowler.com/apsupp/recurring.pdf), 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.