From 8c7dffb6b3cde001183beaf900ff8bb8dcdbe4a8 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Wed, 6 Aug 2025 09:56:25 -0700 Subject: [PATCH 1/6] t1-t2 only gains difftime class optionally, for now --- NEWS.md | 4 ++++ R/IDateTime.R | 6 ++++-- R/onLoad.R | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index c76736e504..93211e9af4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -12,6 +12,10 @@ 3. options `"datatable.old.matrix.autoname"` is now `FALSE` by default, meaning `names(data.table(x=1, cbind(1)))` is now `c("x", "V2")`. Toggle the option to retain the old behavior for now; future releases will work to remove this possibility. See the release notes for 1.18.0, item 1 under `NOTE OF INTENDED FUTURE POTENTIAL BREAKING CHANGES`. +### NOTICE OF INTENDED FUTURE POTENTIAL BREAKING CHANGES + +1. `t1 - t2`, where both `t1` and `t2` are `IDate`, will eventually have class `difftime`, consistent with the case where `t1` and `t2` are both `Date`. See point 17 under Bug Fixes in 1.18.0 for more context. + ### NEW FEATURES 1. `nafill()`, `setnafill()` extended to work on logical and factor vectors (part of [#3992](https://github.com/Rdatatable/data.table/issues/3992)). Includes support for `Date`, `IDate`, `POSIXct`, etc. `nafill()` works for character vectors, but not yet `setnafill()`. Thanks @jangorecki for the request and @jangorecki and @MichaelChirico for the PRs. diff --git a/R/IDateTime.R b/R/IDateTime.R index 49fa5abda2..7b4f13d71a 100644 --- a/R/IDateTime.R +++ b/R/IDateTime.R @@ -128,8 +128,10 @@ chooseOpsMethod.IDate = function(x, y, mx, my, cl, reverse) inherits(y, "Date") } ans = as.integer(unclass(e1) - unclass(e2)) if (inherits(e2, "Date")) { - setattr(ans, "class", "difftime") - setattr(ans, "units", "days") + if (!isTRUE(getOption("datatable.old.diff.date"))) { + setattr(ans, "class", "difftime") + setattr(ans, "units", "days") + } } else { setattr(ans, "class", c("IDate", "Date")) } diff --git a/R/onLoad.R b/R/onLoad.R index b72fee4d1b..a961311ec5 100644 --- a/R/onLoad.R +++ b/R/onLoad.R @@ -99,6 +99,7 @@ datatable.use.index=TRUE, # global switch to address #1422 datatable.prettyprint.char=NULL, # FR #1091 datatable.old.matrix.autoname=FALSE # #7145: how data.table(x=1, matrix(1)) is auto-named set to change + datatable.old.diff.date=TRUE # whether - gets difftime class set to change ) opts = opts[!names(opts) %chin% names(options())] options(opts) From fe7de9e5379998780b4acfd234b44b237ecc01bf Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Wed, 6 Aug 2025 09:58:18 -0700 Subject: [PATCH 2/6] mention option --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 93211e9af4..f2fc78e0ee 100644 --- a/NEWS.md +++ b/NEWS.md @@ -14,7 +14,7 @@ ### NOTICE OF INTENDED FUTURE POTENTIAL BREAKING CHANGES -1. `t1 - t2`, where both `t1` and `t2` are `IDate`, will eventually have class `difftime`, consistent with the case where `t1` and `t2` are both `Date`. See point 17 under Bug Fixes in 1.18.0 for more context. +1. `t1 - t2`, where both `t1` and `t2` are `IDate`, will eventually have class `difftime`, consistent with the case where `t1` and `t2` are both `Date`. You can activate the new behavior by setting `options(datatable.old.diff.time = FALSE)`. See point 17 under Bug Fixes for more context. ### NEW FEATURES From fc7c610cc7bc3ef1f03cf562491fa291c4ff2446 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Wed, 6 Aug 2025 10:38:35 -0700 Subject: [PATCH 3/6] doc options --- man/data.table-options.Rd | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/man/data.table-options.Rd b/man/data.table-options.Rd index 439e88ef2f..5fd2688f29 100644 --- a/man/data.table-options.Rd +++ b/man/data.table-options.Rd @@ -114,6 +114,10 @@ expressions like \code{data.table(x=1, cbind(1))} will be named. When \code{TRUE}, it will be named \code{V1}, otherwise it will be named \code{V2}. } + \item{\code{datatable.old.diff.date}}{Logical, default \code{TRUE}. Governs whether \code{t1 - t2}, + where \code{t1} and \code{t2} are both class \code{IDate}, gains the S3 class \code{difftime}. + When \code{TRUE}, this class is assigned (with \code{units="days"}). + } } } From 4bec075d10148a5f901b03a2b864368dc4a3d180 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Wed, 6 Aug 2025 10:39:12 -0700 Subject: [PATCH 4/6] rename: date->idate --- R/IDateTime.R | 2 +- R/onLoad.R | 2 +- man/data.table-options.Rd | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/R/IDateTime.R b/R/IDateTime.R index 7b4f13d71a..7986edf5c7 100644 --- a/R/IDateTime.R +++ b/R/IDateTime.R @@ -128,7 +128,7 @@ chooseOpsMethod.IDate = function(x, y, mx, my, cl, reverse) inherits(y, "Date") } ans = as.integer(unclass(e1) - unclass(e2)) if (inherits(e2, "Date")) { - if (!isTRUE(getOption("datatable.old.diff.date"))) { + if (!isTRUE(getOption("datatable.old.diff.idate"))) { setattr(ans, "class", "difftime") setattr(ans, "units", "days") } diff --git a/R/onLoad.R b/R/onLoad.R index a961311ec5..62aca65c51 100644 --- a/R/onLoad.R +++ b/R/onLoad.R @@ -99,7 +99,7 @@ datatable.use.index=TRUE, # global switch to address #1422 datatable.prettyprint.char=NULL, # FR #1091 datatable.old.matrix.autoname=FALSE # #7145: how data.table(x=1, matrix(1)) is auto-named set to change - datatable.old.diff.date=TRUE # whether - gets difftime class set to change + datatable.old.diff.idate=TRUE # whether - gets difftime class set to change ) opts = opts[!names(opts) %chin% names(options())] options(opts) diff --git a/man/data.table-options.Rd b/man/data.table-options.Rd index 5fd2688f29..b66a97d6f0 100644 --- a/man/data.table-options.Rd +++ b/man/data.table-options.Rd @@ -114,7 +114,7 @@ expressions like \code{data.table(x=1, cbind(1))} will be named. When \code{TRUE}, it will be named \code{V1}, otherwise it will be named \code{V2}. } - \item{\code{datatable.old.diff.date}}{Logical, default \code{TRUE}. Governs whether \code{t1 - t2}, + \item{\code{datatable.old.diff.idate}}{Logical, default \code{TRUE}. Governs whether \code{t1 - t2}, where \code{t1} and \code{t2} are both class \code{IDate}, gains the S3 class \code{difftime}. When \code{TRUE}, this class is assigned (with \code{units="days"}). } From c55653eb6f695f764d40f0261b9285c11410a53d Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Wed, 6 Aug 2025 10:43:42 -0700 Subject: [PATCH 5/6] tweak tests --- inst/tests/tests.Rraw | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index b5ef44c6b2..3bc8a6b8ac 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -10406,7 +10406,7 @@ test(1673.1, TT + 4L, as.IDate("2016-04-29")) test(1673.2, TT + 4, as.IDate("2016-04-29")) test(1673.3, TT - 3, as.IDate("2016-04-22")) test(1673.4, TT - 3L, as.IDate("2016-04-22")) -test(1673.5, all.equal(as.IDate("2016-04-28") - as.IDate("2016-04-20"), as.difftime(8, units='days'))) +test(1673.5, options=c(datatable.old.diff.idate=FALSE), all.equal(as.IDate("2016-04-28") - as.IDate("2016-04-20"), as.difftime(8, units='days'))) # test for radix integer order when MAXINT is present AND decreasing=TRUE AND na.last=FALSE @@ -21676,16 +21676,18 @@ test(2335.5, isoyear("2019-12-30"), 2020L) test(2335.6, isoyear(as.Date("2019-12-30")), 2020L) # t1-t2 for Date/IDate should be consistent, modulo storage mode #4979 -if (base::getRversion() >= "4.3.0") { ## follow up of #7213, see #7321 - t1 = as.IDate("2025-07-01") - t2 = as.IDate("2025-06-01") - test(2336.1, all.equal(as.Date(t1) - as.Date(t2), t1 - t2)) - test(2336.2, all.equal(as.Date(t2) - as.Date(t1), t2 - t1)) - test(2336.3, all.equal(as.Date(t1) - t2, t1 - t2)) - test(2336.4, all.equal(as.Date(t2) - t1, t2 - t1)) - test(2336.5, all.equal(t1 - as.Date(t2), t1 - t2)) - test(2336.6, all.equal(t2 - as.Date(t1), t2 - t1)) -} +t1 = as.IDate("2025-07-01") +t2 = as.IDate("2025-06-01") +test(2336.01, options=c(datatable.old.diff.idate=FALSE), all.equal(as.Date(t1) - as.Date(t2), t1 - t2)) +test(2336.02, options=c(datatable.old.diff.idate=FALSE), all.equal(as.Date(t2) - as.Date(t1), t2 - t1)) +test(2336.03, options=c(datatable.old.diff.idate=FALSE), all.equal(as.Date(t1) - t2, t1 - t2)) +test(2336.04, options=c(datatable.old.diff.idate=FALSE), all.equal(as.Date(t2) - t1, t2 - t1)) +test(2336.05, all.equal(t1 - as.Date(t2), t1 - t2)) +test(2336.06, all.equal(t2 - as.Date(t1), t2 - t1)) +test(2336.07, all.equal(as.numeric(as.Date(t1) - as.Date(t2)), as.numeric(t1 - t2))) +test(2336.08, all.equal(as.numeric(as.Date(t2) - as.Date(t1)), as.numeric(t2 - t1))) +test(2336.09, all.equal(as.numeric(as.Date(t1) - t2), as.numeric(t1 - t2))) +test(2336.10, all.equal(as.numeric(as.Date(t2) - t1), as.numeric(t2 - t1))) # fwrite: allow dec=',' with single column, #7227 test(2337.1, fwrite(data.table(1), dec=","), output = "V1\n1") From ec3f850d9f54bc780e7e83cf45f005b5d6306fb8 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Sun, 11 Jan 2026 23:00:08 -0800 Subject: [PATCH 6/6] fix up merge --- R/onLoad.R | 2 +- inst/tests/tests.Rraw | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/R/onLoad.R b/R/onLoad.R index 62aca65c51..ddf518b2d3 100644 --- a/R/onLoad.R +++ b/R/onLoad.R @@ -98,7 +98,7 @@ datatable.auto.index=TRUE, # DT[col=="val"] to auto add index so 2nd time faster datatable.use.index=TRUE, # global switch to address #1422 datatable.prettyprint.char=NULL, # FR #1091 - datatable.old.matrix.autoname=FALSE # #7145: how data.table(x=1, matrix(1)) is auto-named set to change + datatable.old.matrix.autoname=FALSE,# #7145: how data.table(x=1, matrix(1)) is auto-named set to change datatable.old.diff.idate=TRUE # whether - gets difftime class set to change ) opts = opts[!names(opts) %chin% names(options())] diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 3bc8a6b8ac..e0fabb05a3 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -21682,12 +21682,14 @@ test(2336.01, options=c(datatable.old.diff.idate=FALSE), all.equal(as.Date(t1) - test(2336.02, options=c(datatable.old.diff.idate=FALSE), all.equal(as.Date(t2) - as.Date(t1), t2 - t1)) test(2336.03, options=c(datatable.old.diff.idate=FALSE), all.equal(as.Date(t1) - t2, t1 - t2)) test(2336.04, options=c(datatable.old.diff.idate=FALSE), all.equal(as.Date(t2) - t1, t2 - t1)) -test(2336.05, all.equal(t1 - as.Date(t2), t1 - t2)) -test(2336.06, all.equal(t2 - as.Date(t1), t2 - t1)) -test(2336.07, all.equal(as.numeric(as.Date(t1) - as.Date(t2)), as.numeric(t1 - t2))) -test(2336.08, all.equal(as.numeric(as.Date(t2) - as.Date(t1)), as.numeric(t2 - t1))) -test(2336.09, all.equal(as.numeric(as.Date(t1) - t2), as.numeric(t1 - t2))) -test(2336.10, all.equal(as.numeric(as.Date(t2) - t1), as.numeric(t2 - t1))) +if (base::getRversion() >= "4.3.0") { ## follow up of #7213, see #7321 + test(2336.05, all.equal(t1 - as.Date(t2), t1 - t2)) + test(2336.06, all.equal(t2 - as.Date(t1), t2 - t1)) + test(2336.07, all.equal(as.numeric(as.Date(t1) - as.Date(t2)), as.numeric(t1 - t2))) + test(2336.08, all.equal(as.numeric(as.Date(t2) - as.Date(t1)), as.numeric(t2 - t1))) + test(2336.09, all.equal(as.numeric(as.Date(t1) - t2), as.numeric(t1 - t2))) + test(2336.10, all.equal(as.numeric(as.Date(t2) - t1), as.numeric(t2 - t1))) +} # fwrite: allow dec=',' with single column, #7227 test(2337.1, fwrite(data.table(1), dec=","), output = "V1\n1")