Skip to content

Commit d6222f7

Browse files
committed
refactored type checking and coercion
1 parent 3cce1ce commit d6222f7

File tree

1 file changed

+57
-9
lines changed

1 file changed

+57
-9
lines changed

R/utils.R

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,64 @@ has_star <- function(x) {
4444
stringr::str_detect(x, "[*x?]")
4545
}
4646

47+
#' Helper for coercion to obtain decent messages when coercion does not work
48+
#' @noRd
49+
check_coerce <- function(x, cofun, atomicclass) {
50+
result <- suppressWarnings(do.call(cofun, list(x)))
51+
failures = is.na(result) & !is.na(x)
52+
if (any(failures)) {
53+
wrong <- paste0("'", x[failures], "'", collapse = ", ")
54+
rlang::warn(c(glue::glue("Expected {atomicclass} values but obtained {wrong}"), i="Replacing with NA"), use_cli_format = TRUE)
55+
}
56+
return(result)
57+
}
58+
59+
#' Helper function for coercion to date
60+
#' Dates are evil
61+
#' @noRd
62+
asdate <- function(x) {
63+
# Excel origin date
64+
excel_origin <- "1899-12-30"
65+
66+
if (is.integer(x) || is.numeric(x)) {
67+
return(as.Date(as.integer(x), origin = excel_origin))
68+
}
69+
70+
if (inherits(x, "POSIXct") || inherits(x, "Date")) {
71+
return(as.Date(x))
72+
}
73+
74+
if (is.character(x)) {
75+
trynumeric <- suppressMessages(as.numeric(x))
76+
if (!anyNA(trynumeric)) {
77+
return(as.Date(as.integer(trynumeric), origin = excel_origin))
78+
} else {
79+
# Try to parse as Date, catch errors
80+
tryCatch(
81+
as.Date(x),
82+
error = function(e) {
83+
rlang::warn(c(
84+
glue::glue("Can't convert character '{x}' to Date: {e$message}"),
85+
"i" = "Returning NA"
86+
), use_cli_format = TRUE)
87+
as.Date(NA)
88+
}
89+
)
90+
}
91+
} else {
92+
rlang::warn(c(
93+
glue::glue("Can't convert object of class {class(x)} to Date"),
94+
"i" = "Returning NA"
95+
), use_cli_format = TRUE)
96+
as.Date(NA)
97+
}
98+
}
4799

48100
#' Coerce a character vector based on atomicclass
49101
#' @param x A character vector
50102
#' @param atomicclass A character string indicating the atomic class
51103
#' @description
52-
#' About date conversion: read_excel() reads dates as the correct POSIXct object
104+
#' About date conversion: read_excel() reads dates as the correct POSIXct object
53105
#' when the excel field is formatted as a date. However, if the field is formatted
54106
#' as a number, it will be read as a numeric value. In this case, the conversion to
55107
#' a date object must be performed using the as.Date() function. The origin
@@ -70,13 +122,9 @@ has_star <- function(x) {
70122
coerce <- function(x, atomicclass) {
71123
switch(atomicclass,
72124
"character" = as.character(x),
73-
"numeric" = as.numeric(x),
74-
"integer" = as.integer(x),
75-
"logical" = as.logical(x),
76-
"date" = if (inherits(x, "POSIXct") || inherits(x, "Date")) {
77-
as.Date(x)
78-
} else {
79-
as.Date(as.integer(x), origin="1899-12-30")
80-
},
125+
"numeric" = check_coerce(x, "as.numeric", "numeric"),
126+
"integer" = check_coerce(x, "as.integer", "integer"),
127+
"logical" = check_coerce(x, "as.logical", "logical"),
128+
"date" = check_coerce(x, "asdate", "date"),
81129
)
82130
}

0 commit comments

Comments
 (0)