diff --git a/NEWS.md b/NEWS.md index c76736e50..f2fc78e0e 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`. 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 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 49fa5abda..7986edf5c 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.idate"))) { + 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 b72fee4d1..ddf518b2d 100644 --- a/R/onLoad.R +++ b/R/onLoad.R @@ -98,7 +98,8 @@ 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())] options(opts) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index b5ef44c6b..e0fabb05a 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,15 +21676,19 @@ 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 +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)) 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)) + 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 diff --git a/man/data.table-options.Rd b/man/data.table-options.Rd index 439e88ef2..b66a97d6f 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.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"}). + } } }