diff --git a/NEWS.md b/NEWS.md index 021ddbbb6..04f49960c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -50,6 +50,8 @@ 9. `fread()` no longer replaces a literal header column name `"NA"` with an auto-generated `Vn` name when `na.strings` includes `"NA"`, [#5124](https://github.com/Rdatatable/data.table/issues/5124). Data rows still continue to parse `"NA"` as missing. Thanks @Mashin6 for the report and @shrektan for the fix. +10. `yearqtr()` gains an optional `format` argument [#7694](https://github.com/Rdatatable/data.table/issues/7694). 'numeric' is the default and preserves the original behavior, while 'character' formats the date as YYYYQ# (e.g., 2025Q2). Thanks to @jan-swissre for the report and @LunaticSage218 for the implementation. + ### Notes 1. {data.table} now depends on R 3.5.0 (2018). diff --git a/R/IDateTime.R b/R/IDateTime.R index 49fa5abda..45248611d 100644 --- a/R/IDateTime.R +++ b/R/IDateTime.R @@ -366,7 +366,17 @@ month = function(x) convertDate(as.IDate(x), "month") quarter = function(x) convertDate(as.IDate(x), "quarter") year = function(x) convertDate(as.IDate(x), "year") yearmon = function(x) convertDate(as.IDate(x), "yearmon") -yearqtr = function(x) convertDate(as.IDate(x), "yearqtr") +yearqtr = function(x, format=c("numeric", "character")) { + format = match.arg(format) + x_as_idate = as.IDate(x) + yqtr = convertDate(x_as_idate, "yearqtr") + if (format == "numeric") return(yqtr) + yr = floor(yqtr) + qtr = round((yqtr - yr) * 4) + 1L + ans = paste0(yr, "Q", qtr) + ans[is.na(x_as_idate)] = NA_character_ + ans +} convertDate = function(x, type) { type = match.arg(type, c("yday", "wday", "mday", "week", "month", "quarter", "year", "yearmon", "yearqtr")) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 443487c6a..af18b0d2b 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -21585,3 +21585,12 @@ close(con) file.create(f <- tempfile()) test(2367.6, fread(file(f)), data.table(), warning="Connection has size 0.") unlink(f) + +# yearqtr() could optionally output 2025Q4 format #7694 +x = c("1111-11-11", "2019-01-01", "2019-02-28", "2019-03-01", "2019-12-31", "2020-02-29", "2020-03-01", "2020-12-31", "2040-01-01", "2040-12-31", "2100-03-01", NA) +test(2368.1, yearqtr(x, format="numeric"), c(1111.75, 2019, 2019, 2019, 2019.75, 2020, 2020, 2020.75, 2040, 2040.75, 2100, NA)) +test(2368.2, yearqtr(x, format="numeric"), yearqtr(x)) # numeric is the default, preserves backwards compatibility +test(2368.3, yearqtr(x, format="character"), c("1111Q4", "2019Q1", "2019Q1", "2019Q1", "2019Q4", "2020Q1", "2020Q1", "2020Q4", "2040Q1", "2040Q4", "2100Q1", NA_character_)) +test(2368.4, yearqtr("2016-08-03 01:02:03.45", format="character"), "2016Q3") +test(2368.5, yearqtr(NA, format="character"), NA_character_) +test(2368.6, yearqtr(x, format="invalid"), error="should be one of") diff --git a/man/IDateTime.Rd b/man/IDateTime.Rd index cf762337e..41e36d267 100644 --- a/man/IDateTime.Rd +++ b/man/IDateTime.Rd @@ -98,8 +98,7 @@ month(x) quarter(x) year(x) yearmon(x) -yearqtr(x) - +yearqtr(x, format = c("numeric", "character")) } \arguments{ @@ -115,6 +114,7 @@ yearqtr(x) the S3 generic.} \item{units}{one of the units listed for truncating. May be abbreviated.} \item{ms}{ For \code{as.ITime} methods, what should be done with sub-second fractions of input? Valid values are \code{'truncate'} (floor), \code{'nearest'} (round), and \code{'ceil'} (ceiling). See Details. } + \item{format}{format is either \code{"numeric"} (default) or \code{"character"}. \code{"character"} formats the result as \code{"2025Q4"}.} } \details{ \code{IDate} is a date class derived from \code{Date}. It has the same