Calendrical Computations Using tind Package
Author: Grzegorz Klima

# Calendrical Computations Using `tind` Package

#### Grzegorz Klima

`tind` package provides an extensive set of functions for calendrical computations. These include: extraction of time index components, determination of properties of time periods (leap years, number of subperiods within a period), computation of dates of moveable observances, and calendar arithmetic. For applications in business and finance, computations involving business days (using different rolling conventions) and computation of accrual factors are provided.

Before proceeding, let us load the package.

    library("tind")

## Current Date and Time

Current date and time can be checked using functions `today()` and `now()`.

    today()

    ## date [tind]
    ## [1] 2026-02-02

    now()

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2026-02-02 20:38:35 CET

By default, the system time zone (as returned by `Sys.timezone()`) is used. However, we can check the current time in other time zones. Let us do it for Tokyo, Warsaw, London, and New York.

    now("Asia/Tokyo")

    ## date-time, time zone: Asia/Tokyo [tind]
    ## [1]  2026-02-03 04:38:35 JST

    today("Asia/Tokyo")

    ## date [tind]
    ## [1] 2026-02-03

    now("Europe/Warsaw")

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2026-02-02 20:38:35 CET

    today("Europe/Warsaw")

    ## date [tind]
    ## [1] 2026-02-02

    now("Europe/London")

    ## date-time, time zone: Europe/London [tind]
    ## [1]  2026-02-02 19:38:35 GMT

    today("Europe/London")

    ## date [tind]
    ## [1] 2026-02-02

    now("America/New_York")

    ## date-time, time zone: America/New_York [tind]
    ## [1]  2026-02-02 14:38:35 EST

    today("America/New_York")

    ## date [tind]
    ## [1] 2026-02-02

## Time Index Components (Year, Month, Day, …)

`tind` package provides a collection of intuitively-named functions for extracting time index components, see examples below.

    (nw <- now())

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2026-02-02 20:38:35 CET

    year(nw)

    ## [1] 2026

    quarter(nw)

    ## [1] 1

    month(nw)

    ## [1] 2

    month(nw, labels = TRUE)

    ## [1] Feb
    ## 12 Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < Sep < ... < Dec

    month(nw, labels = TRUE, abbreviate = FALSE)

    ## [1] February
    ## 12 Levels: January < February < March < April < May < June < ... < December

    week(nw)

    ## [1] 6

    day(nw)

    ## [1] 2

    day_of_week(nw)

    ## [1] 1

    day_of_week(nw, labels = TRUE)

    ## [1] Mon
    ## Levels: Mon < Tue < Wed < Thu < Fri < Sat < Sun

    day_of_week(nw, labels = TRUE, abbreviate = FALSE)

    ## [1] Monday
    ## 7 Levels: Monday < Tuesday < Wednesday < Thursday < Friday < ... < Sunday

    day_of_year(nw)

    ## [1] 33

    hour(nw)

    ## [1] 20

    am(nw)

    ## [1] FALSE

    pm(nw)

    ## [1] TRUE

    minute(nw)

    ## [1] 38

    second(nw)

    ## [1] 35

## Properties of Time Indices

The inherent irregularity of our calendar is well known. Years are divided into leap and non-leap ones, months have different numbers of days (and so do quarters and years), years have varying number of weeks (52 or 53 in ISO 8601). In many time zones Daylight Saving Time is used, which then leads to some days having more or less than 24 hours. The functions shown below allow to easily check properties of time indices at hand.

    (tm <- tind(y = 2023:2024, m = c(10, 2), d = 29, H = 1, tz = "Europe/Warsaw"))

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2023-10-29 01:00 CEST  2024-02-29 01:00 CET

    is.leap_year(tm)

    ## [1] FALSE  TRUE

    days_in_year(tm)

    ## [1] 365 366

    weeks_in_year(tm)

    ## [1] 52 52

    days_in_quarter(tm)

    ## [1] 92 91

    days_in_month(tm)

    ## [1] 31 29

    hours_in_day(tm)

    ## [1] 25 24

    is.dst(tm)

    ## [1]  TRUE FALSE

## Calendar Arithmetic

Time indices can be shifted using `+` and `-` operators and `-` can be used to compute time differences (returning objects of auxiliary `tdiff` class).

    (x <- today())

    ## date [tind]
    ## [1] 2026-02-02

    x + 0:3

    ## date [tind]
    ## [1] 2026-02-02 2026-02-03 2026-02-04 2026-02-05

    x - 0:3

    ## date [tind]
    ## [1] 2026-02-02 2026-02-01 2026-01-31 2026-01-30

    x - 3:0

    ## date [tind]
    ## [1] 2026-01-30 2026-01-31 2026-02-01 2026-02-02

    x - as.date("2000-01-01")

    ## time difference, unit: day [tdiff]
    ## [1]  9529d (~26.1y)

Operators `%+y%`, `%-y%`, `%+m%`, `%-m%`, `%+d%`, `%-d%`, etc. can be used to shift time indices by multiples of a given time unit. An alternative is to use time differences constructed by `as.tdiff` method or convenience functions `years`, `qrtrs`, `mnths`, `weeks`, `days`, `hours`, `mins`, and `secs`. (The naming was chosen in order to avoid conflicts with base functions `months` and `quarters`).

    (x <- today())

    ## date [tind]
    ## [1] 2026-02-02

    x %+y% -1:1

    ## date [tind]
    ## [1] 2025-02-02 2026-02-02 2027-02-02

    x + years(-1:1)

    ## date [tind]
    ## [1] 2025-02-02 2026-02-02 2027-02-02

    x %+m% -1:1

    ## date [tind]
    ## [1] 2026-01-02 2026-02-02 2026-03-02

    x + mnths(-1:1)

    ## date [tind]
    ## [1] 2026-01-02 2026-02-02 2026-03-02

    x %+d% -1:1 # same as x + -1:1

    ## date [tind]
    ## [1] 2026-02-01 2026-02-02 2026-02-03

    (x <- now())

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2026-02-02 20:38:35 CET

    x %-h% 3:0

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2026-02-02 17:38:35 CET  2026-02-02 18:38:35 CET  2026-02-02 19:38:35 CET
    ## [4]  2026-02-02 20:38:35 CET

    x - hours(3:0)

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2026-02-02 17:38:35 CET  2026-02-02 18:38:35 CET  2026-02-02 19:38:35 CET
    ## [4]  2026-02-02 20:38:35 CET

    x %-min% 3:0

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2026-02-02 20:35:35 CET  2026-02-02 20:36:35 CET  2026-02-02 20:37:35 CET
    ## [4]  2026-02-02 20:38:35 CET

    x - mins(3:0)

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2026-02-02 20:35:35 CET  2026-02-02 20:36:35 CET  2026-02-02 20:37:35 CET
    ## [4]  2026-02-02 20:38:35 CET

    x %-s% 3:0 # same as x - 3:0

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2026-02-02 20:38:32 CET  2026-02-02 20:38:33 CET  2026-02-02 20:38:34 CET
    ## [4]  2026-02-02 20:38:35 CET

    x - secs(3:0) # same as x - 3:0

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2026-02-02 20:38:32 CET  2026-02-02 20:38:33 CET  2026-02-02 20:38:34 CET
    ## [4]  2026-02-02 20:38:35 CET

## Sequences of Time Indices

`seq` method for `tind` allows to easily construct series of time indices. The following code creates a sequence of months with a step of two months from November 2023 till April 2025:

    seq(as.month("2023-11"), as.month("2025-04"), by = 2)

    ## month [tind]
    ## [1] 2023-11 2024-01 2024-03 2024-05 2024-07 2024-09 2024-11 2025-01 2025-03

Consider the following problem: for a given month we need to construct a sequence of dates in that month. Note that the last day in a month is the day before the first day in the following month. The following piece of code does the job:

    (m <- as.month("2025-03"))

    ## month [tind]
    ## [1] 2025-03

    seq(as.date(m), as.date(m + 1) - 1)

    ## date [tind]
    ##  [1] 2025-03-01 2025-03-02 2025-03-03 2025-03-04 2025-03-05 2025-03-06
    ##  [7] 2025-03-07 2025-03-08 2025-03-09 2025-03-10 2025-03-11 2025-03-12
    ## [13] 2025-03-13 2025-03-14 2025-03-15 2025-03-16 2025-03-17 2025-03-18
    ## [19] 2025-03-19 2025-03-20 2025-03-21 2025-03-22 2025-03-23 2025-03-24
    ## [25] 2025-03-25 2025-03-26 2025-03-27 2025-03-28 2025-03-29 2025-03-30
    ## [31] 2025-03-31

`seq` method for `tind` also accepts different types of time indices and automatically converts them. The two lines below create sequences from the beginning of current quarter till today and from today till the end of the quarter:

    (td <- today())

    ## date [tind]
    ## [1] 2026-02-02

    seq(as.quarter(td), td)

    ## date [tind]
    ##  [1] 2026-01-01 2026-01-02 2026-01-03 2026-01-04 2026-01-05 2026-01-06
    ##  [7] 2026-01-07 2026-01-08 2026-01-09 2026-01-10 2026-01-11 2026-01-12
    ## [13] 2026-01-13 2026-01-14 2026-01-15 2026-01-16 2026-01-17 2026-01-18
    ## [19] 2026-01-19 2026-01-20 2026-01-21 2026-01-22 2026-01-23 2026-01-24
    ## [25] 2026-01-25 2026-01-26 2026-01-27 2026-01-28 2026-01-29 2026-01-30
    ## [31] 2026-01-31 2026-02-01 2026-02-02

    seq(td, as.quarter(td))

    ## date [tind]
    ##  [1] 2026-02-02 2026-02-03 2026-02-04 2026-02-05 2026-02-06 2026-02-07
    ##  [7] 2026-02-08 2026-02-09 2026-02-10 2026-02-11 2026-02-12 2026-02-13
    ## [13] 2026-02-14 2026-02-15 2026-02-16 2026-02-17 2026-02-18 2026-02-19
    ## [19] 2026-02-20 2026-02-21 2026-02-22 2026-02-23 2026-02-24 2026-02-25
    ## [25] 2026-02-26 2026-02-27 2026-02-28 2026-03-01 2026-03-02 2026-03-03
    ## [31] 2026-03-04 2026-03-05 2026-03-06 2026-03-07 2026-03-08 2026-03-09
    ## [37] 2026-03-10 2026-03-11 2026-03-12 2026-03-13 2026-03-14 2026-03-15
    ## [43] 2026-03-16 2026-03-17 2026-03-18 2026-03-19 2026-03-20 2026-03-21
    ## [49] 2026-03-22 2026-03-23 2026-03-24 2026-03-25 2026-03-26 2026-03-27
    ## [55] 2026-03-28 2026-03-29 2026-03-30 2026-03-31

## Rounding Time Indices

`floor_t` and `ceiling_t` round time indices down or up given time unit or multiple of a unit. `round_t` returns the result of one of these two functions, whichever is closer.

    (x <- tind(y = 2025, m = 3, d = 30))

    ## date [tind]
    ## [1] 2025-03-30

    floor_t(x, "w")

    ## date [tind]
    ## [1] 2025-03-24

    ceiling_t(x, "w")

    ## date [tind]
    ## [1] 2025-03-31

    round_t(x, "w")

    ## date [tind]
    ## [1] 2025-03-31

    floor_t(x, "m")

    ## date [tind]
    ## [1] 2025-03-01

    ceiling_t(x, "m")

    ## date [tind]
    ## [1] 2025-04-01

    round_t(x, "m")

    ## date [tind]
    ## [1] 2025-04-01

    floor_t(x, "2m")

    ## date [tind]
    ## [1] 2025-03-01

    ceiling_t(x, "2m")

    ## date [tind]
    ## [1] 2025-05-01

    round_t(x, "2m")

    ## date [tind]
    ## [1] 2025-03-01

    floor_t(x, "q")

    ## date [tind]
    ## [1] 2025-01-01

    ceiling_t(x, "q")

    ## date [tind]
    ## [1] 2025-04-01

    round_t(x, "q")

    ## date [tind]
    ## [1] 2025-04-01

    floor_t(x, "y")

    ## date [tind]
    ## [1] 2025-01-01

    ceiling_t(x, "y")

    ## date [tind]
    ## [1] 2026-01-01

    round_t(x, "y")

    ## date [tind]
    ## [1] 2025-01-01

`trunc_t` truncates time indices changing type when possible.

    (x <- date_time(x, H = "13:13:13.13"))

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2025-03-30 13:13:13.13 CEST

    trunc_t(x, "s")

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2025-03-30 13:13:13 CEST

    trunc_t(x, "min")

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2025-03-30 13:13 CEST

    trunc_t(x, "h")

    ## date-time, time zone: Europe/Warsaw [tind]
    ## [1]  2025-03-30 13:00 CEST

    trunc_t(x, "d")

    ## date [tind]
    ## [1] 2025-03-30

    trunc_t(x, "w")

    ## week [tind]
    ## [1] 2025-W13

    trunc_t(x, "m")

    ## month [tind]
    ## [1] 2025-03

    trunc_t(x, "q")

    ## quarter [tind]
    ## [1] 2025Q1

    trunc_t(x, "y")

    ## year [tind]
    ## [1] 2025

## Determining Dates of Movable Observances / Calendar Events

It is common to define dates of movable holidays or observances as the first, second, third, fourth, or last occurrence of a particular day of week in a given month. In order to determine dates of such observances two functions can be used: `nth_dw_in_month()` and `last_dw_in_month()`. Days of week are numbered 1–7 with 1 denoting Monday and 7 Sunday (as in ISO 8601).

Thanksgiving in the US is observed on the fourth Thursday of November, which in 2019 was on:

    nth_dw_in_month(4, 4, 201911)

    ## date [tind]
    ## [1] 2019-11-28

Black Friday is the day after Thanksgiving:

    nth_dw_in_month(4, 4, 201911) + 1

    ## date [tind]
    ## [1] 2019-11-29

Daylight Saving Time in the EU begins (at least for now) on the last Sunday in March and ends on the last Sunday of October. In 2019 the two dates were:

    last_dw_in_month(7, 201903)

    ## date [tind]
    ## [1] 2019-03-31

    last_dw_in_month(7, 201910)

    ## date [tind]
    ## [1] 2019-10-27

During change to DST, the day is one hour shorter and during change back one hour longer:

    hours_in_day(last_dw_in_month(7, 201903), "Europe/Warsaw")

    ## [1] 23

    hours_in_day(last_dw_in_month(7, 201910), "Europe/Warsaw")

    ## [1] 25

In the Christian world, Easter is a very important feast and other many observances are set relative to it. Easter date in a given year can be determined using `easter()` function.

    easter(2020:2025)

    ## date [tind]
    ## [1] 2020-04-12 2021-04-04 2022-04-17 2023-04-09 2024-03-31 2025-04-20

## Creating and Working with Custom Calendars

The functions shown above can be used to create custom calendar functions, which can later be used for pretty printing calendars or calculations involving business days.

It is expected that a calendar function:

- takes vector of dates
- returns one of the three:
  - a logical vector of the same length as argument indicating business days,
  - a list of two logical vectors of the same length as argument indicating business days and holidays,
  - a list of three logical vectors of the same length as argument indicating business days, holidays, and other observances.

If a calendar function returns a list and second or third logical vector is named, the names are used by function `calendar()` when pretty printing calendar.

Two examples of calendar functions are provided below and can be used as templates for creating calendar functions for any country, area, or specific applications like trading days or shopping days.

### An Example - Business Days and (Federal) Holidays in the US

Public holidays in the US have either fixed dates or are on nth or last day of week in a month.

The calendar function below returns a two-element list (business days and holidays). A programming trick (marked in the code) is used to name holidays.

    calendar_US <- function(dd)
    {
        dd <- as.tind(dd)
        y <- year(dd)
        m <- month(dd)
        d <- day(dd)
        newyear <- (m == 1) & (d == 1)
        martinlking <- (y >= 2000) & (m == 1) & (dd == nth_dw_in_month(3, 1, dd))
        presidentsday <- (m == 2) & (dd == nth_dw_in_month(3, 1, dd))
        memorialday <- (m == 5) & (dd == last_dw_in_month(1, dd))
        juneteenth <- (y >= 2021) & (m == 6) & (d == 19)
        independence <- (m == 7) & (d == 4)
        labor <- (m == 9) & (dd == nth_dw_in_month(1, 1, dd))
        columbus <- (m == 10) & (dd == nth_dw_in_month(2, 1, dd))
        veterans <- (m == 11) & (d == 11)
        thanksgiving <- (m == 11) & (dd == nth_dw_in_month(4, 4, dd))
        christmas <- (m == 12) & (d == 25)
        holiday <- newyear | martinlking | presidentsday |
                   memorialday | juneteenth | independence |
                   labor | columbus | veterans | thanksgiving |
                   christmas
        # holiday names - a programming trick
        # names of holnms should be the same as names of logical vectors above
        names(holiday) <- rep("", length(holiday))
        holnms <- c(newyear = "New Year's Day",
                    martinlking = "Birthday of Martin Luther King, Jr.",
                    presidentsday = "Washington's Birthday",
                    memorialday = "Memorial Day",
                    juneteenth = "Juneteenth National Independence Day",
                    independence = "Independence Day",
                    labor = "Labor Day",
                    columbus = "Columbus Day",
                    veterans = "Veterans Day",
                    thanksgiving = "Thanksgiving Day",
                    christmas = "Christmas Day")
        lapply(names(holnms), function(nm) names(holiday)[get(nm)] <<- holnms[nm])
        # business days
        business <- !holiday & (day_of_week(dd) %in% 1:5)
        return (list(business = business, holiday = holiday))
    }

Now we can use our function to print the calendar. Note that to obtain coloured output you have to have package `crayon` installed.

    calendar(2020, calendar_US)

    ##                                       2020
    ##              Jan                       Feb                       Mar
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   01        1  2  3  4  5   05                 1  2   09                    1
    ##   02  6  7  8  9 10 11 12   06  3  4  5  6  7  8  9   10  2  3  4  5  6  7  8
    ##   03 13 14 15 16 17 18 19   07 10 11 12 13 14 15 16   11  9 10 11 12 13 14 15
    ##   04 20 21 22 23 24 25 26   08 17 18 19 20 21 22 23   12 16 17 18 19 20 21 22
    ##   05 27 28 29 30 31         09 24 25 26 27 28 29      13 23 24 25 26 27 28 29
    ##                                                       14 30 31
    ##              Apr                       May                       Jun
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   14        1  2  3  4  5   18              1  2  3   23  1  2  3  4  5  6  7
    ##   15  6  7  8  9 10 11 12   19  4  5  6  7  8  9 10   24  8  9 10 11 12 13 14
    ##   16 13 14 15 16 17 18 19   20 11 12 13 14 15 16 17   25 15 16 17 18 19 20 21
    ##   17 20 21 22 23 24 25 26   21 18 19 20 21 22 23 24   26 22 23 24 25 26 27 28
    ##   18 27 28 29 30            22 25 26 27 28 29 30 31   27 29 30
    ##              Jul                       Aug                       Sep
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   27        1  2  3  4  5   31                 1  2   36     1  2  3  4  5  6
    ##   28  6  7  8  9 10 11 12   32  3  4  5  6  7  8  9   37  7  8  9 10 11 12 13
    ##   29 13 14 15 16 17 18 19   33 10 11 12 13 14 15 16   38 14 15 16 17 18 19 20
    ##   30 20 21 22 23 24 25 26   34 17 18 19 20 21 22 23   39 21 22 23 24 25 26 27
    ##   31 27 28 29 30 31         35 24 25 26 27 28 29 30   40 28 29 30
    ##                             36 31
    ##              Oct                       Nov                       Dec
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   40           1  2  3  4   44                    1   49     1  2  3  4  5  6
    ##   41  5  6  7  8  9 10 11   45  2  3  4  5  6  7  8   50  7  8  9 10 11 12 13
    ##   42 12 13 14 15 16 17 18   46  9 10 11 12 13 14 15   51 14 15 16 17 18 19 20
    ##   43 19 20 21 22 23 24 25   47 16 17 18 19 20 21 22   52 21 22 23 24 25 26 27
    ##   44 26 27 28 29 30 31      48 23 24 25 26 27 28 29   53 28 29 30 31
    ##                             49 30

    calendar(as.year(today()), calendar_US)

    ##                                       2026
    ##              Jan                       Feb                       Mar
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   01           1  2  3  4   05                    1   09                    1
    ##   02  5  6  7  8  9 10 11   06[ 2] 3  4  5  6  7  8   10  2  3  4  5  6  7  8
    ##   03 12 13 14 15 16 17 18   07  9 10 11 12 13 14 15   11  9 10 11 12 13 14 15
    ##   04 19 20 21 22 23 24 25   08 16 17 18 19 20 21 22   12 16 17 18 19 20 21 22
    ##   05 26 27 28 29 30 31      09 23 24 25 26 27 28      13 23 24 25 26 27 28 29
    ##                                                       14 30 31
    ##              Apr                       May                       Jun
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   14        1  2  3  4  5   18              1  2  3   23  1  2  3  4  5  6  7
    ##   15  6  7  8  9 10 11 12   19  4  5  6  7  8  9 10   24  8  9 10 11 12 13 14
    ##   16 13 14 15 16 17 18 19   20 11 12 13 14 15 16 17   25 15 16 17 18 19 20 21
    ##   17 20 21 22 23 24 25 26   21 18 19 20 21 22 23 24   26 22 23 24 25 26 27 28
    ##   18 27 28 29 30            22 25 26 27 28 29 30 31   27 29 30
    ##              Jul                       Aug                       Sep
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   27        1  2  3  4  5   31                 1  2   36     1  2  3  4  5  6
    ##   28  6  7  8  9 10 11 12   32  3  4  5  6  7  8  9   37  7  8  9 10 11 12 13
    ##   29 13 14 15 16 17 18 19   33 10 11 12 13 14 15 16   38 14 15 16 17 18 19 20
    ##   30 20 21 22 23 24 25 26   34 17 18 19 20 21 22 23   39 21 22 23 24 25 26 27
    ##   31 27 28 29 30 31         35 24 25 26 27 28 29 30   40 28 29 30
    ##                             36 31
    ##              Oct                       Nov                       Dec
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   40           1  2  3  4   44                    1   49     1  2  3  4  5  6
    ##   41  5  6  7  8  9 10 11   45  2  3  4  5  6  7  8   50  7  8  9 10 11 12 13
    ##   42 12 13 14 15 16 17 18   46  9 10 11 12 13 14 15   51 14 15 16 17 18 19 20
    ##   43 19 20 21 22 23 24 25   47 16 17 18 19 20 21 22   52 21 22 23 24 25 26 27
    ##   44 26 27 28 29 30 31      48 23 24 25 26 27 28 29   53 28 29 30 31
    ##                             49 30

    calendar("2020-01", calendar_US)

    ##            Jan 2020
    ##      Mo Tu We Th Fr Sa Su
    ##   01        1  2  3  4  5  |  1 - New Year's Day
    ##   02  6  7  8  9 10 11 12
    ##   03 13 14 15 16 17 18 19
    ##   04 20 21 22 23 24 25 26  | 20 - Birthday of Martin Luther King, Jr.
    ##   05 27 28 29 30 31

    calendar(calendar = calendar_US)

    ##            Jan 2026
    ##      Mo Tu We Th Fr Sa Su
    ##   01           1  2  3  4  |  1 - New Year's Day
    ##   02  5  6  7  8  9 10 11
    ##   03 12 13 14 15 16 17 18
    ##   04 19 20 21 22 23 24 25  | 19 - Birthday of Martin Luther King, Jr.
    ##   05 26 27 28 29 30 31
    ##            Feb 2026
    ##      Mo Tu We Th Fr Sa Su
    ##   05                    1
    ##   06[ 2] 3  4  5  6  7  8
    ##   07  9 10 11 12 13 14 15
    ##   08 16 17 18 19 20 21 22  | 16 - Washington's Birthday
    ##   09 23 24 25 26 27 28

### An example - Business Days, Holidays, and Other Observances in Poland

Public holidays in Poland have either fixed dates or are placed relative to Easter.

The calendar function below returns a three-element list (business days, holidays, and other observances). The same programming trick as before is used to name holidays and other observances.

    calendar_PL <- function(dd)
    {
        dd <- as.tind(dd)
        y <- year(dd)
        m <- month(dd)
        d <- day(dd)
        # public holidays
        newyear <- (m == 1L) & (d == 1L)
        epiphany <- (y >= 2011L) & (m == 1L) & (d == 6L)
        easterd <- easter(dd) == dd
        eastermon <- easter(dd) + 1L == dd
        labour <- (m == 5L) & (d == 1L)
        constitution <- (m == 5L) & (d == 3L)
        pentecost <- easter(dd) + 49L == dd
        corpuschristi <- easter(dd) + 60L == dd
        assumption <- (m == 8L) & (d == 15L)
        allsaints <- (m == 11L) & (d == 1L)
        independence <- (m == 11L) & (d == 11L)
        christmaseve <- (m == 12L) & (d == 24L) & (y >= 2025)
        christmas <- (m == 12L) & (d == 25L)
        christmas2 <- (m == 12L) & (d == 26L)
        holiday <- newyear | epiphany |
                   easterd | eastermon |
                   labour | constitution |
                   pentecost | corpuschristi |
                   assumption |
                   allsaints | independence |
                   christmaseve | christmas | christmas2
        # holiday names
        names(holiday) <- rep("", length(holiday))
        holnms <- c(newyear = "New Year", epiphany = "Epiphany",
                    easterd = "Easter", eastermon = "Easter Monday",
                    labour = "Labour Day", constitution = "Constitution Day",
                    pentecost = "Pentecost", corpuschristi = "Corpus Christi",
                    assumption = "Assumption of Mary",
                    allsaints = "All Saints Day", independence = "Independence Day",
                    christmaseve = "Christmas Eve",
                    christmas = "Christmas", christmas2 = "Christmas (2nd day)")
        lapply(names(holnms), function(nm) names(holiday)[get(nm)] <<- holnms[nm])
        # working/business days
        work <- !holiday & (day_of_week(dd) <= 5L)
        # other observances
        fatthursday <- easter(dd) - 52L == dd
        shrovetuesday <- easter(dd) - 47L == dd
        ashwednesday <- easter(dd) - 46L == dd
        goodfriday <- easter(dd) - 2L == dd
        primaaprilis <- (m == 4L) & (d == 1L)
        flagday <- (m == 5L) & (d == 2L)
        mothersday <- (m == 5L) & (d == 26L)
        childrensday <- (m == 6L) & (d == 1L)
        saintjohnseve <- (m == 6L) & (d == 23L)
        allsoulsday <- (m == 11L) & (d == 2L)
        saintandrewseve <- (m == 11L) & (d == 29L)
        saintnicholasday <- (m == 12L) & (d == 6L)
        christmaseve <- (m == 12L) & (d == 24L) & (y < 2025)
        newyeareve <- (m == 12L) & (d == 31L)
        other <- fatthursday | shrovetuesday | ashwednesday |
                 goodfriday |
                 primaaprilis |
                 flagday |
                 mothersday | childrensday | saintjohnseve |
                 allsoulsday |
                 saintandrewseve |
                 saintnicholasday | christmaseve |
                 newyeareve
        names(other) <- rep("", length(other))
        othernms <- c(fatthursday = "Fat Thursday",
                      shrovetuesday = "Shrove Tuesday",
                      ashwednesday = "Ash Wednesday",
                      goodfriday = "Good Friday",
                      primaaprilis = "All Fool's Day",
                      flagday = "Flag Day",
                      mothersday = "Mother's Day",
                      childrensday = "Children's Day",
                      saintjohnseve = "Saint John's Eve",
                      allsoulsday = "All Souls' Day",
                      saintandrewseve = "Saint Andrew's Eve",
                      saintnicholasday = "Saint Nicholas Day",
                      christmaseve = "Christmas Eve",
                      newyeareve = "New Year's Eve")
        lapply(names(othernms), function(nm) names(other)[get(nm)] <<- othernms[nm])

        return (list(work = work, holiday = holiday, other = other))
    }

    calendar(2020, calendar_PL)

    ##                                       2020
    ##              Jan                       Feb                       Mar
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   01        1  2  3  4  5   05                 1  2   09                    1
    ##   02  6  7  8  9 10 11 12   06  3  4  5  6  7  8  9   10  2  3  4  5  6  7  8
    ##   03 13 14 15 16 17 18 19   07 10 11 12 13 14 15 16   11  9 10 11 12 13 14 15
    ##   04 20 21 22 23 24 25 26   08 17 18 19 20 21 22 23   12 16 17 18 19 20 21 22
    ##   05 27 28 29 30 31         09 24 25 26 27 28 29      13 23 24 25 26 27 28 29
    ##                                                       14 30 31
    ##              Apr                       May                       Jun
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   14        1  2  3  4  5   18              1  2  3   23  1  2  3  4  5  6  7
    ##   15  6  7  8  9 10 11 12   19  4  5  6  7  8  9 10   24  8  9 10 11 12 13 14
    ##   16 13 14 15 16 17 18 19   20 11 12 13 14 15 16 17   25 15 16 17 18 19 20 21
    ##   17 20 21 22 23 24 25 26   21 18 19 20 21 22 23 24   26 22 23 24 25 26 27 28
    ##   18 27 28 29 30            22 25 26 27 28 29 30 31   27 29 30
    ##              Jul                       Aug                       Sep
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   27        1  2  3  4  5   31                 1  2   36     1  2  3  4  5  6
    ##   28  6  7  8  9 10 11 12   32  3  4  5  6  7  8  9   37  7  8  9 10 11 12 13
    ##   29 13 14 15 16 17 18 19   33 10 11 12 13 14 15 16   38 14 15 16 17 18 19 20
    ##   30 20 21 22 23 24 25 26   34 17 18 19 20 21 22 23   39 21 22 23 24 25 26 27
    ##   31 27 28 29 30 31         35 24 25 26 27 28 29 30   40 28 29 30
    ##                             36 31
    ##              Oct                       Nov                       Dec
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   40           1  2  3  4   44                    1   49     1  2  3  4  5  6
    ##   41  5  6  7  8  9 10 11   45  2  3  4  5  6  7  8   50  7  8  9 10 11 12 13
    ##   42 12 13 14 15 16 17 18   46  9 10 11 12 13 14 15   51 14 15 16 17 18 19 20
    ##   43 19 20 21 22 23 24 25   47 16 17 18 19 20 21 22   52 21 22 23 24 25 26 27
    ##   44 26 27 28 29 30 31      48 23 24 25 26 27 28 29   53 28 29 30 31
    ##                             49 30

    calendar(as.year(today()), calendar_PL)

    ##                                       2026
    ##              Jan                       Feb                       Mar
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   01           1  2  3  4   05                    1   09                    1
    ##   02  5  6  7  8  9 10 11   06[ 2] 3  4  5  6  7  8   10  2  3  4  5  6  7  8
    ##   03 12 13 14 15 16 17 18   07  9 10 11 12 13 14 15   11  9 10 11 12 13 14 15
    ##   04 19 20 21 22 23 24 25   08 16 17 18 19 20 21 22   12 16 17 18 19 20 21 22
    ##   05 26 27 28 29 30 31      09 23 24 25 26 27 28      13 23 24 25 26 27 28 29
    ##                                                       14 30 31
    ##              Apr                       May                       Jun
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   14        1  2  3  4  5   18              1  2  3   23  1  2  3  4  5  6  7
    ##   15  6  7  8  9 10 11 12   19  4  5  6  7  8  9 10   24  8  9 10 11 12 13 14
    ##   16 13 14 15 16 17 18 19   20 11 12 13 14 15 16 17   25 15 16 17 18 19 20 21
    ##   17 20 21 22 23 24 25 26   21 18 19 20 21 22 23 24   26 22 23 24 25 26 27 28
    ##   18 27 28 29 30            22 25 26 27 28 29 30 31   27 29 30
    ##              Jul                       Aug                       Sep
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   27        1  2  3  4  5   31                 1  2   36     1  2  3  4  5  6
    ##   28  6  7  8  9 10 11 12   32  3  4  5  6  7  8  9   37  7  8  9 10 11 12 13
    ##   29 13 14 15 16 17 18 19   33 10 11 12 13 14 15 16   38 14 15 16 17 18 19 20
    ##   30 20 21 22 23 24 25 26   34 17 18 19 20 21 22 23   39 21 22 23 24 25 26 27
    ##   31 27 28 29 30 31         35 24 25 26 27 28 29 30   40 28 29 30
    ##                             36 31
    ##              Oct                       Nov                       Dec
    ##      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su      Mo Tu We Th Fr Sa Su
    ##   40           1  2  3  4   44                    1   49     1  2  3  4  5  6
    ##   41  5  6  7  8  9 10 11   45  2  3  4  5  6  7  8   50  7  8  9 10 11 12 13
    ##   42 12 13 14 15 16 17 18   46  9 10 11 12 13 14 15   51 14 15 16 17 18 19 20
    ##   43 19 20 21 22 23 24 25   47 16 17 18 19 20 21 22   52 21 22 23 24 25 26 27
    ##   44 26 27 28 29 30 31      48 23 24 25 26 27 28 29   53 28 29 30 31
    ##                             49 30

    calendar("2020-06", calendar_PL)

    ##            Jun 2020
    ##      Mo Tu We Th Fr Sa Su
    ##   23  1  2  3  4  5  6  7  |  1 - Children's Day
    ##   24  8  9 10 11 12 13 14  | 11 - Corpus Christi
    ##   25 15 16 17 18 19 20 21
    ##   26 22 23 24 25 26 27 28  | 23 - Saint John's Eve
    ##   27 29 30

    calendar(calendar = calendar_PL)

    ##            Jan 2026
    ##      Mo Tu We Th Fr Sa Su
    ##   01           1  2  3  4  |  1 - New Year
    ##   02  5  6  7  8  9 10 11  |  6 - Epiphany
    ##   03 12 13 14 15 16 17 18
    ##   04 19 20 21 22 23 24 25
    ##   05 26 27 28 29 30 31
    ##            Feb 2026
    ##      Mo Tu We Th Fr Sa Su
    ##   05                    1
    ##   06[ 2] 3  4  5  6  7  8
    ##   07  9 10 11 12 13 14 15  | 12 - Fat Thursday
    ##   08 16 17 18 19 20 21 22  | 17 - Shrove Tuesday, 18 - Ash Wednesday
    ##   09 23 24 25 26 27 28

## Business Days

Calendar functions described above can be used for business day computations. `tind` provides the following functions that can be used in this context:

- `bizday()` for finding the closest business day (using different date rolling conventions) if the given day is not a one,
- `bizday_diff()` for computing number of business days between two dates,
- functions determining the first and the last business day in a given period: `first_bizday_in_month()`, `last_bizday_in_month()`, `first_bizday_in_quarter()`, `last_bizday_in_quarter()`,
- functions determining the number of business days in a given period: `bizdays_in_month()`, `bizdays_in_quarter()`, `bizdays_in_year()`.

Some examples using `bizday()` and calendar functions presented earlier are provided below (January 16, 2023 was a public holiday in the US and August 15, 2023 in Poland):

    calendar("2023-01", calendar = calendar_US)

    ##            Jan 2023
    ##      Mo Tu We Th Fr Sa Su
    ##   52                    1  |  1 - New Year's Day
    ##   01  2  3  4  5  6  7  8
    ##   02  9 10 11 12 13 14 15
    ##   03 16 17 18 19 20 21 22  | 16 - Birthday of Martin Luther King, Jr.
    ##   04 23 24 25 26 27 28 29
    ##   05 30 31

    bizday("2023-01-15", "p", calendar_US)

    ## date [tind]
    ## [1] 2023-01-13

    bizday("2023-01-15", "f", calendar_US)

    ## date [tind]
    ## [1] 2023-01-17

    bizday("2023-01-15", "mf2", calendar_US)

    ## date [tind]
    ## [1] 2023-01-13

    calendar("2023-08", calendar = calendar_PL)

    ##            Aug 2023
    ##      Mo Tu We Th Fr Sa Su
    ##   31     1  2  3  4  5  6
    ##   32  7  8  9 10 11 12 13
    ##   33 14 15 16 17 18 19 20  | 15 - Assumption of Mary
    ##   34 21 22 23 24 25 26 27
    ##   35 28 29 30 31

    bizday("2023-08-15", "p", calendar_PL)

    ## date [tind]
    ## [1] 2023-08-14

    bizday("2023-08-15", "f", calendar_PL)

    ## date [tind]
    ## [1] 2023-08-16

    bizday("2023-08-15", "mf2", calendar_PL)

    ## date [tind]
    ## [1] 2023-08-14

The code below creates a table summarising the number of business days in months in 2023 in Poland and the US.

    m <- as.month("2023-01") + 0:11
    data.frame(month = m, PL = bizdays_in_month(m, calendar_PL),
                          US = bizdays_in_month(m, calendar_US))

    ##      month PL US
    ## 1  2023-01 21 21
    ## 2  2023-02 20 19
    ## 3  2023-03 23 23
    ## 4  2023-04 19 20
    ## 5  2023-05 21 22
    ## 6  2023-06 21 21
    ## 7  2023-07 21 20
    ## 8  2023-08 22 23
    ## 9  2023-09 21 20
    ## 10 2023-10 22 21
    ## 11 2023-11 21 21
    ## 12 2023-12 19 20

## Year Fractions and Accrual Factors

`year_frac` function returns time indices as year fractions in the form year + year fraction, where fraction is the number of periods (quarters, months, weeks, days, seconds) since the beginning of the year over total number of periods in the year.

    (d <- as.date("2024-05-05"))

    ## date [tind]
    ## [1] 2024-05-05

    year_frac(d)

    ## [1] 2024.342

    year(d) + (day_of_year(d) - 1) / days_in_year(d)

    ## [1] 2024.342

    as.quarter(d)

    ## quarter [tind]
    ## [1] 2024Q2

    year_frac(as.quarter(d))

    ## [1] 2024.25

    year(d) + (quarter(d) - 1) / 4

    ## [1] 2024.25

    as.month(d)

    ## month [tind]
    ## [1] 2024-05

    year_frac(as.month(d))

    ## [1] 2024.333

    year(d) + (month(d) - 1) / 12

    ## [1] 2024.333

    as.week(d)

    ## week [tind]
    ## [1] 2024-W18

    year_frac(as.week(d))

    ## [1] 2024.327

    year(d) + (week(d) - 1) / weeks_in_year(d)

    ## [1] 2024.327

`daycount_frac` allows to compute accrual factor using different day count conventions. Below we create a sequence of 5 consecutive International Money Market days and compute accrual factors between the first and the following four using different conventions:

    (imm <- nth_dw_in_month(3, 3, tind(y = 2025, m = 12) + 3 * (0:4)))

    ## date [tind]
    ## [1] 2025-12-17 2026-03-18 2026-06-17 2026-09-16 2026-12-16

    daycount_frac(imm[1L], imm[-1L], "30/360")

    ## [1] 0.2527778 0.5000000 0.7472222 0.9972222

    daycount_frac(imm[1L], imm[-1L], "30E/360")

    ## [1] 0.2527778 0.5000000 0.7472222 0.9972222

    daycount_frac(imm[1L], imm[-1L], "ACT/ACT")

    ## [1] 0.2493151 0.4986301 0.7479452 0.9972603

    daycount_frac(imm[1L], imm[-1L], "ACT/365F")

    ## [1] 0.2493151 0.4986301 0.7479452 0.9972603

    daycount_frac(imm[1L], imm[-1L], "ACT/360")

    ## [1] 0.2527778 0.5055556 0.7583333 1.0111111