Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,8 @@ See [#2611](https://github.com/Rdatatable/data.table/issues/2611) for details. T

20. `fread()` now supports the `comment.char` argument to skip trailing comments or comment-only lines, consistent with `read.table()`, [#856](https://github.com/Rdatatable/data.table/issues/856). The default remains `comment.char = ""` (no comment parsing) for backward compatibility and performance, in contrast to `read.table(comment.char = "#")`. Thanks to @arunsrinivasan and many others for the suggestion and @ben-schwen for the implementation.

21. `yearqtr()` and `yearmon()` now gain an optional format specifier [#7694](https://github.com/Rdatatable/data.table/issues/7694). 'numeric' is the deafult, which preserves the original behavior, but 'character' formats `yearqtr()` as YYYYQ# (e.g. 2025Q2) and `yearmon()` as YYYYM## (e.g. 2025M02, 2025M10). Thanks to @jan-swissre for the report and @LunaticSage218 for the implementation.

### BUG FIXES

1. `fread()` no longer warns on certain systems on R 4.5.0+ where the file owner can't be resolved, [#6918](https://github.com/Rdatatable/data.table/issues/6918). Thanks @ProfFancyPants for the report and PR.
Expand Down
26 changes: 24 additions & 2 deletions R/IDateTime.R
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,30 @@ isoyear = function(x) as.integer(format(as.IDate(x), "%G"))
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")
yearmon = function(x, format=c("numeric", "character")) {
format = match.arg(format)
x_as_idate = as.IDate(x)
ymon = convertDate(x_as_idate, "yearmon")
if (format == "numeric") return(ymon)
ans = rep(NA_character_, length(x_as_idate))
ok = !is.na(x_as_idate)
yr = floor(ymon[ok])
mon = round((ymon[ok] - yr) * 12) + 1L
ans[ok] = sprintf("%dM%02d", as.integer(yr), as.integer(mon))
ans
}
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)
ans = rep(NA_character_, length(x_as_idate))
ok = !is.na(x_as_idate)
yr = floor(yqtr[ok])
qtr = round((yqtr[ok] - yr) * 4) + 1L
ans[ok] = paste0(yr, "Q", qtr)
ans
}

convertDate = function(x, type) {
type = match.arg(type, c("yday", "wday", "mday", "week", "month", "quarter", "year", "yearmon", "yearqtr"))
Expand Down
14 changes: 14 additions & 0 deletions inst/tests/tests.Rraw
Original file line number Diff line number Diff line change
Expand Up @@ -21591,3 +21591,17 @@ x = fread("x\n-1-01-01")$x
test(2368.1, year(x), -1L)
test(2368.2, month(x), 1L)
test(2368.3, mday(x), 1L)

# yearqtr() and yearmon() 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(2369.1, yearqtr(x, format="numeric"), c(1111.75, 2019, 2019, 2019, 2019.75, 2020, 2020, 2020.75, 2040, 2040.75, 2100, NA))
test(2369.2, yearqtr(x, format="numeric"), yearqtr(x)) # numeric is the default, preserves backwards compatibility
test(2369.3, yearqtr(x, format="character"), c("1111Q4", "2019Q1", "2019Q1", "2019Q1", "2019Q4", "2020Q1", "2020Q1", "2020Q4", "2040Q1", "2040Q4", "2100Q1", NA_character_))
test(2369.4, yearqtr("2016-08-03 01:02:03.45", format="character"), "2016Q3")
test(2369.5, yearqtr(NA, format="character"), NA_character_)

test(2370.1, yearmon(x, format="numeric"), c(1111+10/12, 2019, 2019+1/12, 2019+2/12, 2019+11/12, 2020+1/12, 2020+2/12, 2020+11/12, 2040, 2040+11/12, 2100+2/12, NA))
test(2370.2, yearmon(x, format="numeric"), yearmon(x)) # numeric is the default, preserves backwards compatibility
test(2370.3, yearmon(x, format="character"), c("1111M11", "2019M01", "2019M02", "2019M03", "2019M12", "2020M02", "2020M03", "2020M12", "2040M01", "2040M12", "2100M03", NA_character_))
test(2370.4, yearmon("2016-08-03 01:02:03.45", format="character"), "2016M08")
test(2370.5, yearmon(NA, format="character"), NA_character_)
16 changes: 13 additions & 3 deletions man/IDateTime.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ isoyear(x)
month(x)
quarter(x)
year(x)
yearmon(x)
yearqtr(x)
yearmon(x, format = c("numeric", "character"))
yearqtr(x, format = c("numeric", "character"))

}

Expand All @@ -115,6 +115,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}{For \code{yearmon} and \code{yearqtr}, either \code{"numeric"} (default) or \code{"character"}. \code{"character"} formats the result as \code{"2025M04"} for \code{yearmon} and \code{"2025Q2"} for \code{yearqtr}.}
}
\details{
\code{IDate} is a date class derived from \code{Date}. It has the same
Expand Down Expand Up @@ -209,7 +210,11 @@ Similarly, \code{isoyear()} returns the ISO 8601 year corresponding to the ISO w
for second, minute, hour, day of year, day of week,
day of month, week, month, quarter, and year, respectively.
\code{yearmon} and \code{yearqtr} return double values representing
respectively \code{year + (month-1) / 12} and \code{year + (quarter-1) / 4}.
respectively \code{year + (month-1) / 12} and \code{year + (quarter-1) / 4}
when \code{format = "numeric"} (the default). When \code{format = "character"},
they return character vectors of the form \code{"YYYYMMM"} (e.g. \code{"2025M04"},
zero-padded for sortability) and \code{"YYYYQN"} (e.g. \code{"2025Q2"}) respectively,
with \code{NA} input returned as \code{NA_character_}.

\code{second}, \code{minute}, \code{hour} are taken directly from
the \code{POSIXlt} representation.
Expand Down Expand Up @@ -296,6 +301,11 @@ year(d2)
isoweek(d2)
isoyear(d2)

# Character format for yearmon() and yearqtr()
d3 = as.IDate(c("2019-01-01", "2019-12-31"))
yearmon(d3, format = "character") # "2019M01" "2019M12"
yearqtr(d3, format = "character") # "2019Q1" "2019Q4"

}
\keyword{utilities}

Loading