diff --git a/NEWS.md b/NEWS.md index c586401d8..cb2148c30 100644 --- a/NEWS.md +++ b/NEWS.md @@ -12,6 +12,8 @@ ## NOTES +1. `as.IDate`, `as.ITime`, `second`, `minute`, and `hour` now recognize UTC equivalents for speed: GMT, GMT-0, GMT+0, GMT0, Etc/GMT, and Etc/UTC, [#4116](https://github.com/Rdatatable/data.table/issues/4116). + # data.table [v1.12.8](https://github.com/Rdatatable/data.table/milestone/15?closed=1) (09 Dec 2019) diff --git a/R/IDateTime.R b/R/IDateTime.R index 265d57ffb..1e5f27e5a 100644 --- a/R/IDateTime.R +++ b/R/IDateTime.R @@ -33,11 +33,10 @@ as.IDate.Date = function(x, ...) { } as.IDate.POSIXct = function(x, tz = attr(x, "tzone", exact=TRUE), ...) { - if (is.null(tz)) tz='' - if (tz %chin% c("UTC", "GMT")) { + if (is_utc(tz)) (setattr(as.integer(as.numeric(x) %/% 86400L), "class", c("IDate", "Date"))) # %/% returns new object so can use setattr() on it; wrap with () to return visibly - } else - as.IDate(as.Date(x, tz = tz, ...)) + else + as.IDate(as.Date(x, tz = if (is.null(tz)) '' else tz, ...)) } as.IDate.IDate = function(x, ...) x @@ -138,9 +137,8 @@ as.ITime.default = function(x, ...) { } as.ITime.POSIXct = function(x, tz = attr(x, "tzone", exact=TRUE), ...) { - if (is.null(tz)) tz='' - if (tz %chin% c("UTC", "GMT")) as.ITime(unclass(x), ...) - else as.ITime(as.POSIXlt(x, tz = tz, ...), ...) + if (is_utc(tz)) as.ITime(unclass(x), ...) + else as.ITime(as.POSIXlt(x, tz = if (is.null(tz)) '' else tz, ...), ...) } as.ITime.numeric = function(x, ms = 'truncate', ...) { @@ -305,19 +303,19 @@ as.POSIXlt.ITime = function(x, ...) { second = function(x) { # if we know the object is in UTC, can calculate the hour much faster - if (inherits(x, 'POSIXct') && identical(attr(x, 'tzone', exact=TRUE), 'UTC')) return(as.integer(as.numeric(x) %% 60L)) + if (inherits(x, 'POSIXct') && is_utc(attr(x, 'tzone', exact=TRUE))) return(as.integer(as.numeric(x) %% 60L)) if (inherits(x, 'ITime')) return(as.integer(x) %% 60L) as.integer(as.POSIXlt(x)$sec) } minute = function(x) { # ever-so-slightly faster than x %% 3600L %/% 60L - if (inherits(x, 'POSIXct') && identical(attr(x, 'tzone', exact=TRUE), 'UTC')) return(as.integer(as.numeric(x) %/% 60L %% 60L)) + if (inherits(x, 'POSIXct') && is_utc(attr(x, 'tzone', exact=TRUE))) return(as.integer(as.numeric(x) %/% 60L %% 60L)) if (inherits(x, 'ITime')) return(as.integer(x) %/% 60L %% 60L) as.POSIXlt(x)$min } hour = function(x) { # ever-so-slightly faster than x %% 86400L %/% 3600L - if (inherits(x, 'POSIXct') && identical(attr(x, 'tzone', exact=TRUE), 'UTC')) return(as.integer(as.numeric(x) %/% 3600L %% 24L)) + if (inherits(x, 'POSIXct') && is_utc(attr(x, 'tzone', exact=TRUE))) return(as.integer(as.numeric(x) %/% 3600L %% 24L)) if (inherits(x, 'ITime')) return(as.integer(x) %/% 3600L %% 24L) as.POSIXlt(x)$hour } diff --git a/R/utils.R b/R/utils.R index de35e709c..7ef988912 100644 --- a/R/utils.R +++ b/R/utils.R @@ -117,6 +117,14 @@ do_patterns = function(pat_sub, all_cols) { return(matched) } +# check UTC status +is_utc = function(tz) { + # via grep('UTC|GMT', OlsonNames(), value = TRUE) + utc_tz = c("Etc/GMT", "Etc/UTC", "GMT", "GMT-0", "GMT+0", "GMT0", "UTC") + if (is.null(tz)) tz = Sys.timezone() + return(tz %chin% utc_tz) +} + # nocov start #593 always return a data.table edit.data.table = function(name, ...) { setDT(NextMethod('edit', name))[] diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 80f2bc25e..950e58161 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -11125,7 +11125,7 @@ test(1764.2, format(structure(NA_integer_, class = "ITime")), NA_character_) # IDateTime error when tzone is NULL, #1973 x = as.POSIXct('2017-03-17', tz="UTC") attr(x, 'tzone') = NULL -test(1765, print(IDateTime(x)), output=".*idate.*itime.*1: 2017-03-17") +test(1765, print(IDateTime(x)), output=".*idate.*itime.*1: 2017-03-1[67]") # print(null.data.table()) should not output NULL as well, #1852 # use capture.output() in this case rather than output= to ensure NULL is not output