From 9bf4836c56601dbdbccc84815c72261bb2faf88e Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Thu, 18 Oct 2018 20:44:38 -0400 Subject: [PATCH 01/21] Add excel_time_to_numeric to convert many time formats to seconds --- NAMESPACE | 1 + NEWS.md | 2 + R/excel_dates.R | 1 + R/excel_time_to_numeric.R | 155 ++++++++++++++++++++ man/excel_numeric_to_date.Rd | 3 + man/excel_time_to_numeric.Rd | 38 +++++ tests/testthat/test-excel_time_to_numeric.R | 143 ++++++++++++++++++ 7 files changed, 343 insertions(+) create mode 100644 R/excel_time_to_numeric.R create mode 100644 man/excel_time_to_numeric.Rd create mode 100644 tests/testthat/test-excel_time_to_numeric.R diff --git a/NAMESPACE b/NAMESPACE index 0cec4360..a8d2c38a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -20,6 +20,7 @@ export(clean_names) export(convert_to_NA) export(crosstab) export(excel_numeric_to_date) +export(excel_time_to_numeric) export(get_dupes) export(make_clean_names) export(remove_empty) diff --git a/NEWS.md b/NEWS.md index a380715c..499360e5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,8 @@ The new function `make_clean_names()` takes a character vector and returns the c This new function can be supplied as a value for the `.name_repair` argument of `as_tibble()` in the latest development version of the `tibble` package (on GitHub as of September 2019). For example: `as_tibble(iris, .name_repair = make_clean_names)`. +The new function `excel_time_to_numeric()` converts times from Excel that do not have accompanying dates into a number of seconds. (#245, thanks to **@billdenney** for the feature.) + ## Minor features * `excel_numeric_to_date()` now returns a POSIXct object and includes a time zone. (#225, thanks to **@billdenney** for the feature.) diff --git a/R/excel_dates.R b/R/excel_dates.R index 94ac32cb..1ff0995d 100644 --- a/R/excel_dates.R +++ b/R/excel_dates.R @@ -17,6 +17,7 @@ #' Windows (as described in #' https://support.microsoft.com/en-us/help/2722715/support-for-the-leap-second). #' @export +#' @seealso \code{\link{excel_time_to_numeric}} #' @examples #' excel_numeric_to_date(40000) #' excel_numeric_to_date(40000.5) # No time is included diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R new file mode 100644 index 00000000..f1012378 --- /dev/null +++ b/R/excel_time_to_numeric.R @@ -0,0 +1,155 @@ +#' Convert a time that may be inconsistently or inconveniently formatted from +#' Microsoft Excel to a numeric number of seconds within a day. +#' +#' @details +#' +#' \code{time_value} may be one of the following formats: +#' \itemize{ +#' \item{numeric}{The input must be a value from 0 to 1 (exclusive of 1); this value is returned as-is.} +#' \item{POSIXlt or POSIXct}{The input must be on the day 1899-12-31 (any other day is an error). The time of day is extracted and converted to a fraction of a day.} +#' \item{character}{Any of the following (or a mixture of the choices):} +#' \itemize{ +#' \item{A character string that is a number between 0 and 1 (exclusive of 1), converted like a numeric value.} +#' \item{A character string that looks like a date on 1899-12-31 (specifically, it must start with \code{"1899-12-31 "}), converted like a POSIXct object.} +#' \item{A character string that looks like a time. Choices are 12-hour time ""} +#' } +#' } +#' +#' @param time_value A vector of values to convert (see Details) +#' @param round_seconds Should the output number of seconds be rounded to an +#' integer? +#' @return A vector of numbers >= 0 and <86400 +#' @seealso \code{\link{excel_numeric_to_date}} +#' @export +excel_time_to_numeric <- function(time_value, round_seconds=TRUE) { + UseMethod("excel_time_to_numeric") +} + +excel_time_to_numeric.logical <- function(time_value, round_seconds=TRUE) { + if (all(is.na(time_value))) { + rep(NA_real_, length(time_value)) + } else { + stop("If given as a logical vector, all values must be ") + } +} + +excel_time_to_numeric.numeric <- function(time_value, round_seconds=TRUE) { + if (all(is.na(time_value) | + time_value >= 0 & + time_value < 1)) { + seconds <- time_value*86400 + if (round_seconds) { + seconds <- round(seconds) + } + } else { + stop("When numeric, all `time_value`s must be between 0 and 1 (exclusive of 1)") + } + seconds +} + +excel_time_to_numeric.POSIXct <- function(time_value, round_seconds=TRUE) { + # using trunc removes timezone inconsistency. Timezones aren't used in Excel. + seconds <- as.numeric(time_value) - as.numeric(trunc(time_value, units="days")) + mask_good_seconds <- is.na(seconds) | (seconds >= 0 & seconds < 86400) + if (all(mask_good_seconds)) { + if (round_seconds) { + seconds <- round(seconds) + } + } else { + stop(sum(!mask_good_seconds), " `time_value`s were not on the date 1899-12-31.") + } + seconds +} + +excel_time_to_numeric.POSIXlt <- function(time_value, round_seconds=TRUE) { + excel_time_to_numeric.POSIXct( + as.POSIXct(time_value), + round_seconds = round_seconds + ) +} + +excel_time_to_numeric.character <- function(time_value, round_seconds=TRUE) { + ret <- rep(NA_real_, length(time_value)) + patterns <- + list( + number="^0(\\.[0-9]*)?$", + "12hr"="^([0]?[1-9]|1[0-2]):([0-5][0-9])(?::([0-5][0-9]))? ?([AP]M)$", + "24hr"="^([0-1]?[0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9]))?$", + # The ".*?" at the end of POSIX is to allow for a time zone, but it allows + # for imperfect parsing if there were just a date and a space. + # The the entire time is optional to allow for midnight which shows as + # just the date and time zone. + POSIX="1899-12-31 (?:([0-1]?[0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9]))?)?.*?$" + ) + mask_na <- is.na(time_value) + mask_number <- grepl(pattern=patterns$number, x=time_value) + mask_POSIX <- grepl(pattern=patterns[["POSIX"]], x=time_value) + mask_12hr <- grepl(pattern=patterns[["12hr"]], x=time_value, ignore.case=TRUE) + mask_24hr <- grepl(pattern=patterns[["24hr"]], x=time_value) + unmatched <- !(mask_na | mask_number | mask_POSIX | mask_12hr | mask_24hr) + if (any(unmatched)) { + stop( + "The following character strings did not match an interpretable ", + "character format for time conversion: ", + paste(unique(time_value[unmatched])) + ) + } + if (any(mask_number)) { + ret[mask_number] <- + excel_time_to_numeric.numeric( + time_value=as.numeric(time_value[mask_number]), + round_seconds=round_seconds + ) + } + mask_clock <- mask_12hr | mask_24hr | mask_POSIX + if (any(mask_clock)) { + hours <- minutes <- seconds <- rep(NA_real_, length(time_value)) + if (any(mask_POSIX)) { + hours[mask_POSIX] <- + gsub(pattern=patterns$POSIX, replacement="\\1", x=time_value[mask_POSIX]) + minutes[mask_POSIX] <- + gsub(pattern=patterns$POSIX, replacement="\\2", x=time_value[mask_POSIX]) + seconds[mask_POSIX] <- + gsub(pattern=patterns$POSIX, replacement="\\3", x=time_value[mask_POSIX]) + } + if (any(mask_12hr)) { + mask_pm <- rep(FALSE, length(time_value)) + hours[mask_12hr] <- + gsub(pattern=patterns[["12hr"]], replacement="\\1", x=time_value[mask_12hr], ignore.case=TRUE) + minutes[mask_12hr] <- + gsub(pattern=patterns[["12hr"]], replacement="\\2", x=time_value[mask_12hr], ignore.case=TRUE) + seconds[mask_12hr] <- + gsub(pattern=patterns[["12hr"]], replacement="\\3", x=time_value[mask_12hr], ignore.case=TRUE) + # 12 is 0 hours in the AM and the PM conversion below adds the needed 12 + # at noon. + mask_0_hours <- mask_12hr & (hours %in% "12") + hours[mask_0_hours] <- "0" + mask_pm[mask_12hr] <- + mask_12hr & + tolower( + gsub(pattern=patterns[["12hr"]], replacement="\\4", x=time_value[mask_12hr], ignore.case=TRUE) + ) %in% "pm" + hours[mask_pm] <- 12 + as.numeric(hours[mask_pm]) + } + if (any(mask_24hr)) { + hours[mask_24hr] <- + gsub(pattern=patterns[["24hr"]], replacement="\\1", x=time_value[mask_24hr]) + minutes[mask_24hr] <- + gsub(pattern=patterns[["24hr"]], replacement="\\2", x=time_value[mask_24hr]) + seconds[mask_24hr] <- + gsub(pattern=patterns[["24hr"]], replacement="\\3", x=time_value[mask_24hr]) + } + hours[hours %in% ""] <- "0" + minutes[minutes %in% ""] <- "0" + seconds[seconds %in% ""] <- "0" + + ret[mask_clock] <- + as.numeric(hours[mask_clock]) * 3600 + + as.numeric(minutes[mask_clock]) * 60 + + as.numeric(seconds[mask_clock]) + } + if (round_seconds) { + ret <- round(ret) + } + ret +} diff --git a/man/excel_numeric_to_date.Rd b/man/excel_numeric_to_date.Rd index d9df90e1..dff89a5a 100644 --- a/man/excel_numeric_to_date.Rd +++ b/man/excel_numeric_to_date.Rd @@ -41,3 +41,6 @@ excel_numeric_to_date(40000.521, include_time = TRUE) # Time is included excel_numeric_to_date(40000.521, include_time = TRUE, round_seconds = FALSE) # Time with fractional seconds is included } +\seealso{ +\code{\link{excel_time_to_numeric}} +} diff --git a/man/excel_time_to_numeric.Rd b/man/excel_time_to_numeric.Rd new file mode 100644 index 00000000..93b1018d --- /dev/null +++ b/man/excel_time_to_numeric.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/excel_time_to_numeric.R +\name{excel_time_to_numeric} +\alias{excel_time_to_numeric} +\title{Convert a time that may be inconsistently or inconveniently formatted from +Microsoft Excel to a numeric number of seconds within a day.} +\usage{ +excel_time_to_numeric(time_value, round_seconds = TRUE) +} +\arguments{ +\item{time_value}{A vector of values to convert (see Details)} + +\item{round_seconds}{Should the output number of seconds be rounded to an +integer?} +} +\value{ +A vector of numbers >= 0 and <86400 +} +\description{ +Convert a time that may be inconsistently or inconveniently formatted from +Microsoft Excel to a numeric number of seconds within a day. +} +\details{ +\code{time_value} may be one of the following formats: +\itemize{ + \item{numeric}{The input must be a value from 0 to 1 (exclusive of 1); this value is returned as-is.} + \item{POSIXlt or POSIXct}{The input must be on the day 1899-12-31 (any other day is an error). The time of day is extracted and converted to a fraction of a day.} + \item{character}{Any of the following (or a mixture of the choices):} + \itemize{ + \item{A character string that is a number between 0 and 1 (exclusive of 1), converted like a numeric value.} + \item{A character string that looks like a date on 1899-12-31 (specifically, it must start with \code{"1899-12-31 "}), converted like a POSIXct object.} + \item{A character string that looks like a time. Choices are 12-hour time ""} + } +} +} +\seealso{ +\code{\link{excel_numeric_to_date}} +} diff --git a/tests/testthat/test-excel_time_to_numeric.R b/tests/testthat/test-excel_time_to_numeric.R new file mode 100644 index 00000000..a277b095 --- /dev/null +++ b/tests/testthat/test-excel_time_to_numeric.R @@ -0,0 +1,143 @@ +context("Excel time cleaning") + +test_that("excel_time_to_numeric numbers function correctly", { + expect_equal(excel_time_to_numeric(0.1), 8640) + expect_equal(excel_time_to_numeric(0.1000000001), 8640) + expect_equal(excel_time_to_numeric(0), 0) + expect_equal(excel_time_to_numeric(NA_real_), NA_real_) +}) + +test_that("excel_time_to_numeric POSIX objects extract the correct part of the time", { + expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 08:00")), 8*3600) + expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:00")), 13*3600) + expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:05:10")), 13*3600 + 5*60 + 10) +}) + +test_that("excel_time_to_numeric POSIX objects ignore the time zone", { + expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:00", tz="EST")), 13*3600) + expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:00", tz="UTC")), 13*3600) + expect_equal( + excel_time_to_numeric( + as.POSIXct(c("1899-12-31 13:00", "1899-12-31 13:00"), tz="EST") + ), + rep(13*3600, 2) + ) +}) + +test_that("excel_time_to_numeric numbers errors when out of range", { + expect_error(excel_time_to_numeric(1)) + expect_error(excel_time_to_numeric(-0.1)) +}) + +test_that("excel_time_to_numeric logical values return as expected", { + expect_equal(excel_time_to_numeric(NA), NA_real_) + expect_error(excel_time_to_numeric(c(NA, TRUE))) + expect_error(excel_time_to_numeric(TRUE)) +}) + +test_that("excel_time_to_numeric, character strings of numbers work as expected", { + expect_equal(excel_time_to_numeric("0.5"), 12*3600) + expect_equal(excel_time_to_numeric("0"), 0) + expect_equal(excel_time_to_numeric("0."), 0) + expect_equal(excel_time_to_numeric("0.000000"), 0) + expect_equal(excel_time_to_numeric("0.00001"), 1) + expect_equal( + excel_time_to_numeric("0.00001", round_seconds=FALSE), + 0.00001*86400 + ) +}) + +test_that("excel_time_to_numeric, am/pm times work", { + expect_equal(excel_time_to_numeric("8:00am"), 8*3600) + expect_equal(excel_time_to_numeric("8:00pm"), 20*3600) + expect_equal(excel_time_to_numeric("8:10am"), 8*3600 + 10*60) + expect_equal(excel_time_to_numeric("8:10:05am"), 8*3600 + 10*60 + 5) + expect_equal( + excel_time_to_numeric("12:10:05am"), 10*60 + 5, + info="After midnight is treated as 0 not 12." + ) + expect_equal( + excel_time_to_numeric("12:10:05pm"), 12*3600 + 10*60 + 5, + info="After noon is treated as 12." + ) +}) + +test_that("excel_time_to_numeric, am/pm times work case insensitively and with spaces", { + expect_equal( + excel_time_to_numeric("8:00am"), + excel_time_to_numeric("8:00AM") + ) + expect_equal( + excel_time_to_numeric("8:00am"), + excel_time_to_numeric("8:00Am") + ) + expect_equal( + excel_time_to_numeric("8:00am"), + excel_time_to_numeric("8:00aM") + ) + expect_equal( + excel_time_to_numeric("8:00am"), + excel_time_to_numeric("8:00 AM") + ) +}) + +test_that("excel_time_to_numeric, 24-hour times work (zero-padded hours or not)", { + expect_equal(excel_time_to_numeric("8:00"), 8*3600) + expect_equal(excel_time_to_numeric("08:00"), 8*3600) + expect_equal(excel_time_to_numeric("08:10"), 8*3600 + 10*60) + expect_equal(excel_time_to_numeric("8:10:05"), 8*3600 + 10*60 + 5) + expect_equal(excel_time_to_numeric("21:05"), 21*3600 + 5*60) + expect_equal(excel_time_to_numeric("21:05:20"), 21*3600 + 5*60 + 20) +}) + +test_that("excel_time_to_numeric, 24-hour times work (zero-padded hours or not)", { + expect_equal(excel_time_to_numeric("8:00"), 8*3600) + expect_equal(excel_time_to_numeric("08:00"), 8*3600) + expect_equal(excel_time_to_numeric("08:10"), 8*3600 + 10*60) + expect_equal(excel_time_to_numeric("8:10:05"), 8*3600 + 10*60 + 5) + expect_equal(excel_time_to_numeric("21:05"), 21*3600 + 5*60) + expect_equal(excel_time_to_numeric("0:05"), 5*60) + expect_equal(excel_time_to_numeric("00:05"), 5*60) + expect_equal(excel_time_to_numeric("21:05:20"), 21*3600 + 5*60 + 20) +}) + +test_that("excel_time_to_numeric, POSIX times on 1899-12-31 work", { + expect_equal(excel_time_to_numeric("1899-12-31 8:00"), 8*3600) + expect_equal(excel_time_to_numeric("1899-12-31 08:00"), 8*3600) + expect_equal(excel_time_to_numeric("1899-12-31 08:10"), 8*3600 + 10*60) + expect_equal(excel_time_to_numeric("1899-12-31 8:10:05"), 8*3600 + 10*60 + 5) + expect_equal(excel_time_to_numeric("1899-12-31 21:05"), 21*3600 + 5*60) + expect_equal(excel_time_to_numeric("1899-12-31 0:05"), 5*60) + expect_equal(excel_time_to_numeric("1899-12-31 00:05"), 5*60) + expect_equal(excel_time_to_numeric("1899-12-31 21:05:20"), 21*3600 + 5*60 + 20) +}) + +test_that("excel_time_to_numeric, POSIX times ignore extra text (which is hopefully a time zone)", { + expect_equal(excel_time_to_numeric("1899-12-31 8:00 foo"), 8*3600) + expect_equal(excel_time_to_numeric("1899-12-31 08:00 foo"), 8*3600) + expect_equal(excel_time_to_numeric("1899-12-31 08:10 foo"), 8*3600 + 10*60) + expect_equal(excel_time_to_numeric("1899-12-31 8:10:05 foo"), 8*3600 + 10*60 + 5) + expect_equal(excel_time_to_numeric("1899-12-31 21:05 foo"), 21*3600 + 5*60) + expect_equal(excel_time_to_numeric("1899-12-31 0:05 foo"), 5*60) + expect_equal(excel_time_to_numeric("1899-12-31 00:05 foo"), 5*60) + expect_equal(excel_time_to_numeric("1899-12-31 21:05:20 foo"), 21*3600 + 5*60 + 20) +}) + +test_that("excel_time_to_numeric, POSIX times treat no time as midnight but only if there is a space indicating a well-formed date-time object.", { + expect_equal(excel_time_to_numeric("1899-12-31 foo"), 0) + expect_error(excel_time_to_numeric("1899-12-31foo")) +}) + +test_that("excel_time_to_numeric, invalid character times trigger an error", { + expect_error(excel_time_to_numeric("1")) + expect_error(excel_time_to_numeric("-0.1")) + expect_error(excel_time_to_numeric("0:05:20am")) + expect_error(excel_time_to_numeric("1:60:20am")) + expect_error(excel_time_to_numeric("1:00:70am")) + expect_error(excel_time_to_numeric("13:05:20am")) + expect_error(excel_time_to_numeric("13:05:20am")) + expect_error(excel_time_to_numeric("25:05:20")) + expect_error(excel_time_to_numeric("23:65:20")) + expect_error(excel_time_to_numeric("23:05:90")) +}) + From 0a72be44311bc2f1e482e97039e5ea7d037d2f52 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Tue, 19 Feb 2019 13:15:25 -0500 Subject: [PATCH 02/21] Added tests --- tests/testthat/test-excel_time_to_numeric.R | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/testthat/test-excel_time_to_numeric.R b/tests/testthat/test-excel_time_to_numeric.R index a277b095..73dee03a 100644 --- a/tests/testthat/test-excel_time_to_numeric.R +++ b/tests/testthat/test-excel_time_to_numeric.R @@ -24,6 +24,13 @@ test_that("excel_time_to_numeric POSIX objects ignore the time zone", { ) }) +test_that("excel_time_to_numeric POSIXlt works like POSIXct", { + expect_equal( + excel_time_to_numeric(as.POSIXct("1899-12-31 13:00", tz="EST")), + excel_time_to_numeric(as.POSIXlt("1899-12-31 13:00", tz="EST")) + ) +}) + test_that("excel_time_to_numeric numbers errors when out of range", { expect_error(excel_time_to_numeric(1)) expect_error(excel_time_to_numeric(-0.1)) @@ -139,5 +146,6 @@ test_that("excel_time_to_numeric, invalid character times trigger an error", { expect_error(excel_time_to_numeric("25:05:20")) expect_error(excel_time_to_numeric("23:65:20")) expect_error(excel_time_to_numeric("23:05:90")) + expect_error(excel_time_to_numeric(as.POSIXct("1899-12-30 21:05:20"))) }) From 12dd48708ef6b42a6b79e902ad5dea37f3052582 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Fri, 15 Apr 2022 14:08:49 -0400 Subject: [PATCH 03/21] Rebuild docs, fix NEWS.md --- NEWS.md | 5 ++--- R/excel_time_to_numeric.R | 1 + man/convert_to_date.Rd | 1 + man/excel_numeric_to_date.Rd | 6 ++++-- man/excel_time_to_numeric.Rd | 6 ++++++ man/sas_numeric_to_date.Rd | 3 ++- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/NEWS.md b/NEWS.md index 054f8970..a5ff4a78 100644 --- a/NEWS.md +++ b/NEWS.md @@ -18,6 +18,8 @@ * A new function `sas_numeric_to_date()` has been added to convert SAS dates, times, and datetimes to R objects (fix #475, thanks to **@billdenney** for suggesting and implementing) +* The new function `excel_time_to_numeric()` converts times from Excel that do not have accompanying dates into a number of seconds. (#245, thanks to **@billdenney** for the feature.) + ## Minor features * Some warning messages now have classes so that they can be specifically suppressed with suppressWarnings(..., class="the_class_to_suppress"). To find the class of a warning you typically must look at the code where the error is occurring. (#452, thanks to **@mgacc0** for suggesting and **@billdenney** for fixing) @@ -146,9 +148,6 @@ This feature (#50) took almost 3 years from conception to implementation. Major * `remove_empty()` now has a companion function `remove_constant()` which removes columns containing only a single unique value, optionally ignoring `NA` (#222, thanks to **@billdenney** for suggesting & implementing). - -The new function `excel_time_to_numeric()` converts times from Excel that do not have accompanying dates into a number of seconds. (#245, thanks to **@billdenney** for the feature.) - ## Minor features * `excel_numeric_to_date()` now returns a POSIXct object and includes a time zone. (#225, thanks to **@billdenney** for the feature.) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index f1012378..8e53dd5f 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -19,6 +19,7 @@ #' @param round_seconds Should the output number of seconds be rounded to an #' integer? #' @return A vector of numbers >= 0 and <86400 +#' @family Date-time cleaning #' @seealso \code{\link{excel_numeric_to_date}} #' @export excel_time_to_numeric <- function(time_value, round_seconds=TRUE) { diff --git a/man/convert_to_date.Rd b/man/convert_to_date.Rd index 22935ae0..75014078 100644 --- a/man/convert_to_date.Rd +++ b/man/convert_to_date.Rd @@ -71,6 +71,7 @@ convert_to_datetime( \seealso{ Other Date-time cleaning: \code{\link{excel_numeric_to_date}()}, +\code{\link{excel_time_to_numeric}()}, \code{\link{sas_numeric_to_date}()} } \concept{Date-time cleaning} diff --git a/man/excel_numeric_to_date.Rd b/man/excel_numeric_to_date.Rd index 138159d3..47e223ed 100644 --- a/man/excel_numeric_to_date.Rd +++ b/man/excel_numeric_to_date.Rd @@ -63,10 +63,12 @@ excel_numeric_to_date(40000.521, include_time = TRUE) # Time is included excel_numeric_to_date(40000.521, include_time = TRUE, round_seconds = FALSE) # Time with fractional seconds is included } -\concept{Date-time cleaning} \seealso{ +\code{\link{excel_time_to_numeric}} + Other Date-time cleaning: \code{\link{convert_to_date}()}, +\code{\link{excel_time_to_numeric}()}, \code{\link{sas_numeric_to_date}()} -\code{\link{excel_time_to_numeric}} } +\concept{Date-time cleaning} diff --git a/man/excel_time_to_numeric.Rd b/man/excel_time_to_numeric.Rd index 93b1018d..e1c2dbcb 100644 --- a/man/excel_time_to_numeric.Rd +++ b/man/excel_time_to_numeric.Rd @@ -35,4 +35,10 @@ Microsoft Excel to a numeric number of seconds within a day. } \seealso{ \code{\link{excel_numeric_to_date}} + +Other Date-time cleaning: +\code{\link{convert_to_date}()}, +\code{\link{excel_numeric_to_date}()}, +\code{\link{sas_numeric_to_date}()} } +\concept{Date-time cleaning} diff --git a/man/sas_numeric_to_date.Rd b/man/sas_numeric_to_date.Rd index ad31d551..a54cd50a 100644 --- a/man/sas_numeric_to_date.Rd +++ b/man/sas_numeric_to_date.Rd @@ -38,6 +38,7 @@ SAS Date, Time, and Datetime Values reference (retrieved on \seealso{ Other Date-time cleaning: \code{\link{convert_to_date}()}, -\code{\link{excel_numeric_to_date}()} +\code{\link{excel_numeric_to_date}()}, +\code{\link{excel_time_to_numeric}()} } \concept{Date-time cleaning} From b8a071a83eb89e64228895caab5aa2e946b7325e Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Fri, 15 Apr 2022 14:36:49 -0400 Subject: [PATCH 04/21] Fix test issue --- NAMESPACE | 5 +++++ R/excel_time_to_numeric.R | 7 ++++++- tests/testthat/test-excel_time_to_numeric.R | 6 ++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 45a60970..efc036dc 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -8,6 +8,11 @@ S3method(clean_names,sf) S3method(clean_names,tbl_graph) S3method(describe_class,default) S3method(describe_class,factor) +S3method(excel_time_to_numeric,POSIXct) +S3method(excel_time_to_numeric,POSIXlt) +S3method(excel_time_to_numeric,character) +S3method(excel_time_to_numeric,logical) +S3method(excel_time_to_numeric,numeric) S3method(fisher.test,default) S3method(fisher.test,tabyl) S3method(print,tabyl) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index 8e53dd5f..fcaa15f4 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -26,6 +26,7 @@ excel_time_to_numeric <- function(time_value, round_seconds=TRUE) { UseMethod("excel_time_to_numeric") } +#' @export excel_time_to_numeric.logical <- function(time_value, round_seconds=TRUE) { if (all(is.na(time_value))) { rep(NA_real_, length(time_value)) @@ -34,6 +35,7 @@ excel_time_to_numeric.logical <- function(time_value, round_seconds=TRUE) { } } +#' @export excel_time_to_numeric.numeric <- function(time_value, round_seconds=TRUE) { if (all(is.na(time_value) | time_value >= 0 & @@ -48,6 +50,7 @@ excel_time_to_numeric.numeric <- function(time_value, round_seconds=TRUE) { seconds } +#' @export excel_time_to_numeric.POSIXct <- function(time_value, round_seconds=TRUE) { # using trunc removes timezone inconsistency. Timezones aren't used in Excel. seconds <- as.numeric(time_value) - as.numeric(trunc(time_value, units="days")) @@ -57,11 +60,12 @@ excel_time_to_numeric.POSIXct <- function(time_value, round_seconds=TRUE) { seconds <- round(seconds) } } else { - stop(sum(!mask_good_seconds), " `time_value`s were not on the date 1899-12-31.") + stop(sum(!mask_good_seconds), " `time_value`s were not at or above 0 and below 86400.") } seconds } +#' @export excel_time_to_numeric.POSIXlt <- function(time_value, round_seconds=TRUE) { excel_time_to_numeric.POSIXct( as.POSIXct(time_value), @@ -69,6 +73,7 @@ excel_time_to_numeric.POSIXlt <- function(time_value, round_seconds=TRUE) { ) } +#' @export excel_time_to_numeric.character <- function(time_value, round_seconds=TRUE) { ret <- rep(NA_real_, length(time_value)) patterns <- diff --git a/tests/testthat/test-excel_time_to_numeric.R b/tests/testthat/test-excel_time_to_numeric.R index 73dee03a..8077d282 100644 --- a/tests/testthat/test-excel_time_to_numeric.R +++ b/tests/testthat/test-excel_time_to_numeric.R @@ -130,7 +130,9 @@ test_that("excel_time_to_numeric, POSIX times ignore extra text (which is hopefu expect_equal(excel_time_to_numeric("1899-12-31 21:05:20 foo"), 21*3600 + 5*60 + 20) }) -test_that("excel_time_to_numeric, POSIX times treat no time as midnight but only if there is a space indicating a well-formed date-time object.", { +test_that("excel_time_to_numeric, POSIX times treat no time as midnight but only if there is a space indicating a mostly-well-formed date-time object.", { + # the just-a-space requirement is there because some time formatting puts the + # date then a space then the time zone. expect_equal(excel_time_to_numeric("1899-12-31 foo"), 0) expect_error(excel_time_to_numeric("1899-12-31foo")) }) @@ -146,6 +148,6 @@ test_that("excel_time_to_numeric, invalid character times trigger an error", { expect_error(excel_time_to_numeric("25:05:20")) expect_error(excel_time_to_numeric("23:65:20")) expect_error(excel_time_to_numeric("23:05:90")) - expect_error(excel_time_to_numeric(as.POSIXct("1899-12-30 21:05:20"))) + expect_error(excel_time_to_numeric("1899-12-30 21:05:20")) }) From 774a94e553b86704eeb6a85f7c4bca439008e186 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Fri, 15 Apr 2022 15:21:04 -0400 Subject: [PATCH 05/21] Improve test coverage by clarifying likely-unreachable code --- R/excel_time_to_numeric.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index fcaa15f4..6845aefe 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -60,7 +60,8 @@ excel_time_to_numeric.POSIXct <- function(time_value, round_seconds=TRUE) { seconds <- round(seconds) } } else { - stop(sum(!mask_good_seconds), " `time_value`s were not at or above 0 and below 86400.") + # This should be impossible except for leap seconds + stop(sum(!mask_good_seconds), " `time_value`s were not at or above 0 and below 86400.") # nocov } seconds } From dd8c94aa1c0734f067fa6f074da2c0b5fdfbd7f8 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Wed, 1 Nov 2023 14:52:01 -0400 Subject: [PATCH 06/21] Update docs --- man/excel_time_to_numeric.Rd | 16 ++++++++-------- man/janitor-package.Rd | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/man/excel_time_to_numeric.Rd b/man/excel_time_to_numeric.Rd index e1c2dbcb..6a80edef 100644 --- a/man/excel_time_to_numeric.Rd +++ b/man/excel_time_to_numeric.Rd @@ -23,14 +23,14 @@ Microsoft Excel to a numeric number of seconds within a day. \details{ \code{time_value} may be one of the following formats: \itemize{ - \item{numeric}{The input must be a value from 0 to 1 (exclusive of 1); this value is returned as-is.} - \item{POSIXlt or POSIXct}{The input must be on the day 1899-12-31 (any other day is an error). The time of day is extracted and converted to a fraction of a day.} - \item{character}{Any of the following (or a mixture of the choices):} - \itemize{ - \item{A character string that is a number between 0 and 1 (exclusive of 1), converted like a numeric value.} - \item{A character string that looks like a date on 1899-12-31 (specifically, it must start with \code{"1899-12-31 "}), converted like a POSIXct object.} - \item{A character string that looks like a time. Choices are 12-hour time ""} - } +\item{numeric}{The input must be a value from 0 to 1 (exclusive of 1); this value is returned as-is.} +\item{POSIXlt or POSIXct}{The input must be on the day 1899-12-31 (any other day is an error). The time of day is extracted and converted to a fraction of a day.} +\item{character}{Any of the following (or a mixture of the choices):} +\itemize{ +\item{A character string that is a number between 0 and 1 (exclusive of 1), converted like a numeric value.} +\item{A character string that looks like a date on 1899-12-31 (specifically, it must start with \code{"1899-12-31 "}), converted like a POSIXct object.} +\item{A character string that looks like a time. Choices are 12-hour time ""} +} } } \seealso{ diff --git a/man/janitor-package.Rd b/man/janitor-package.Rd index abc82e51..f58f513a 100644 --- a/man/janitor-package.Rd +++ b/man/janitor-package.Rd @@ -48,6 +48,7 @@ Other contributors: \item Ryan Knight \email{ryangknight@gmail.com} [contributor] \item Malte Grosser \email{malte.grosser@gmail.com} [contributor] \item Jonathan Zadra \email{jonathan.zadra@sorensonimpact.com} [contributor] + \item Olivier Roy [contributor] } } From 5776ed489ec89b8305d0b62a0f7f9166efa40cd8 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Wed, 1 Nov 2023 15:08:13 -0400 Subject: [PATCH 07/21] Work with SI formatted numbers --- R/excel_time_to_numeric.R | 13 +++++++++---- tests/testthat/test-excel_time_to_numeric.R | 5 +++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index 6845aefe..c4ceadcd 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -1,8 +1,8 @@ #' Convert a time that may be inconsistently or inconveniently formatted from #' Microsoft Excel to a numeric number of seconds within a day. -#' +#' #' @details -#' +#' #' \code{time_value} may be one of the following formats: #' \itemize{ #' \item{numeric}{The input must be a value from 0 to 1 (exclusive of 1); this value is returned as-is.} @@ -80,6 +80,9 @@ excel_time_to_numeric.character <- function(time_value, round_seconds=TRUE) { patterns <- list( number="^0(\\.[0-9]*)?$", + # SI numbers have to have the form [number]E-[number] becasue the number + # has to be between 0 and 1 and can't be bigger than 1. + si_number="^[1-9](\\.[0-9]*)?E-[0-9]+$", "12hr"="^([0]?[1-9]|1[0-2]):([0-5][0-9])(?::([0-5][0-9]))? ?([AP]M)$", "24hr"="^([0-1]?[0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9]))?$", # The ".*?" at the end of POSIX is to allow for a time zone, but it allows @@ -89,7 +92,9 @@ excel_time_to_numeric.character <- function(time_value, round_seconds=TRUE) { POSIX="1899-12-31 (?:([0-1]?[0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9]))?)?.*?$" ) mask_na <- is.na(time_value) - mask_number <- grepl(pattern=patterns$number, x=time_value) + mask_number <- + grepl(pattern=patterns$number, x=time_value) | + grepl(pattern=patterns$si_number, x=time_value) mask_POSIX <- grepl(pattern=patterns[["POSIX"]], x=time_value) mask_12hr <- grepl(pattern=patterns[["12hr"]], x=time_value, ignore.case=TRUE) mask_24hr <- grepl(pattern=patterns[["24hr"]], x=time_value) @@ -149,7 +154,7 @@ excel_time_to_numeric.character <- function(time_value, round_seconds=TRUE) { hours[hours %in% ""] <- "0" minutes[minutes %in% ""] <- "0" seconds[seconds %in% ""] <- "0" - + ret[mask_clock] <- as.numeric(hours[mask_clock]) * 3600 + as.numeric(minutes[mask_clock]) * 60 + diff --git a/tests/testthat/test-excel_time_to_numeric.R b/tests/testthat/test-excel_time_to_numeric.R index 8077d282..af0af0ec 100644 --- a/tests/testthat/test-excel_time_to_numeric.R +++ b/tests/testthat/test-excel_time_to_numeric.R @@ -52,6 +52,11 @@ test_that("excel_time_to_numeric, character strings of numbers work as expected" excel_time_to_numeric("0.00001", round_seconds=FALSE), 0.00001*86400 ) + # Confirm scientific notation values + expect_equal( + excel_time_to_numeric("2.9166666666666664E-2", round_seconds=TRUE), + 2520 + ) }) test_that("excel_time_to_numeric, am/pm times work", { From 46678a6055087327582beda1313ca2585de0a2f8 Mon Sep 17 00:00:00 2001 From: billdenney Date: Wed, 1 Nov 2023 19:11:25 +0000 Subject: [PATCH 08/21] Style code (GHA) --- R/excel_time_to_numeric.R | 68 ++++++------- tests/testthat/test-excel_time_to_numeric.R | 103 ++++++++++---------- 2 files changed, 85 insertions(+), 86 deletions(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index c4ceadcd..3866cd49 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -22,12 +22,12 @@ #' @family Date-time cleaning #' @seealso \code{\link{excel_numeric_to_date}} #' @export -excel_time_to_numeric <- function(time_value, round_seconds=TRUE) { +excel_time_to_numeric <- function(time_value, round_seconds = TRUE) { UseMethod("excel_time_to_numeric") } #' @export -excel_time_to_numeric.logical <- function(time_value, round_seconds=TRUE) { +excel_time_to_numeric.logical <- function(time_value, round_seconds = TRUE) { if (all(is.na(time_value))) { rep(NA_real_, length(time_value)) } else { @@ -36,11 +36,11 @@ excel_time_to_numeric.logical <- function(time_value, round_seconds=TRUE) { } #' @export -excel_time_to_numeric.numeric <- function(time_value, round_seconds=TRUE) { +excel_time_to_numeric.numeric <- function(time_value, round_seconds = TRUE) { if (all(is.na(time_value) | - time_value >= 0 & - time_value < 1)) { - seconds <- time_value*86400 + time_value >= 0 & + time_value < 1)) { + seconds <- time_value * 86400 if (round_seconds) { seconds <- round(seconds) } @@ -51,9 +51,9 @@ excel_time_to_numeric.numeric <- function(time_value, round_seconds=TRUE) { } #' @export -excel_time_to_numeric.POSIXct <- function(time_value, round_seconds=TRUE) { +excel_time_to_numeric.POSIXct <- function(time_value, round_seconds = TRUE) { # using trunc removes timezone inconsistency. Timezones aren't used in Excel. - seconds <- as.numeric(time_value) - as.numeric(trunc(time_value, units="days")) + seconds <- as.numeric(time_value) - as.numeric(trunc(time_value, units = "days")) mask_good_seconds <- is.na(seconds) | (seconds >= 0 & seconds < 86400) if (all(mask_good_seconds)) { if (round_seconds) { @@ -67,7 +67,7 @@ excel_time_to_numeric.POSIXct <- function(time_value, round_seconds=TRUE) { } #' @export -excel_time_to_numeric.POSIXlt <- function(time_value, round_seconds=TRUE) { +excel_time_to_numeric.POSIXlt <- function(time_value, round_seconds = TRUE) { excel_time_to_numeric.POSIXct( as.POSIXct(time_value), round_seconds = round_seconds @@ -75,29 +75,29 @@ excel_time_to_numeric.POSIXlt <- function(time_value, round_seconds=TRUE) { } #' @export -excel_time_to_numeric.character <- function(time_value, round_seconds=TRUE) { +excel_time_to_numeric.character <- function(time_value, round_seconds = TRUE) { ret <- rep(NA_real_, length(time_value)) patterns <- list( - number="^0(\\.[0-9]*)?$", + number = "^0(\\.[0-9]*)?$", # SI numbers have to have the form [number]E-[number] becasue the number # has to be between 0 and 1 and can't be bigger than 1. - si_number="^[1-9](\\.[0-9]*)?E-[0-9]+$", - "12hr"="^([0]?[1-9]|1[0-2]):([0-5][0-9])(?::([0-5][0-9]))? ?([AP]M)$", - "24hr"="^([0-1]?[0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9]))?$", + si_number = "^[1-9](\\.[0-9]*)?E-[0-9]+$", + "12hr" = "^([0]?[1-9]|1[0-2]):([0-5][0-9])(?::([0-5][0-9]))? ?([AP]M)$", + "24hr" = "^([0-1]?[0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9]))?$", # The ".*?" at the end of POSIX is to allow for a time zone, but it allows # for imperfect parsing if there were just a date and a space. # The the entire time is optional to allow for midnight which shows as # just the date and time zone. - POSIX="1899-12-31 (?:([0-1]?[0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9]))?)?.*?$" + POSIX = "1899-12-31 (?:([0-1]?[0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9]))?)?.*?$" ) mask_na <- is.na(time_value) mask_number <- - grepl(pattern=patterns$number, x=time_value) | - grepl(pattern=patterns$si_number, x=time_value) - mask_POSIX <- grepl(pattern=patterns[["POSIX"]], x=time_value) - mask_12hr <- grepl(pattern=patterns[["12hr"]], x=time_value, ignore.case=TRUE) - mask_24hr <- grepl(pattern=patterns[["24hr"]], x=time_value) + grepl(pattern = patterns$number, x = time_value) | + grepl(pattern = patterns$si_number, x = time_value) + mask_POSIX <- grepl(pattern = patterns[["POSIX"]], x = time_value) + mask_12hr <- grepl(pattern = patterns[["12hr"]], x = time_value, ignore.case = TRUE) + mask_24hr <- grepl(pattern = patterns[["24hr"]], x = time_value) unmatched <- !(mask_na | mask_number | mask_POSIX | mask_12hr | mask_24hr) if (any(unmatched)) { stop( @@ -109,8 +109,8 @@ excel_time_to_numeric.character <- function(time_value, round_seconds=TRUE) { if (any(mask_number)) { ret[mask_number] <- excel_time_to_numeric.numeric( - time_value=as.numeric(time_value[mask_number]), - round_seconds=round_seconds + time_value = as.numeric(time_value[mask_number]), + round_seconds = round_seconds ) } mask_clock <- mask_12hr | mask_24hr | mask_POSIX @@ -118,38 +118,38 @@ excel_time_to_numeric.character <- function(time_value, round_seconds=TRUE) { hours <- minutes <- seconds <- rep(NA_real_, length(time_value)) if (any(mask_POSIX)) { hours[mask_POSIX] <- - gsub(pattern=patterns$POSIX, replacement="\\1", x=time_value[mask_POSIX]) + gsub(pattern = patterns$POSIX, replacement = "\\1", x = time_value[mask_POSIX]) minutes[mask_POSIX] <- - gsub(pattern=patterns$POSIX, replacement="\\2", x=time_value[mask_POSIX]) + gsub(pattern = patterns$POSIX, replacement = "\\2", x = time_value[mask_POSIX]) seconds[mask_POSIX] <- - gsub(pattern=patterns$POSIX, replacement="\\3", x=time_value[mask_POSIX]) + gsub(pattern = patterns$POSIX, replacement = "\\3", x = time_value[mask_POSIX]) } if (any(mask_12hr)) { mask_pm <- rep(FALSE, length(time_value)) hours[mask_12hr] <- - gsub(pattern=patterns[["12hr"]], replacement="\\1", x=time_value[mask_12hr], ignore.case=TRUE) + gsub(pattern = patterns[["12hr"]], replacement = "\\1", x = time_value[mask_12hr], ignore.case = TRUE) minutes[mask_12hr] <- - gsub(pattern=patterns[["12hr"]], replacement="\\2", x=time_value[mask_12hr], ignore.case=TRUE) + gsub(pattern = patterns[["12hr"]], replacement = "\\2", x = time_value[mask_12hr], ignore.case = TRUE) seconds[mask_12hr] <- - gsub(pattern=patterns[["12hr"]], replacement="\\3", x=time_value[mask_12hr], ignore.case=TRUE) + gsub(pattern = patterns[["12hr"]], replacement = "\\3", x = time_value[mask_12hr], ignore.case = TRUE) # 12 is 0 hours in the AM and the PM conversion below adds the needed 12 # at noon. mask_0_hours <- mask_12hr & (hours %in% "12") hours[mask_0_hours] <- "0" mask_pm[mask_12hr] <- mask_12hr & - tolower( - gsub(pattern=patterns[["12hr"]], replacement="\\4", x=time_value[mask_12hr], ignore.case=TRUE) - ) %in% "pm" + tolower( + gsub(pattern = patterns[["12hr"]], replacement = "\\4", x = time_value[mask_12hr], ignore.case = TRUE) + ) %in% "pm" hours[mask_pm] <- 12 + as.numeric(hours[mask_pm]) } if (any(mask_24hr)) { hours[mask_24hr] <- - gsub(pattern=patterns[["24hr"]], replacement="\\1", x=time_value[mask_24hr]) + gsub(pattern = patterns[["24hr"]], replacement = "\\1", x = time_value[mask_24hr]) minutes[mask_24hr] <- - gsub(pattern=patterns[["24hr"]], replacement="\\2", x=time_value[mask_24hr]) + gsub(pattern = patterns[["24hr"]], replacement = "\\2", x = time_value[mask_24hr]) seconds[mask_24hr] <- - gsub(pattern=patterns[["24hr"]], replacement="\\3", x=time_value[mask_24hr]) + gsub(pattern = patterns[["24hr"]], replacement = "\\3", x = time_value[mask_24hr]) } hours[hours %in% ""] <- "0" minutes[minutes %in% ""] <- "0" diff --git a/tests/testthat/test-excel_time_to_numeric.R b/tests/testthat/test-excel_time_to_numeric.R index af0af0ec..a7d0c5e8 100644 --- a/tests/testthat/test-excel_time_to_numeric.R +++ b/tests/testthat/test-excel_time_to_numeric.R @@ -8,26 +8,26 @@ test_that("excel_time_to_numeric numbers function correctly", { }) test_that("excel_time_to_numeric POSIX objects extract the correct part of the time", { - expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 08:00")), 8*3600) - expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:00")), 13*3600) - expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:05:10")), 13*3600 + 5*60 + 10) + expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 08:00")), 8 * 3600) + expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:00")), 13 * 3600) + expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:05:10")), 13 * 3600 + 5 * 60 + 10) }) test_that("excel_time_to_numeric POSIX objects ignore the time zone", { - expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:00", tz="EST")), 13*3600) - expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:00", tz="UTC")), 13*3600) + expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:00", tz = "EST")), 13 * 3600) + expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:00", tz = "UTC")), 13 * 3600) expect_equal( excel_time_to_numeric( - as.POSIXct(c("1899-12-31 13:00", "1899-12-31 13:00"), tz="EST") + as.POSIXct(c("1899-12-31 13:00", "1899-12-31 13:00"), tz = "EST") ), - rep(13*3600, 2) + rep(13 * 3600, 2) ) }) test_that("excel_time_to_numeric POSIXlt works like POSIXct", { expect_equal( - excel_time_to_numeric(as.POSIXct("1899-12-31 13:00", tz="EST")), - excel_time_to_numeric(as.POSIXlt("1899-12-31 13:00", tz="EST")) + excel_time_to_numeric(as.POSIXct("1899-12-31 13:00", tz = "EST")), + excel_time_to_numeric(as.POSIXlt("1899-12-31 13:00", tz = "EST")) ) }) @@ -43,34 +43,34 @@ test_that("excel_time_to_numeric logical values return as expected", { }) test_that("excel_time_to_numeric, character strings of numbers work as expected", { - expect_equal(excel_time_to_numeric("0.5"), 12*3600) + expect_equal(excel_time_to_numeric("0.5"), 12 * 3600) expect_equal(excel_time_to_numeric("0"), 0) expect_equal(excel_time_to_numeric("0."), 0) expect_equal(excel_time_to_numeric("0.000000"), 0) expect_equal(excel_time_to_numeric("0.00001"), 1) expect_equal( - excel_time_to_numeric("0.00001", round_seconds=FALSE), - 0.00001*86400 + excel_time_to_numeric("0.00001", round_seconds = FALSE), + 0.00001 * 86400 ) # Confirm scientific notation values expect_equal( - excel_time_to_numeric("2.9166666666666664E-2", round_seconds=TRUE), + excel_time_to_numeric("2.9166666666666664E-2", round_seconds = TRUE), 2520 ) }) test_that("excel_time_to_numeric, am/pm times work", { - expect_equal(excel_time_to_numeric("8:00am"), 8*3600) - expect_equal(excel_time_to_numeric("8:00pm"), 20*3600) - expect_equal(excel_time_to_numeric("8:10am"), 8*3600 + 10*60) - expect_equal(excel_time_to_numeric("8:10:05am"), 8*3600 + 10*60 + 5) + expect_equal(excel_time_to_numeric("8:00am"), 8 * 3600) + expect_equal(excel_time_to_numeric("8:00pm"), 20 * 3600) + expect_equal(excel_time_to_numeric("8:10am"), 8 * 3600 + 10 * 60) + expect_equal(excel_time_to_numeric("8:10:05am"), 8 * 3600 + 10 * 60 + 5) expect_equal( - excel_time_to_numeric("12:10:05am"), 10*60 + 5, - info="After midnight is treated as 0 not 12." + excel_time_to_numeric("12:10:05am"), 10 * 60 + 5, + info = "After midnight is treated as 0 not 12." ) expect_equal( - excel_time_to_numeric("12:10:05pm"), 12*3600 + 10*60 + 5, - info="After noon is treated as 12." + excel_time_to_numeric("12:10:05pm"), 12 * 3600 + 10 * 60 + 5, + info = "After noon is treated as 12." ) }) @@ -94,45 +94,45 @@ test_that("excel_time_to_numeric, am/pm times work case insensitively and with s }) test_that("excel_time_to_numeric, 24-hour times work (zero-padded hours or not)", { - expect_equal(excel_time_to_numeric("8:00"), 8*3600) - expect_equal(excel_time_to_numeric("08:00"), 8*3600) - expect_equal(excel_time_to_numeric("08:10"), 8*3600 + 10*60) - expect_equal(excel_time_to_numeric("8:10:05"), 8*3600 + 10*60 + 5) - expect_equal(excel_time_to_numeric("21:05"), 21*3600 + 5*60) - expect_equal(excel_time_to_numeric("21:05:20"), 21*3600 + 5*60 + 20) + expect_equal(excel_time_to_numeric("8:00"), 8 * 3600) + expect_equal(excel_time_to_numeric("08:00"), 8 * 3600) + expect_equal(excel_time_to_numeric("08:10"), 8 * 3600 + 10 * 60) + expect_equal(excel_time_to_numeric("8:10:05"), 8 * 3600 + 10 * 60 + 5) + expect_equal(excel_time_to_numeric("21:05"), 21 * 3600 + 5 * 60) + expect_equal(excel_time_to_numeric("21:05:20"), 21 * 3600 + 5 * 60 + 20) }) test_that("excel_time_to_numeric, 24-hour times work (zero-padded hours or not)", { - expect_equal(excel_time_to_numeric("8:00"), 8*3600) - expect_equal(excel_time_to_numeric("08:00"), 8*3600) - expect_equal(excel_time_to_numeric("08:10"), 8*3600 + 10*60) - expect_equal(excel_time_to_numeric("8:10:05"), 8*3600 + 10*60 + 5) - expect_equal(excel_time_to_numeric("21:05"), 21*3600 + 5*60) - expect_equal(excel_time_to_numeric("0:05"), 5*60) - expect_equal(excel_time_to_numeric("00:05"), 5*60) - expect_equal(excel_time_to_numeric("21:05:20"), 21*3600 + 5*60 + 20) + expect_equal(excel_time_to_numeric("8:00"), 8 * 3600) + expect_equal(excel_time_to_numeric("08:00"), 8 * 3600) + expect_equal(excel_time_to_numeric("08:10"), 8 * 3600 + 10 * 60) + expect_equal(excel_time_to_numeric("8:10:05"), 8 * 3600 + 10 * 60 + 5) + expect_equal(excel_time_to_numeric("21:05"), 21 * 3600 + 5 * 60) + expect_equal(excel_time_to_numeric("0:05"), 5 * 60) + expect_equal(excel_time_to_numeric("00:05"), 5 * 60) + expect_equal(excel_time_to_numeric("21:05:20"), 21 * 3600 + 5 * 60 + 20) }) test_that("excel_time_to_numeric, POSIX times on 1899-12-31 work", { - expect_equal(excel_time_to_numeric("1899-12-31 8:00"), 8*3600) - expect_equal(excel_time_to_numeric("1899-12-31 08:00"), 8*3600) - expect_equal(excel_time_to_numeric("1899-12-31 08:10"), 8*3600 + 10*60) - expect_equal(excel_time_to_numeric("1899-12-31 8:10:05"), 8*3600 + 10*60 + 5) - expect_equal(excel_time_to_numeric("1899-12-31 21:05"), 21*3600 + 5*60) - expect_equal(excel_time_to_numeric("1899-12-31 0:05"), 5*60) - expect_equal(excel_time_to_numeric("1899-12-31 00:05"), 5*60) - expect_equal(excel_time_to_numeric("1899-12-31 21:05:20"), 21*3600 + 5*60 + 20) + expect_equal(excel_time_to_numeric("1899-12-31 8:00"), 8 * 3600) + expect_equal(excel_time_to_numeric("1899-12-31 08:00"), 8 * 3600) + expect_equal(excel_time_to_numeric("1899-12-31 08:10"), 8 * 3600 + 10 * 60) + expect_equal(excel_time_to_numeric("1899-12-31 8:10:05"), 8 * 3600 + 10 * 60 + 5) + expect_equal(excel_time_to_numeric("1899-12-31 21:05"), 21 * 3600 + 5 * 60) + expect_equal(excel_time_to_numeric("1899-12-31 0:05"), 5 * 60) + expect_equal(excel_time_to_numeric("1899-12-31 00:05"), 5 * 60) + expect_equal(excel_time_to_numeric("1899-12-31 21:05:20"), 21 * 3600 + 5 * 60 + 20) }) test_that("excel_time_to_numeric, POSIX times ignore extra text (which is hopefully a time zone)", { - expect_equal(excel_time_to_numeric("1899-12-31 8:00 foo"), 8*3600) - expect_equal(excel_time_to_numeric("1899-12-31 08:00 foo"), 8*3600) - expect_equal(excel_time_to_numeric("1899-12-31 08:10 foo"), 8*3600 + 10*60) - expect_equal(excel_time_to_numeric("1899-12-31 8:10:05 foo"), 8*3600 + 10*60 + 5) - expect_equal(excel_time_to_numeric("1899-12-31 21:05 foo"), 21*3600 + 5*60) - expect_equal(excel_time_to_numeric("1899-12-31 0:05 foo"), 5*60) - expect_equal(excel_time_to_numeric("1899-12-31 00:05 foo"), 5*60) - expect_equal(excel_time_to_numeric("1899-12-31 21:05:20 foo"), 21*3600 + 5*60 + 20) + expect_equal(excel_time_to_numeric("1899-12-31 8:00 foo"), 8 * 3600) + expect_equal(excel_time_to_numeric("1899-12-31 08:00 foo"), 8 * 3600) + expect_equal(excel_time_to_numeric("1899-12-31 08:10 foo"), 8 * 3600 + 10 * 60) + expect_equal(excel_time_to_numeric("1899-12-31 8:10:05 foo"), 8 * 3600 + 10 * 60 + 5) + expect_equal(excel_time_to_numeric("1899-12-31 21:05 foo"), 21 * 3600 + 5 * 60) + expect_equal(excel_time_to_numeric("1899-12-31 0:05 foo"), 5 * 60) + expect_equal(excel_time_to_numeric("1899-12-31 00:05 foo"), 5 * 60) + expect_equal(excel_time_to_numeric("1899-12-31 21:05:20 foo"), 21 * 3600 + 5 * 60 + 20) }) test_that("excel_time_to_numeric, POSIX times treat no time as midnight but only if there is a space indicating a mostly-well-formed date-time object.", { @@ -155,4 +155,3 @@ test_that("excel_time_to_numeric, invalid character times trigger an error", { expect_error(excel_time_to_numeric("23:05:90")) expect_error(excel_time_to_numeric("1899-12-30 21:05:20")) }) - From 88bcc3b169e46d6b5ac25ed8bab9a18d060fa508 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Wed, 1 Nov 2023 15:18:12 -0400 Subject: [PATCH 09/21] Fix incorrect selection with mixed inputs --- R/excel_time_to_numeric.R | 1 - tests/testthat/test-excel_time_to_numeric.R | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index c4ceadcd..50d41cf0 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -137,7 +137,6 @@ excel_time_to_numeric.character <- function(time_value, round_seconds=TRUE) { mask_0_hours <- mask_12hr & (hours %in% "12") hours[mask_0_hours] <- "0" mask_pm[mask_12hr] <- - mask_12hr & tolower( gsub(pattern=patterns[["12hr"]], replacement="\\4", x=time_value[mask_12hr], ignore.case=TRUE) ) %in% "pm" diff --git a/tests/testthat/test-excel_time_to_numeric.R b/tests/testthat/test-excel_time_to_numeric.R index af0af0ec..20a7b393 100644 --- a/tests/testthat/test-excel_time_to_numeric.R +++ b/tests/testthat/test-excel_time_to_numeric.R @@ -72,6 +72,11 @@ test_that("excel_time_to_numeric, am/pm times work", { excel_time_to_numeric("12:10:05pm"), 12*3600 + 10*60 + 5, info="After noon is treated as 12." ) + # Test mixed AM/PM and 24-hour clock values + expect_equal( + excel_time_to_numeric(c("8:00pm", "8:00", "9:00")), + c(20, 8, 9)*3600 + ) }) test_that("excel_time_to_numeric, am/pm times work case insensitively and with spaces", { From 8aa21dcc29d03dca7fcd9abde5df8367e39f386f Mon Sep 17 00:00:00 2001 From: billdenney Date: Wed, 1 Nov 2023 19:21:13 +0000 Subject: [PATCH 10/21] Style code (GHA) --- tests/testthat/test-excel_time_to_numeric.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-excel_time_to_numeric.R b/tests/testthat/test-excel_time_to_numeric.R index ce464d56..e0fe0a31 100644 --- a/tests/testthat/test-excel_time_to_numeric.R +++ b/tests/testthat/test-excel_time_to_numeric.R @@ -75,7 +75,7 @@ test_that("excel_time_to_numeric, am/pm times work", { # Test mixed AM/PM and 24-hour clock values expect_equal( excel_time_to_numeric(c("8:00pm", "8:00", "9:00")), - c(20, 8, 9)*3600 + c(20, 8, 9) * 3600 ) }) From a461e77d985755bc82bb8c52928cce47f3b388f7 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Wed, 1 Nov 2023 15:23:08 -0400 Subject: [PATCH 11/21] Fix merge issue --- R/excel_time_to_numeric.R | 7 ------- 1 file changed, 7 deletions(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index 398768c1..1371d01d 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -137,16 +137,9 @@ excel_time_to_numeric.character <- function(time_value, round_seconds = TRUE) { mask_0_hours <- mask_12hr & (hours %in% "12") hours[mask_0_hours] <- "0" mask_pm[mask_12hr] <- -<<<<<<< HEAD tolower( gsub(pattern=patterns[["12hr"]], replacement="\\4", x=time_value[mask_12hr], ignore.case=TRUE) ) %in% "pm" -======= - mask_12hr & - tolower( - gsub(pattern = patterns[["12hr"]], replacement = "\\4", x = time_value[mask_12hr], ignore.case = TRUE) - ) %in% "pm" ->>>>>>> 46678a6055087327582beda1313ca2585de0a2f8 hours[mask_pm] <- 12 + as.numeric(hours[mask_pm]) } if (any(mask_24hr)) { From 576a532b282c6af1fee6feb4afc948cc559b26b7 Mon Sep 17 00:00:00 2001 From: billdenney Date: Wed, 1 Nov 2023 19:25:54 +0000 Subject: [PATCH 12/21] Style code (GHA) --- R/excel_time_to_numeric.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index 1371d01d..f9f2e887 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -138,7 +138,7 @@ excel_time_to_numeric.character <- function(time_value, round_seconds = TRUE) { hours[mask_0_hours] <- "0" mask_pm[mask_12hr] <- tolower( - gsub(pattern=patterns[["12hr"]], replacement="\\4", x=time_value[mask_12hr], ignore.case=TRUE) + gsub(pattern = patterns[["12hr"]], replacement = "\\4", x = time_value[mask_12hr], ignore.case = TRUE) ) %in% "pm" hours[mask_pm] <- 12 + as.numeric(hours[mask_pm]) } From edaf56ebe301403b4b8384777986baf47955c069 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Thu, 30 Nov 2023 15:32:12 -0500 Subject: [PATCH 13/21] Update R/excel_time_to_numeric.R Co-authored-by: Sam Firke --- R/excel_time_to_numeric.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index f9f2e887..9bea5b53 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -1,5 +1,5 @@ #' Convert a time that may be inconsistently or inconveniently formatted from -#' Microsoft Excel to a numeric number of seconds within a day. +#' Microsoft Excel to a numeric number of seconds between 0 and 86400. #' #' @details #' From 56f3d7ac39aced16d1a6ed5c479432b35829c3f5 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Thu, 30 Nov 2023 15:32:35 -0500 Subject: [PATCH 14/21] Update R/excel_time_to_numeric.R Co-authored-by: Sam Firke --- R/excel_time_to_numeric.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index 9bea5b53..8a6eb66b 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -6,7 +6,7 @@ #' \code{time_value} may be one of the following formats: #' \itemize{ #' \item{numeric}{The input must be a value from 0 to 1 (exclusive of 1); this value is returned as-is.} -#' \item{POSIXlt or POSIXct}{The input must be on the day 1899-12-31 (any other day is an error). The time of day is extracted and converted to a fraction of a day.} +#' \item{POSIXlt or POSIXct}{The input must be on the day 1899-12-31 (any other day will cause an error). The time of day is extracted and converted to a fraction of a day.} #' \item{character}{Any of the following (or a mixture of the choices):} #' \itemize{ #' \item{A character string that is a number between 0 and 1 (exclusive of 1), converted like a numeric value.} From ce621d36b5b26b468925284ef60cc317b593b34b Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Thu, 30 Nov 2023 15:45:51 -0500 Subject: [PATCH 15/21] Code review fixes and updates --- R/excel_time_to_numeric.R | 8 ++++---- tests/testthat/test-excel_time_to_numeric.R | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index 8a6eb66b..2632c7c6 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -10,8 +10,8 @@ #' \item{character}{Any of the following (or a mixture of the choices):} #' \itemize{ #' \item{A character string that is a number between 0 and 1 (exclusive of 1), converted like a numeric value.} -#' \item{A character string that looks like a date on 1899-12-31 (specifically, it must start with \code{"1899-12-31 "}), converted like a POSIXct object.} -#' \item{A character string that looks like a time. Choices are 12-hour time ""} +#' \item{A character string that looks like a date on 1899-12-31 (specifically, it must start with \code{"1899-12-31 "}), converted like a POSIXct object as described above.} +#' \item{A character string that looks like a time. Choices are 12-hour time as hour, minute, and optionally second followed by "am" or "pm" (case insensitive) or 24-hour time when hour, minute, optionally second, and no "am" or "pm" is included.} #' } #' } #' @@ -38,8 +38,8 @@ excel_time_to_numeric.logical <- function(time_value, round_seconds = TRUE) { #' @export excel_time_to_numeric.numeric <- function(time_value, round_seconds = TRUE) { if (all(is.na(time_value) | - time_value >= 0 & - time_value < 1)) { + (time_value >= 0 & + time_value < 1))) { seconds <- time_value * 86400 if (round_seconds) { seconds <- round(seconds) diff --git a/tests/testthat/test-excel_time_to_numeric.R b/tests/testthat/test-excel_time_to_numeric.R index e0fe0a31..da994fbb 100644 --- a/tests/testthat/test-excel_time_to_numeric.R +++ b/tests/testthat/test-excel_time_to_numeric.R @@ -8,6 +8,7 @@ test_that("excel_time_to_numeric numbers function correctly", { }) test_that("excel_time_to_numeric POSIX objects extract the correct part of the time", { + expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 00:01")), 60) expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 08:00")), 8 * 3600) expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:00")), 13 * 3600) expect_equal(excel_time_to_numeric(as.POSIXct("1899-12-31 13:05:10")), 13 * 3600 + 5 * 60 + 10) From be1571d4373e1464e2aceee07486be2d3f1b4b8d Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Thu, 30 Nov 2023 15:46:14 -0500 Subject: [PATCH 16/21] Update tests/testthat/test-excel_time_to_numeric.R Co-authored-by: olivroy <52606734+olivroy@users.noreply.github.com> --- tests/testthat/test-excel_time_to_numeric.R | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/testthat/test-excel_time_to_numeric.R b/tests/testthat/test-excel_time_to_numeric.R index da994fbb..454ad741 100644 --- a/tests/testthat/test-excel_time_to_numeric.R +++ b/tests/testthat/test-excel_time_to_numeric.R @@ -1,5 +1,3 @@ -context("Excel time cleaning") - test_that("excel_time_to_numeric numbers function correctly", { expect_equal(excel_time_to_numeric(0.1), 8640) expect_equal(excel_time_to_numeric(0.1000000001), 8640) From 6a0e63d85120d21a30cf355b639bb5519c04c0f8 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Thu, 30 Nov 2023 15:46:39 -0500 Subject: [PATCH 17/21] Update R/excel_time_to_numeric.R Co-authored-by: Sam Firke --- R/excel_time_to_numeric.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index 2632c7c6..9922dd9a 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -9,7 +9,7 @@ #' \item{POSIXlt or POSIXct}{The input must be on the day 1899-12-31 (any other day will cause an error). The time of day is extracted and converted to a fraction of a day.} #' \item{character}{Any of the following (or a mixture of the choices):} #' \itemize{ -#' \item{A character string that is a number between 0 and 1 (exclusive of 1), converted like a numeric value.} +#' \item{A character string that is a number between 0 and 1 (exclusive of 1). This value will be converted like a numeric value.} #' \item{A character string that looks like a date on 1899-12-31 (specifically, it must start with \code{"1899-12-31 "}), converted like a POSIXct object as described above.} #' \item{A character string that looks like a time. Choices are 12-hour time as hour, minute, and optionally second followed by "am" or "pm" (case insensitive) or 24-hour time when hour, minute, optionally second, and no "am" or "pm" is included.} #' } From faf632d984b917a65da05e9efe97317f459ccc7d Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Thu, 30 Nov 2023 15:46:59 -0500 Subject: [PATCH 18/21] Update R/excel_time_to_numeric.R Co-authored-by: Sam Firke --- R/excel_time_to_numeric.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index 9922dd9a..8e401531 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -87,7 +87,7 @@ excel_time_to_numeric.character <- function(time_value, round_seconds = TRUE) { "24hr" = "^([0-1]?[0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9]))?$", # The ".*?" at the end of POSIX is to allow for a time zone, but it allows # for imperfect parsing if there were just a date and a space. - # The the entire time is optional to allow for midnight which shows as + # The entire time is optional to allow for midnight which shows as # just the date and time zone. POSIX = "1899-12-31 (?:([0-1]?[0-9]|2[0-3]):([0-5][0-9])(?::([0-5][0-9]))?)?.*?$" ) From e3d4f634dc173ea076ca2c7c364ef93822e7c648 Mon Sep 17 00:00:00 2001 From: billdenney Date: Thu, 30 Nov 2023 20:48:39 +0000 Subject: [PATCH 19/21] Style code (GHA) --- R/excel_time_to_numeric.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index 8e401531..69b5b9d7 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -38,8 +38,8 @@ excel_time_to_numeric.logical <- function(time_value, round_seconds = TRUE) { #' @export excel_time_to_numeric.numeric <- function(time_value, round_seconds = TRUE) { if (all(is.na(time_value) | - (time_value >= 0 & - time_value < 1))) { + (time_value >= 0 & + time_value < 1))) { seconds <- time_value * 86400 if (round_seconds) { seconds <- round(seconds) From ef5c5595d2e78587f993e385f96831ff37163896 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Thu, 30 Nov 2023 15:49:11 -0500 Subject: [PATCH 20/21] Rebuild docs --- R/excel_time_to_numeric.R | 6 +++--- man/excel_time_to_numeric.Rd | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/R/excel_time_to_numeric.R b/R/excel_time_to_numeric.R index 8e401531..e8eafe08 100644 --- a/R/excel_time_to_numeric.R +++ b/R/excel_time_to_numeric.R @@ -3,14 +3,14 @@ #' #' @details #' -#' \code{time_value} may be one of the following formats: +#' `time_value` may be one of the following formats: #' \itemize{ #' \item{numeric}{The input must be a value from 0 to 1 (exclusive of 1); this value is returned as-is.} #' \item{POSIXlt or POSIXct}{The input must be on the day 1899-12-31 (any other day will cause an error). The time of day is extracted and converted to a fraction of a day.} #' \item{character}{Any of the following (or a mixture of the choices):} #' \itemize{ #' \item{A character string that is a number between 0 and 1 (exclusive of 1). This value will be converted like a numeric value.} -#' \item{A character string that looks like a date on 1899-12-31 (specifically, it must start with \code{"1899-12-31 "}), converted like a POSIXct object as described above.} +#' \item{A character string that looks like a date on 1899-12-31 (specifically, it must start with `"1899-12-31 "`), converted like a POSIXct object as described above.} #' \item{A character string that looks like a time. Choices are 12-hour time as hour, minute, and optionally second followed by "am" or "pm" (case insensitive) or 24-hour time when hour, minute, optionally second, and no "am" or "pm" is included.} #' } #' } @@ -20,7 +20,7 @@ #' integer? #' @return A vector of numbers >= 0 and <86400 #' @family Date-time cleaning -#' @seealso \code{\link{excel_numeric_to_date}} +#' @seealso `\link{excel_numeric_to_date}` #' @export excel_time_to_numeric <- function(time_value, round_seconds = TRUE) { UseMethod("excel_time_to_numeric") diff --git a/man/excel_time_to_numeric.Rd b/man/excel_time_to_numeric.Rd index 6a80edef..59e9b033 100644 --- a/man/excel_time_to_numeric.Rd +++ b/man/excel_time_to_numeric.Rd @@ -3,7 +3,7 @@ \name{excel_time_to_numeric} \alias{excel_time_to_numeric} \title{Convert a time that may be inconsistently or inconveniently formatted from -Microsoft Excel to a numeric number of seconds within a day.} +Microsoft Excel to a numeric number of seconds between 0 and 86400.} \usage{ excel_time_to_numeric(time_value, round_seconds = TRUE) } @@ -18,23 +18,23 @@ A vector of numbers >= 0 and <86400 } \description{ Convert a time that may be inconsistently or inconveniently formatted from -Microsoft Excel to a numeric number of seconds within a day. +Microsoft Excel to a numeric number of seconds between 0 and 86400. } \details{ \code{time_value} may be one of the following formats: \itemize{ \item{numeric}{The input must be a value from 0 to 1 (exclusive of 1); this value is returned as-is.} -\item{POSIXlt or POSIXct}{The input must be on the day 1899-12-31 (any other day is an error). The time of day is extracted and converted to a fraction of a day.} +\item{POSIXlt or POSIXct}{The input must be on the day 1899-12-31 (any other day will cause an error). The time of day is extracted and converted to a fraction of a day.} \item{character}{Any of the following (or a mixture of the choices):} \itemize{ -\item{A character string that is a number between 0 and 1 (exclusive of 1), converted like a numeric value.} -\item{A character string that looks like a date on 1899-12-31 (specifically, it must start with \code{"1899-12-31 "}), converted like a POSIXct object.} -\item{A character string that looks like a time. Choices are 12-hour time ""} +\item{A character string that is a number between 0 and 1 (exclusive of 1). This value will be converted like a numeric value.} +\item{A character string that looks like a date on 1899-12-31 (specifically, it must start with \code{"1899-12-31 "}), converted like a POSIXct object as described above.} +\item{A character string that looks like a time. Choices are 12-hour time as hour, minute, and optionally second followed by "am" or "pm" (case insensitive) or 24-hour time when hour, minute, optionally second, and no "am" or "pm" is included.} } } } \seealso{ -\code{\link{excel_numeric_to_date}} +\verb{\link{excel_numeric_to_date}} Other Date-time cleaning: \code{\link{convert_to_date}()}, From 25ee9c2731d415f469ff41e81bd3df6e320c8db9 Mon Sep 17 00:00:00 2001 From: Bill Denney Date: Thu, 7 Dec 2023 08:41:38 -0500 Subject: [PATCH 21/21] Remove duplicate tests --- tests/testthat/test-excel_time_to_numeric.R | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/testthat/test-excel_time_to_numeric.R b/tests/testthat/test-excel_time_to_numeric.R index 454ad741..42d7ac93 100644 --- a/tests/testthat/test-excel_time_to_numeric.R +++ b/tests/testthat/test-excel_time_to_numeric.R @@ -97,15 +97,6 @@ test_that("excel_time_to_numeric, am/pm times work case insensitively and with s ) }) -test_that("excel_time_to_numeric, 24-hour times work (zero-padded hours or not)", { - expect_equal(excel_time_to_numeric("8:00"), 8 * 3600) - expect_equal(excel_time_to_numeric("08:00"), 8 * 3600) - expect_equal(excel_time_to_numeric("08:10"), 8 * 3600 + 10 * 60) - expect_equal(excel_time_to_numeric("8:10:05"), 8 * 3600 + 10 * 60 + 5) - expect_equal(excel_time_to_numeric("21:05"), 21 * 3600 + 5 * 60) - expect_equal(excel_time_to_numeric("21:05:20"), 21 * 3600 + 5 * 60 + 20) -}) - test_that("excel_time_to_numeric, 24-hour times work (zero-padded hours or not)", { expect_equal(excel_time_to_numeric("8:00"), 8 * 3600) expect_equal(excel_time_to_numeric("08:00"), 8 * 3600)