Building Deterministic Calendar Grids with betaCalendarsR

betaCalendarsR converts base R dates into explicit calendar positions. The resulting data frames can be used by reports, dashboards, Shiny applications, tests, or renderers without asking the package to draw a particular interface.

library(betaCalendarsR)

January 2027 and week starts

The default grid contains six complete weeks. in_month distinguishes January dates from the leading and trailing cells.

january <- month_grid(2027, 1, week_start = "monday")
stopifnot(nrow(january) == 42L)
january[january$in_month, c("date", "weekday", "iso_week")]
#>          date   weekday iso_week
#> 5  2027-01-01    friday       53
#> 6  2027-01-02  saturday       53
#> 7  2027-01-03    sunday       53
#> 8  2027-01-04    monday        1
#> 9  2027-01-05   tuesday        1
#> 10 2027-01-06 wednesday        1
#> 11 2027-01-07  thursday        1
#> 12 2027-01-08    friday        1
#> 13 2027-01-09  saturday        1
#> 14 2027-01-10    sunday        1
#> 15 2027-01-11    monday        2
#> 16 2027-01-12   tuesday        2
#> 17 2027-01-13 wednesday        2
#> 18 2027-01-14  thursday        2
#> 19 2027-01-15    friday        2
#> 20 2027-01-16  saturday        2
#> 21 2027-01-17    sunday        2
#> 22 2027-01-18    monday        3
#> 23 2027-01-19   tuesday        3
#> 24 2027-01-20 wednesday        3
#> 25 2027-01-21  thursday        3
#> 26 2027-01-22    friday        3
#> 27 2027-01-23  saturday        3
#> 28 2027-01-24    sunday        3
#> 29 2027-01-25    monday        4
#> 30 2027-01-26   tuesday        4
#> 31 2027-01-27 wednesday        4
#> 32 2027-01-28  thursday        4
#> 33 2027-01-29    friday        4
#> 34 2027-01-30  saturday        4
#> 35 2027-01-31    sunday        4

Sunday-first and Saturday-first calendars use the same columns with a different first weekday.

sunday_grid <- month_grid(2027, 1, week_start = "sunday")
saturday_grid <- month_grid(2027, 1, week_start = "saturday")
head(sunday_grid[c("date", "weekday", "row", "column")])
#>         date   weekday row column
#> 1 2026-12-27    sunday   1      1
#> 2 2026-12-28    monday   1      2
#> 3 2026-12-29   tuesday   1      3
#> 4 2026-12-30 wednesday   1      4
#> 5 2026-12-31  thursday   1      5
#> 6 2027-01-01    friday   1      6
head(saturday_grid[c("date", "weekday", "row", "column")])
#>         date   weekday row column
#> 1 2026-12-26  saturday   1      1
#> 2 2026-12-27    sunday   1      2
#> 3 2026-12-28    monday   1      3
#> 4 2026-12-29   tuesday   1      4
#> 5 2026-12-30 wednesday   1      5
#> 6 2026-12-31  thursday   1      6

Fixed and compact layouts; overflow cells

Compact grids use the minimum four, five, or six complete weeks needed for a month. Overflow cells can be hidden while retaining their positions.

compact <- month_grid(2027, 2, fixed_rows = FALSE)
hidden_overflow <- month_grid(2027, 2, overflow = FALSE)
c(nrow(compact), nrow(hidden_overflow), sum(hidden_overflow$visible))
#> [1] 28 42 28

A year and a cross-year range

The combined year table stores the requested month in grid_month even when a cell belongs to an adjacent month.

year <- year_grid(2027, fixed_rows = FALSE)
table(year$grid_month)
#> 
#>  1  2  3  4  5  6  7  8  9 10 11 12 
#> 35 28 35 35 42 35 35 42 35 35 35 35

new_year <- date_range_grid(as.Date("2026-12-30"), as.Date("2027-01-03"))
new_year[c("date", "year", "month", "iso_week", "iso_week_year")]
#>         date year month iso_week iso_week_year
#> 1 2026-12-30 2026    12       53          2026
#> 2 2026-12-31 2026    12       53          2026
#> 3 2027-01-01 2027     1       53          2026
#> 4 2027-01-02 2027     1       53          2026
#> 5 2027-01-03 2027     1       53          2026

Leap days and ISO week-years

Gregorian century rules make 2000 a leap year and 2100 a common year. ISO week years may cross the calendar-year boundary; for example, January 1, 2021 belongs to ISO week 53 of 2020.

calendar_boundaries(2000)$is_leap_year
#> [1] TRUE
calendar_boundaries(2100)$is_leap_year
#> [1] FALSE
date_range_grid(as.Date("2021-01-01"), as.Date("2021-01-03"))[
  c("date", "iso_week", "iso_week_year")
]
#>         date iso_week iso_week_year
#> 1 2021-01-01       53          2020
#> 2 2021-01-02       53          2020
#> 3 2021-01-03       53          2020

Bounded recurrence rules

Every recurrence is evaluated inside an explicit inclusive range. A missing 31st can be skipped or clamped to the month end.

paydays <- recurrence_dates(
  as.Date("2027-01-01"), as.Date("2027-12-31"),
  frequency = "nth_weekday", weekday = "friday", n = 2
)
month_ends <- recurrence_dates(
  as.Date("2027-01-01"), as.Date("2027-03-31"),
  frequency = "monthly", day = 31, missing = "clamp"
)
head(paydays$date)
#> [1] "2027-01-08" "2027-02-12" "2027-03-12" "2027-04-09" "2027-05-14"
#> [6] "2027-06-11"
month_ends$date
#> [1] "2027-01-31" "2027-02-28" "2027-03-31"

The package represents civil calendar dates. It does not convert instants or apply time-zone rules; those remain responsibilities of the application.