Skip to content

Commit 31a7c4f

Browse files
authored
[read] reduce date/datetime differences to releases prior 1.15 (#1336)
* [write] treat difftime as vector * [read] reduce differences to releases < 1.15 if a date is detected in a mixed type column, return the date as character and not the unix time stamp * [read] guard date conversions
1 parent ff584f4 commit 31a7c4f

File tree

4 files changed

+97
-7
lines changed

4 files changed

+97
-7
lines changed

R/dates.R

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,19 @@
2929
#' x <- 0.50918982
3030
#' convert_hms(x)
3131
convert_date <- function(x, origin = "1900-01-01", ...) {
32-
out <- date_to_unix(x, origin = origin, datetime = FALSE)
32+
sel <- is_charnum(x)
33+
out <- x
34+
out[sel] <- date_to_unix(x[sel], origin = origin, datetime = FALSE)
3335
out <- as.double(out)
3436
.Date(out)
3537
}
3638

3739
#' @rdname convert_date
3840
#' @export
3941
convert_datetime <- function(x, origin = "1900-01-01", ...) {
40-
out <- date_to_unix(x, origin = origin, datetime = TRUE)
42+
sel <- is_charnum(x)
43+
out <- x
44+
out[sel] <- date_to_unix(x[sel], origin = origin, datetime = TRUE)
4145
out <- as.double(out)
4246
tz <- ifelse(!is.null(tz <- list(...)$tz), tz, "UTC")
4347
.POSIXct(out, tz)

R/read.R

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ convert_df <- function(z, types, date_conv, datetime_conv, hms_conv, as_characte
2121
if (length(dtes)) z[dtes] <- lapply(z[dtes], date_conv_c)
2222
if (length(poxs)) z[poxs] <- lapply(z[poxs], datetime_conv_c)
2323
if (length(logs)) z[logs] <- lapply(z[logs], function(i) as.character(as.logical(i)))
24-
if (isNamespaceLoaded("hms")) z[difs] <- lapply(z[difs], hms_conv_c)
24+
if (length(difs)) z[difs] <- lapply(z[difs], hms_conv_c)
2525
} else {
2626
if (length(nums)) z[nums] <- lapply(z[nums], function(i) as.numeric(replace(i, i == "#NUM!", "NaN")))
2727
if (length(dtes)) z[dtes] <- lapply(z[dtes], date_conv)
2828
if (length(poxs)) z[poxs] <- lapply(z[poxs], datetime_conv)
2929
if (length(logs)) z[logs] <- lapply(z[logs], as.logical)
30-
if (isNamespaceLoaded("hms")) z[difs] <- lapply(z[difs], hms_conv)
30+
if (length(difs)) z[difs] <- lapply(z[difs], hms_conv)
3131
}
3232

3333
for (i in seq_along(z)) { # convert df to class formula
@@ -477,11 +477,11 @@ wb_to_df <- function(
477477

478478
if (any(uccs %in% xlsx_hms_style)) {
479479
sel <- cc$c_s %in% xlsx_hms_style & !cc$is_string & cc$v != ""
480-
if (isNamespaceLoaded("hms")) {
480+
if (convert) {
481481
# if hms is loaded, we have to avoid applying convert_hms() twice
482482
cc$val[sel] <- cc$v[sel]
483483
} else {
484-
cc$val[sel] <- suppressWarnings(as.character(convert_hms(cc$v[sel])))
484+
cc$val[sel] <- as.character(convert_hms(cc$v[sel]))
485485
}
486486
cc$typ[sel] <- "h"
487487
}
@@ -713,6 +713,36 @@ wb_to_df <- function(
713713
# could make it optional or explicit
714714
if (convert) {
715715
z <- convert_df(z, types, date_conv, datetime_conv, hms_conv)
716+
717+
## this reduces the difference to releases < 1.15. If in mixed columns
718+
## conversion to date fails and a character frame is returned, we return
719+
## a character instead of the unix time stamp as character.
720+
if (detect_dates) {
721+
date_conv_c <- function(...) as.character(date_conv(...))
722+
datetime_conv_c <- function(...) as.character(datetime_conv(...))
723+
hms_conv_c <- function(...) as.character(hms_conv(...))
724+
725+
sel <- !is.na(names(types))
726+
# update only if types is character
727+
chrs <- names(which(types[sel] == 0))
728+
729+
for (chr in chrs) {
730+
sel <- tt[[chr]] == "d" & !is.na(z[[chr]])
731+
if (length(sel)) {
732+
z[[chr]][sel] <- vapply(z[[chr]][sel], date_conv_c, NA_character_)
733+
}
734+
735+
sel <- tt[[chr]] == "p" & !is.na(z[[chr]])
736+
if (length(sel)) {
737+
z[[chr]][sel] <- vapply(z[[chr]][sel], datetime_conv_c, NA_character_)
738+
}
739+
740+
sel <- tt[[chr]] == "h" & !is.na(z[[chr]])
741+
if (length(sel)) {
742+
z[[chr]][sel] <- vapply(z[[chr]][sel], hms_conv_c, NA_character_)
743+
}
744+
}
745+
}
716746
}
717747

718748
# column names were picked earlier

R/write.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,7 @@ write_data_table <- function(
10161016
if (transpose) x <- transpose_df(x)
10171017
}
10181018

1019-
if (is.vector(x) || is.factor(x) || inherits(x, "Date") || inherits(x, "POSIXt") || inherits(x, "character")) {
1019+
if (is.vector(x) || is.factor(x) || inherits(x, "Date") || inherits(x, "POSIXt") || inherits(x, "difftime") || inherits(x, "character")) {
10201020
colNames <- FALSE
10211021
} ## this will go to coerce.default and rowNames will be ignored
10221022

tests/testthat/test-date_time_conversion.R

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,59 @@ test_that("date conversion works", {
194194
got <- rownames(df)
195195
expect_equal(exp, got)
196196
})
197+
198+
test_that("conversion works", {
199+
200+
wb <- wb_workbook()$add_worksheet()
201+
# row 1 column name
202+
wb$add_data(dims = "A1", x = "Var1")
203+
wb$add_data(dims = "B1", x = "Var2")
204+
wb$add_data(dims = "C1", x = "Var3")
205+
# row 2 character
206+
wb$add_data(dims = "A2", x = "2024-01-31")
207+
wb$add_data(dims = "B2", x = "2024-01-31")
208+
wb$add_data(dims = "C2", x = "2024-01-31")
209+
# various dates
210+
wb$add_data(dims = "A3", x = as.Date("2024-02-01"))
211+
wb$add_data(dims = "B3", x = structure(1234, units = "secs", class = c("hms", "difftime")))
212+
wb$add_data(dims = "C3", x = as.POSIXct("2024-04-25 08:47:03", tz = "UTC"))
213+
214+
exp <- structure(
215+
list(
216+
Var1 = c("2024-01-31", "2024-02-01"),
217+
Var2 = c("2024-01-31", "00:20:34"),
218+
Var3 = c("2024-01-31", "2024-04-25 08:47:03")
219+
), row.names = 2:3, class = "data.frame")
220+
got <- wb$to_df()
221+
expect_equal(exp, got)
222+
223+
got <- wb$to_df(convert = FALSE)
224+
expect_equal(exp, got)
225+
226+
exp <- structure(
227+
list(
228+
`2024-01-31` = structure(19754, class = "Date"),
229+
`2024-01-31` = "00:20:34",
230+
`2024-01-31` = structure(1714034823, class = c("POSIXct", "POSIXt"), tzone = "UTC")
231+
), row.names = 3L, class = "data.frame")
232+
233+
got <- wb$to_df(start_row = 2)
234+
expect_equal(exp, got)
235+
236+
exp <- structure(
237+
list(
238+
`2024-01-31` = "2024-02-01",
239+
`2024-01-31` = "00:20:34",
240+
`2024-01-31` = "2024-04-25 08:47:03"
241+
), row.names = 3L, class = "data.frame")
242+
got <- wb$to_df(start_row = 2, convert = FALSE)
243+
expect_equal(exp, got)
244+
245+
exp <- structure(c(NA, 19838), class = "Date")
246+
expect_warning(got <- wb$to_df(types = c(Var3 = "Date")), "coercion")
247+
expect_equal(exp, got$Var3)
248+
249+
exp <- structure(c(NA, 1706745600), class = c("POSIXct", "POSIXt"), tzone = "UTC")
250+
expect_warning(got <- wb$to_df(types = c(Var1 = "POSIXct")), "coercion")
251+
expect_equal(exp, got$Var1)
252+
})

0 commit comments

Comments
 (0)