Skip to content

Commit e452ac9

Browse files
Merge branch 'master' into fwrite_forceDecimal
2 parents 578bff0 + d875823 commit e452ac9

File tree

17 files changed

+188
-98
lines changed

17 files changed

+188
-98
lines changed

.github/workflows/R-CMD-check.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ jobs:
3636
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
3737
RSPM: ${{ matrix.config.rspm }}
3838
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
39+
_R_CHECK_RD_CHECKRD_MINLEVEL_: -Inf
3940

4041
steps:
4142
- uses: actions/checkout@v4

.gitlab-ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ test-lin-rel:
114114
variables:
115115
_R_CHECK_FORCE_SUGGESTS_: "TRUE"
116116
OPENBLAS_MAIN_FREE: "1"
117+
_R_CHECK_RD_CHECKRD_MINLEVEL_: "-Inf"
117118
script:
118119
- *install-deps
119120
- echo 'CFLAGS=-g -O3 -flto=auto -fno-common -fopenmp -Wall -Wvla -pedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2' > ~/.R/Makevars

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ if (getRversion() >= "3.6.0") {
155155
export(as.IDate,as.ITime,IDateTime)
156156
export(second,minute,hour,yday,wday,mday,week,isoweek,isoyear,month,quarter,year,yearmon,yearqtr)
157157

158+
if (getRversion() >= "4.3.0") S3method(chooseOpsMethod, IDate)
158159
S3method("[", ITime)
159160
S3method("+", IDate)
160161
S3method("-", IDate)

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@
103103
104104
16. `between()` is now more robust with `integer64` arguments. Combining small integer `x` with certain large `integer64` bounds no longer misinterprets the bounds as `double`; if a `double` bound cannot be losslessly converted into `integer64` for comparison with `integer64` `x`, an error is signalled instead of returning a wrong answer with a warning; [#7164](https://github.com/Rdatatable/data.table/issues/7164). Thanks @aitap for the bug report and the fix.
105105
106+
17. `t1 - t2`, where one is an `IDate` and the other is a `Date`, are now consistent with the case where both are `IDate` or both are `Date`, [#4749](https://github.com/Rdatatable/data.table/issues/4749). Thanks @George9000 for the report and @MichaelChirico for the fix.
107+
106108
### NOTES
107109
108110
1. The following in-progress deprecations have proceeded:

R/IDateTime.R

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ round.IDate = function(x, digits=c("weeks", "months", "quarters", "years"), ...)
9191
years = ISOdate(year(x), 1L, 1L)))
9292
}
9393

94+
chooseOpsMethod.IDate = function(x, y, mx, my, cl, reverse) inherits(y, "Date")
95+
9496
#Adapted from `+.Date`
9597
`+.IDate` = function(e1, e2) {
9698
if (nargs() == 1L)
@@ -115,7 +117,7 @@ round.IDate = function(x, digits=c("weeks", "months", "quarters", "years"), ...)
115117
if (storage.mode(e1) != "integer")
116118
internal_error("storage mode of IDate is somehow no longer integer") # nocov
117119
if (nargs() == 1L)
118-
stopf("unary - is not defined for \"IDate\" objects")
120+
stopf('unary - is not defined for "IDate" objects')
119121
if (inherits(e2, "difftime"))
120122
internal_error("difftime objects may not be subtracted from IDate, but Ops dispatch should have intervened to prevent this") # nocov
121123

@@ -127,7 +129,12 @@ round.IDate = function(x, digits=c("weeks", "months", "quarters", "years"), ...)
127129
# ii) .Date was newly exposed in R some time after 3.4.4
128130
}
129131
ans = as.integer(unclass(e1) - unclass(e2))
130-
if (!inherits(e2, "Date")) setattr(ans, "class", c("IDate", "Date"))
132+
if (inherits(e2, "Date")) {
133+
setattr(ans, "class", "difftime")
134+
setattr(ans, "units", "days")
135+
} else {
136+
setattr(ans, "class", c("IDate", "Date"))
137+
}
131138
ans
132139
}
133140

R/setops.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ all.equal.data.table = function(target, current, trim.levels=TRUE, check.attribu
160160
return(sprintf(
161161
"%s. 'target': %s. 'current': %s.",
162162
gettext("Datasets have different keys"),
163-
if(length(k1)) brackify(k1) else gettextf("has no key"),
164-
if(length(k2)) brackify(k2) else gettextf("has no key")
163+
if(length(k1)) brackify(k1) else gettext("has no key"),
164+
if(length(k2)) brackify(k2) else gettext("has no key")
165165
))
166166
}
167167
# check index
@@ -171,8 +171,8 @@ all.equal.data.table = function(target, current, trim.levels=TRUE, check.attribu
171171
return(sprintf(
172172
"%s. 'target': %s. 'current': %s.",
173173
gettext("Datasets have different indices"),
174-
if(length(i1)) brackify(i1) else gettextf("has no index"),
175-
if(length(i2)) brackify(i2) else gettextf("has no index")
174+
if(length(i1)) brackify(i1) else gettext("has no index"),
175+
if(length(i2)) brackify(i2) else gettext("has no index")
176176
))
177177
}
178178

R/test.data.table.R

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,30 @@ test.data.table = function(script="tests.Rraw", verbose=FALSE, pkg=".", silent=F
205205
}
206206
# nocov end
207207

208-
err = try(sys.source(fn, envir=env), silent=silent)
208+
env$warnings = list()
209+
err = try(
210+
withCallingHandlers(
211+
sys.source(fn, envir=env),
212+
warning=function(w) {
213+
# nocov start
214+
if (!silent && showProgress) print(w)
215+
env$warnings = c(env$warnings, list(list(
216+
"after test"=env$prevtest, warning=conditionMessage(w),
217+
calls=paste(
218+
vapply_1c(sys.calls(), function(call) {
219+
if (is.name(call[[1]])) {
220+
as.character(call[[1]])
221+
} else "..."
222+
}),
223+
collapse=" -> "
224+
)
225+
)))
226+
invokeRestart("muffleWarning")
227+
# nocov end
228+
}
229+
),
230+
silent=silent
231+
)
209232

210233
options(oldOptions)
211234
for (i in oldEnv) {
@@ -262,6 +285,21 @@ test.data.table = function(script="tests.Rraw", verbose=FALSE, pkg=".", silent=F
262285
# important to stopf() here, so that 'R CMD check' fails
263286
# nocov end
264287
}
288+
if (length(env$warnings)) {
289+
# nocov start
290+
warnings = rbindlist(env$warnings)
291+
catf(
292+
ngettext(nrow(warnings),
293+
"Caught %d warning outside the test() calls:\n",
294+
"Caught %d warnings outside the test() calls:\n"
295+
),
296+
nrow(warnings),
297+
domain=NA
298+
)
299+
print(warnings, nrows = nrow(warnings))
300+
stopf("Tests succeeded, but non-test code caused warnings. Search %s for tests shown above.", names(fn))
301+
# nocov end
302+
}
265303

266304
# There aren't any errors, so we can use up 11 lines for the timings table
267305
time = nTest = RSS = NULL # to avoid 'no visible binding' note

inst/tests/tests.Rraw

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10342,7 +10342,7 @@ test(1673.1, TT + 4L, as.IDate("2016-04-29"))
1034210342
test(1673.2, TT + 4, as.IDate("2016-04-29"))
1034310343
test(1673.3, TT - 3, as.IDate("2016-04-22"))
1034410344
test(1673.4, TT - 3L, as.IDate("2016-04-22"))
10345-
test(1673.5, as.IDate("2016-04-28") - as.IDate("2016-04-20"), 8L)
10345+
test(1673.5, all.equal(as.IDate("2016-04-28") - as.IDate("2016-04-20"), as.difftime(8, units='days')))
1034610346

1034710347

1034810348
# test for radix integer order when MAXINT is present AND decreasing=TRUE AND na.last=FALSE
@@ -16996,7 +16996,8 @@ if (.Platform$OS.type=="windows") local({
1699616996
LC_TIME = "Chinese (Simplified)_China.936"
1699716997
)
1699816998
x_old = Map(Sys.getlocale, names(x))
16999-
invisible(Map(Sys.setlocale, names(x), x))
16999+
# setlocale() warns for non-UTF-8 locales in modern versions of R, #7210
17000+
suppressWarnings(invisible(Map(Sys.setlocale, names(x), x)))
1700017001
on.exit(Map(Sys.setlocale, names(x_old), x_old))
1700117002
# triggered segfault here in #4402, Windows-only under translation.
1700217003
# test that the argument order changes correctly (the 'item 2' moves to the beginning of the message)
@@ -18615,12 +18616,18 @@ test(2252.2, dt[, let(b=2L)], error = "\\[ was called on a data.table.*not data.
1861518616
rm(.datatable.aware)
1861618617

1861718618
# tests for trunc.char handling wide characters # 5096
18618-
local({
18619+
local(if (l10n_info()$`UTF-8` || {
1861918620
lc_ctype = Sys.getlocale('LC_CTYPE')
18621+
lc_wantctype = 'en_US.UTF-8'
1862018622
# Japanese multibyte characters require utf8. As of 2025, we're likely to be already running in a UTF-8 locale, but if not, try this setlocale() call as a last chance.
1862118623
# Unfortunately, there is no guaranteed, portable way of switching to UTF-8 US English.
18622-
if (!l10n_info()$`UTF-8`) Sys.setlocale('LC_CTYPE', "en_US.UTF-8")
18623-
on.exit(Sys.setlocale('LC_CTYPE', lc_ctype))
18624+
# Avoid the warning upon possible failure, #7210.
18625+
lc_newctype = suppressWarnings(Sys.setlocale('LC_CTYPE', lc_wantctype))
18626+
if (identical(lc_newctype, lc_wantctype)) {
18627+
on.exit(Sys.setlocale('LC_CTYPE', lc_ctype))
18628+
TRUE
18629+
} else FALSE
18630+
}) {
1862418631
accented_a = "\u0061\u0301"
1862518632
ja_ichi = "\u4E00"
1862618633
ja_ni = "\u4E8C"
@@ -18670,6 +18677,8 @@ local({
1867018677
paste0(c(ja_ko, ja_n, accented_a), dots, collapse=" ")))
1867118678
# test for data.table with NA, #6441
1867218679
test(2253.20, options=list(datatable.prettyprint.char = 1L), data.table(a = c("abc", NA)), output=" a\n1: a...\n2: <NA>")
18680+
} else {
18681+
cat("Tests 2253* skipped because they need a UTF-8 locale.\n")
1867318682
})
1867418683

1867518684
# allow 1-D matrix in j for consistency, #783
@@ -21554,10 +21563,18 @@ test(2332.81, {M1[]=frev(M1); M1}, {M2[]=rev(M2); M2})
2155421563
test(2333, as.expression(data.table(a = 1))[["a"]], 1)
2155521564

2155621565
# regression test for hexdigits subscript overrun (uint8_t wraps over 255, unsigned overflow is well defined in c)
21557-
f = tempfile()
21558-
writeLines(c('a', rep('0x1.ffffp0', 10000L), '0x1.ff\x9fp0', rep('0x1.ffffp0', 20000L)), f)
21559-
test(2334, names(fread(f)), "a")
21560-
unlink(f)
21566+
local({
21567+
f = tempfile()
21568+
on.exit(unlink(f))
21569+
# the line is likely invalid in current encoding, so disable any translation, #7209
21570+
# test.data.table() sets options(encoding="UTF-8"), so go the long way around.
21571+
ff = file(f, encoding = "")
21572+
tryCatch(
21573+
writeLines(c('a', rep('0x1.ffffp0', 10000L), `Encoding<-`('0x1.ff\x9fp0', 'bytes'), rep('0x1.ffffp0', 20000L)), ff),
21574+
finally = close(ff)
21575+
)
21576+
test(2334, names(fread(f)), "a")
21577+
})
2156121578

2156221579
# Tests for new isoyear() helper (complement to isoweek) #7154
2156321580
test(2335.1, isoyear(as.IDate("2019-12-30")), 2020L) # End of year edge case
@@ -21567,18 +21584,28 @@ test(2335.4, isoyear(as.IDate(c("2019-12-30", "2016-01-01", "2023-08-15"))),c(20
2156721584
test(2335.5, isoyear("2019-12-30"), 2020L)
2156821585
test(2335.6, isoyear(as.Date("2019-12-30")), 2020L)
2156921586

21587+
# t1-t2 for Date/IDate should be consistent, modulo storage mode #4979
21588+
t1 = as.IDate("2025-07-01")
21589+
t2 = as.IDate("2025-06-01")
21590+
test(2336.1, all.equal(as.Date(t1) - as.Date(t2), t1 - t2))
21591+
test(2336.2, all.equal(as.Date(t2) - as.Date(t1), t2 - t1))
21592+
test(2336.3, all.equal(as.Date(t1) - t2, t1 - t2))
21593+
test(2336.4, all.equal(as.Date(t2) - t1, t2 - t1))
21594+
test(2336.5, all.equal(t1 - as.Date(t2), t1 - t2))
21595+
test(2336.6, all.equal(t2 - as.Date(t1), t2 - t1))
21596+
2157021597
# 2864 force decimal points for whole numbers in numeric columns
2157121598
dd = data.table(x=c(1, 2, 3))
2157221599
di = data.table(x=c(1L, 2L, 3L))
21573-
test(2336.1, capture.output(fwrite(dd, forceDecimal=TRUE)), c("x", "1.", "2.", "3."))
21574-
test(2336.2, capture.output(fwrite(dd, forceDecimal=TRUE, dec=",", sep="\t")), c("x", "1,", "2,", "3,"))
21575-
test(2336.3, capture.output(fwrite(dd, forceDecimal=FALSE)), c("x", "1", "2", "3"))
21576-
test(2336.4, capture.output(fwrite(di, forceDecimal=TRUE)), c("x", "1", "2", "3"))
21577-
test(2336.5, capture.output(fwrite(data.table(x=c(0.)), forceDecimal=TRUE)), c("x", "0."))
21578-
test(2336.6, capture.output(fwrite(data.table(x=c(-0.)), forceDecimal=TRUE)), c("x", "0."))
21600+
test(2337.1, capture.output(fwrite(dd, forceDecimal=TRUE)), c("x", "1.", "2.", "3."))
21601+
test(2337.2, capture.output(fwrite(dd, forceDecimal=TRUE, dec=",", sep="\t")), c("x", "1,", "2,", "3,"))
21602+
test(2337.3, capture.output(fwrite(dd, forceDecimal=FALSE)), c("x", "1", "2", "3"))
21603+
test(2337.4, capture.output(fwrite(di, forceDecimal=TRUE)), c("x", "1", "2", "3"))
21604+
test(2337.5, capture.output(fwrite(data.table(x=c(0.)), forceDecimal=TRUE)), c("x", "0."))
21605+
test(2337.6, capture.output(fwrite(data.table(x=c(-0.)), forceDecimal=TRUE)), c("x", "0."))
2157921606
# round trip
2158021607
local({
2158121608
f <- tempfile(); on.exit(unlink(f))
21582-
test(2336.7, {fwrite(dd, f, forceDecimal=TRUE); fread(f)}, dd)
21583-
test(2336.8, {fwrite(dd, f, forceDecimal=FALSE); fread(f)}, di)
21609+
test(2337.7, {fwrite(dd, f, forceDecimal=TRUE); fread(f)}, dd)
21610+
test(2337.8, {fwrite(dd, f, forceDecimal=FALSE); fread(f)}, di)
2158421611
})

man/IDateTime.Rd

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,13 @@ as.IDate(x, \dots)
7070
\method{as.IDate}{Date}(x, \dots)
7171
\method{as.Date}{IDate}(x, \dots)
7272
\method{as.POSIXct}{IDate}(x, tz = "UTC", time = 0, \dots)
73-
\method{round}{IDate}(x, digits = c("weeks", "months", "quarters","years"), \ldots)
73+
\method{round}{IDate}(x, digits = c("weeks", "months", "quarters","years"), ...)
7474

7575
as.ITime(x, \dots)
7676
\method{as.ITime}{default}(x, \dots)
7777
\method{as.ITime}{POSIXlt}(x, ms = 'truncate', \dots)
78-
\method{round}{ITime}(x, digits = c("hours", "minutes"), \ldots)
79-
\method{trunc}{ITime}(x, units = c("hours", "minutes"), \ldots)
78+
\method{round}{ITime}(x, digits = c("hours", "minutes"), ...)
79+
\method{trunc}{ITime}(x, units = c("hours", "minutes"), ...)
8080

8181
\method{as.POSIXct}{ITime}(x, tz = "UTC", date = Sys.Date(), \dots)
8282
\method{as.character}{ITime}(x, \dots)

man/between.Rd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
\alias{\%between\%}
44
\alias{inrange}
55
\alias{\%inrange\%}
6-
\title{ Convenience functions for range subsets. }
6+
\title{ Convenience functions for range subsets }
77
\description{
88
Intended for use in \code{i} in \code{[.data.table}.
99

0 commit comments

Comments
 (0)