The Calendar class
Calendar is an S7 class with four
properties:
| Property | Type | Purpose |
|---|---|---|
@leaves |
data.frame |
One row per slice (the terminal cells). |
@timeframes |
character |
Ordered timeframe names, coarsest first. |
@levels |
list of character
|
Ordered label vocabulary per timeframe. |
@meta |
list |
Free-form metadata (name, desc, …). |
cal <- calendar("q4_h24", desc = "Quarter × hour-of-day")
S7::S7_class(cal)
#> <timescales::Calendar> class
#> @ parent : <S7_object>
#> @ constructor: function(leaves, timeframes, levels, meta) {...}
#> @ validator : function(self) {...}
#> @ properties :
#> $ leaves : S3<data.frame>
#> $ timeframes: <character>
#> $ levels : <list>
#> $ meta : <list>
@leaves
A plain data.frame with three required columns plus one
column per timeframe:
| Column | Type | Required | Notes |
|---|---|---|---|
slice |
character |
yes | Unique non-empty slice ID. |
share |
numeric |
yes |
> 0; sums to meta$year_fraction. |
weight |
numeric |
yes |
>= 0; default share * 8760. |
| one per timeframe | character |
yes | Label at that timeframe. |
str(cal@leaves)
#> 'data.frame': 96 obs. of 5 variables:
#> $ QUARTER: chr "Q1" "Q2" "Q3" "Q4" ...
#> $ HOUR : chr "h00" "h00" "h00" "h00" ...
#> $ share : num 0.0103 0.0104 0.0105 0.0105 0.0103 ...
#> $ weight : num 90 91 92 92 90 91 92 92 90 91 ...
#> $ slice : chr "Q1_h00" "Q2_h00" "Q3_h00" "Q4_h00" ...slice is auto-generated by joining the per-timeframe
labels with an internal separator when not supplied. Users never need to
parse it.
@timeframes
The hierarchy, coarsest first. This sets the column order of
@leaves and the join order during recasting.
cal@timeframes
#> [1] "QUARTER" "HOUR"
@levels
Per-timeframe label vocabulary in the order the calendar uses. This fixes deterministic ordering for downstream sorting and plotting.
cal@levels
#> $QUARTER
#> [1] "Q1" "Q2" "Q3" "Q4"
#>
#> $HOUR
#> [1] "h00" "h01" "h02" "h03" "h04" "h05" "h06" "h07" "h08" "h09" "h10" "h11"
#> [13] "h12" "h13" "h14" "h15" "h16" "h17" "h18" "h19" "h20" "h21" "h22" "h23"
@meta
A free-form named list. Recognised keys today:
| Key | Type | Default | Used by |
|---|---|---|---|
name |
character | "" |
print() |
desc |
character | "" |
print() |
year_start |
list | list(month=1L, day=1L) |
instant_to_slice(), expand_calendar()
|
utc_offset_minutes |
integer | 0L |
instant_to_slice() (planned) |
year_fraction |
numeric | 1 |
validator (sum(share)) |
year_qualified |
logical | FALSE |
set by calendar("y_...")
|
Anything else you put in @meta is preserved
untouched.
names(cal@meta)
#> [1] "name" "desc" "year_start"
#> [4] "utc_offset_minutes" "year_fraction"
cal@meta$year_start
#> $month
#> [1] 1
#>
#> $day
#> [1] 1The token registry
Tokens live in an internal environment, populated at load time with
the built-in set and extensible at run time with
register_token().
list_tokens()
#> [1] "d360" "d364" "d365" "d366" "h168" "h24" "m12" "m12a" "min60"
#> [10] "q4" "w52" "w53" "wd7"A token is a list with two fields:
-
timeframe— one ofCORE_TIMEFRAMES, -
expand— a zero-argument function returningdata.frame(label, share)withsharesumming to 1.
m12 <- get_token("m12")
m12$timeframe
#> [1] "MONTH"
head(m12$expand())
#> label share
#> 1 m01 0.08493151
#> 2 m02 0.07671233
#> 3 m03 0.08493151
#> 4 m04 0.08219178
#> 5 m05 0.08493151
#> 6 m06 0.08219178Custom tokens behave identically to built-ins:
register_token("d4q", "YDAY", function() {
data.frame(label = c("Q1d", "Q2d", "Q3d", "Q4d"),
share = c(90, 91, 92, 92) / 365)
})
calendar_build("d4q", "h24")
#> <timescales::Calendar>
#> @ leaves :'data.frame': 96 obs. of 5 variables:
#> .. $ YDAY : chr "Q1d" "Q2d" "Q3d" "Q4d" ...
#> .. $ HOUR : chr "h00" "h00" "h00" "h00" ...
#> .. $ share : num 0.0103 0.0104 0.0105 0.0105 0.0103 ...
#> .. $ weight: num 90 91 92 92 90 91 92 92 90 91 ...
#> .. $ slice : chr "Q1d_h00" "Q2d_h00" "Q3d_h00" "Q4d_h00" ...
#> @ timeframes: chr [1:2] "YDAY" "HOUR"
#> @ levels :List of 2
#> .. $ YDAY: chr [1:4] "Q1d" "Q2d" "Q3d" "Q4d"
#> .. $ HOUR: chr [1:24] "h00" "h01" "h02" "h03" ...
#> @ meta :List of 5
#> .. $ name : chr "d4q_h24"
#> .. $ desc : chr ""
#> .. $ year_start :List of 2
#> .. ..$ month: int 1
#> .. ..$ day : int 1
#> .. $ utc_offset_minutes: int 0
#> .. $ year_fraction : num 1Construction validity
The validator on Calendar enforces:
- required columns present in
@leaves, -
sliceunique and non-empty, -
share > 0andsum(share) == meta$year_fraction, -
weight >= 0, -
year_startcarries validmonth(1..12) andday(1..31), -
utc_offset_minutesis an integer scalar, - each
levels[[tf]]is set-equal tounique(leaves[[tf]]).
Any violation throws at construction time, so a Calendar
you hold is always internally consistent.
Conversion outputs
recast() returns a data.frame with the same
column layout as its input — slice plus the value columns —
but with the target calendar’s slice IDs.
src <- data.frame(slice = sprintf("m%02d", 1:12),
load = seq(100, 210, length.out = 12))
recast(src, from = calendar("m12"), to = calendar("q4"),
year = 2025, rule = "weighted_mean", by = "day")
#> slice load
#> 1 Q1 110.0000
#> 2 Q2 140.0000
#> 3 Q3 169.8913
#> 4 Q4 200.0000instant_to_slice() is the inverse direction (datetime →
slice ID) and returns a character vector of the same length as the
input, with NA for instants outside the calendar’s
coverage.