Dint: A tooblox for year-quarter and year-month dates

Stefan Fleck

2018-09-29

library(dint)

dint helps you with working with year-quarter and year-month dates. It is implemented in base R and comes with zero external dependencies. Even if you don’t need to work with such special dates directly, dint can still help you format (for example) plot axis.

date_xx Objects

dint provides 4 different S3 classes that inherit from date_xx. * date_yq for year-quarter dates * date_yw for year-month dates * date_yw for year-isoweek dates. Please note that the year for isoweeks does not necessarily correspond to the calender year wikipedia * date_y for storing years. This class exists for consistency and provides litle advantage over storing years as bare integers. The main use of this class is in package development when you want to write your own generics and methods for years.

Creating


# date_* Objects can be created using explicit constructors...
date_yq(2015, 1)
#> [1] "2015-Q1"
date_ym(c(2015, 2016), c(1, 2))
#> [1] "2015-M01" "2016-M02"
date_yw(c(2008, 2009), 1)
#> [1] "2008-W01" "2009-W01"

# ...or through coercion of dates or integers
as_date_yq(Sys.Date())
#> [1] "2018-Q3"
as_date_yq(20141)   # the last digit is interpreted as quarter
#> [1] "2014-Q1"
as_date_ym(201412)  # the last two digits are interpreted as month
#> [1] "2014-M12"
as_date_yw("2018-01-01")  # anything else that can be parsed by as.Date() works
#> [1] "2018-W01"

Coercion


# You can coerce dates to any date_xx object with as_date_**
d <- as.Date("2018-05-12")
as_date_yq(d)
#> [1] "2018-Q2"
as_date_ym(d)
#> [1] "2018-M05"
as_date_yw(d)
#> [1] "2018-W19"
as_date_y(d)
#> [1] "2018"


# Conversely, you can convert date_xx objects back to the various R Date formats
q <- date_yq(2015, 1)
as.Date(q)
#> [1] "2015-01-01"
as.POSIXlt(q)
#> [1] "2015-01-01 UTC"


# as.POSIXct creates datetimes in UTC/GMT, so the result might not always be as
# expected, depending on your local timezone.
as.POSIXct(q)
#> [1] "2015-01-01 01:00:00 CET"
as.POSIXct(q, tz = "GMT")
#> [1] "2015-01-01 01:00:00 CET"
print(as.POSIXct(q), tz = "GMT")
#> [1] "2015-01-01 GMT"
print(as.POSIXct(q), tz = "CET")
#> [1] "2015-01-01 01:00:00 CET"

Arithmetics and Sequences

# Quarters
q <- date_yq(2014, 4)
q
#> [1] "2014-Q4"
q + 1
#> [1] "2015-Q1"
seq(q -2, q + 2)
#> [1] "2014-Q2" "2014-Q3" "2014-Q4" "2015-Q1" "2015-Q2"


# Months
m <- date_ym(2014, 12)
m
#> [1] "2014-M12"
m + 1
#> [1] "2015-M01"
seq(m -2, m + 2)
#> [1] "2014-M10" "2014-M11" "2014-M12" "2015-M01" "2015-M02"

Accessing elements

q <- date_yq(2014, 4)
get_year(q)
#> [1] 2014
get_quarter(q)
#> [1] 4
get_month(q) # defaults to first month of quarter
#> [1] 10


m <- date_ym(2014, 12)
get_year(m)
#> [1] 2014
get_quarter(m)
#> [1] 4
get_month(m)
#> [1] 12

If you use lubridate, you can just use the slighly less verbose lubridate accessors

stopifnot(requireNamespace("lubridate"))

lubridate::year(q)
#> [1] 2014
lubridate::quarter(q)
#> [1] 4
lubridate::month(q)
#> [1] 10

You can also get the first and last days of calendar periods with dint

q <- date_yq(2015, 1)
first_of_quarter(q)  # the same as as.Date(q), but more explicit
#> [1] "2015-01-01"
last_of_quarter(q)  # the same as as.Date(q), but more explicit
#> [1] "2015-03-31"


# These functions work with normal dates
d <- as.Date("2018-05-12")
first_of_year(d)
#> [1] "2018-01-01"
last_of_year(d)
#> [1] "2018-12-31"
first_of_quarter(d)
#> [1] "2018-04-01"
last_of_quarter(d)
#> [1] "2018-06-30"
first_of_month(d)
#> [1] "2018-05-01"
last_of_month(d)
#> [1] "2018-05-31"
first_of_isoweek(d)
#> [1] "2018-05-07"
last_of_isoweek(d)
#> [1] "2018-05-13"

# Alternativeley you can also use these:
first_of_yq(2012, 2)
#> [1] "2012-04-01"
last_of_ym(2012, 2)
#> [1] "2012-02-29"

Formatting

Formating date_yq objects is easy and uses a subset of the placeholders of base::strptime() (+ %q for quarters).

q <- date_yq(2014, 4)
format(q, "%Y-Q%q")  # iso/default
#> [1] "2014-Q4"
format(q, "%Y.%q")
#> [1] "2014.4"
format(q, "%y.%q")
#> [1] "14.4"


m <- date_ym(2014, 12)
format(m, "%Y-M%m")  # iso/default
#> [1] "2014-M12"

w <- date_yw(2014, 1)
format(w, "%Y-W%W")  # iso/default
#> [1] "2014-W01"


# You can use these for coercion and formatting in one step
format_yq(Sys.Date())
#> [1] "2018-Q3"
format_ym(Sys.Date())
#> [1] "2018-M09"
format_yw(Sys.Date())
#> [1] "2018-W39"

For convenience, dint provides direct formatting functions for dates that are useful for labelling ggplot axis.

library(ggplot2)

x <- data.frame(
  time  = seq(as.Date("2016-01-01"), as.Date("2016-08-08"), by = "day")
)

x$value <- rnorm(nrow(x))

p <- ggplot(
  x,
  aes(
    x = time, 
    y = value)
  ) + geom_point()


p + ggtitle("iso") + ggtitle("default")
p + scale_x_date(labels = format_yq_iso) + ggtitle("date_yq_iso")
p + scale_x_date(labels = format_ym_short) + ggtitle("date_ym_short")
p + scale_x_date(labels = format_yw_shorter) + ggtitle("date_yw_shorter")