diff --git a/DESCRIPTION b/DESCRIPTION index 808ce73..e8d65f5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -68,4 +68,4 @@ Encoding: UTF-8 Language: en-US LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.1.1 +RoxygenNote: 7.1.2 diff --git a/NAMESPACE b/NAMESPACE index 4d18b77..5f14753 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -59,6 +59,7 @@ export(sjl_weighted) export(sloss_week) export(so) export(sum_time) +export(sum_times) export(tbt) importFrom(dplyr,"%>%") importFrom(dplyr,across) diff --git a/NEWS.md b/NEWS.md index 5c3ade7..2ac1f5a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -17,6 +17,7 @@ * `round_time()` is now a S3 generic. * `shortest_interval()` was renamed to `shorter_interval()`. * `shorter_interval()` will now return only `Interval` objects. +* `sum_time()` now have different arguments and was divided in two functions: `sum_time()` (for non-vectorized sums) and `sum_times()` (for vectorized sums). * Several functions were optimized. * The test suite was optimized. diff --git a/R/gu.R b/R/gu.R index f9a3925..f001aae 100644 --- a/R/gu.R +++ b/R/gu.R @@ -92,5 +92,5 @@ gu <- function(se, si) { assert_duration(si) assert_identical(se, si, type = "length") - sum_time(se, si, class = "hms", circular = TRUE, vectorize = TRUE) + sum_times(se, si, cycle = lubridate::ddays()) } diff --git a/R/ms.R b/R/ms.R index f4cf2c8..d48d876 100644 --- a/R/ms.R +++ b/R/ms.R @@ -97,7 +97,7 @@ ms <- function(so, sd) { assert_duration(sd) assert_identical(so, sd, type = "length") - sum_time(so, (sd / 2), class = "hms", circular = TRUE, vectorize = TRUE) + sum_times(so, (sd / 2), cycle = lubridate::ddays()) } #' Compute MCTQ corrected local time of mid-sleep on work-free days @@ -290,14 +290,14 @@ msf_sc <- function(msf, sd_w, sd_f, sd_week, alarm_f) { ## `sc` exists to remove unnecessary warnings of the lubridate package when ## subtracting objects of class `Duration`. - sc <- sum_time(sd_f, - sd_week, class = "Duration", vectorize = TRUE) + sc <- sum_times(sd_f, - sd_week, cycle = lubridate::ddays()) %>% + lubridate::as.duration() sc <- sc / 2 dplyr::case_when( alarm_f == TRUE ~ hms::as_hms(NA), sd_f <= sd_w ~ msf, - sd_f > sd_w ~ sum_time(msf, - sc, class = "hms", - circular = TRUE, vectorize = TRUE) + sd_f > sd_w ~ sum_times(msf, - sc, cycle = lubridate::ddays()) ) } diff --git a/R/random_mctq.R b/R/random_mctq.R index 0e51dea..e78f1d6 100644 --- a/R/random_mctq.R +++ b/R/random_mctq.R @@ -436,7 +436,7 @@ random_shift_mctq <- function(n_w = c(n_w_m = 6, n_w_e = 4, n_w_n = 6), for (i in c("bt_w_m", "bt_w_e", "bt_w_n", "bt_f_m", "bt_f_e", "bt_f_n")) { sample <- sample_time(min = min, max = max, by = by, prob = prob) name <- paste0("sprep_", str_extract_(i, "._.$")) - assign(i, sum_time(get(name), - sample, circular = TRUE)) + assign(i, sum_time(get(name), - sample, cycle = lubridate::ddays())) } # Create `slat_w_*` and `slat_f_*` ----- @@ -510,7 +510,7 @@ random_shift_mctq <- function(n_w = c(n_w_m = 6, n_w_e = 4, n_w_n = 6), se_w_m = list( name = "se_w_m", min = sum_time(sprep_w_m, slat_w_m, hms::parse_hm("02:05"), - circular = TRUE), + cycle = lubridate::ddays()), max = hms::parse_hm("05:00"), # Changed mean = hms::parse_hm("04:35"), sd = hms::parse_hm("00:35")), @@ -521,7 +521,7 @@ random_shift_mctq <- function(n_w = c(n_w_m = 6, n_w_e = 4, n_w_n = 6), se_w_e = list( name = "se_w_e", min = sum_time(sprep_w_e, slat_w_e, hms::parse_hm("03:40"), - circular = TRUE), + cycle = lubridate::ddays()), max = hms::parse_hm("12:25"), mean = hms::parse_hm("08:25"), sd = hms::parse_hm("01:20")), @@ -532,7 +532,7 @@ random_shift_mctq <- function(n_w = c(n_w_m = 6, n_w_e = 4, n_w_n = 6), se_w_n = list( name = "se_w_n", min = sum_time(sprep_w_n, slat_w_n, hms::parse_hm("02:00"), # Chan. - circular = TRUE), + cycle = lubridate::ddays()), max = hms::parse_hm("18:05"), mean = hms::parse_hm("13:30"), sd = hms::parse_hm("01:30")), @@ -678,7 +678,7 @@ random_shift_mctq <- function(n_w = c(n_w_m = 6, n_w_e = 4, n_w_n = 6), "nape_f_n")) { name <- get(paste0("napo_", str_extract_(i, "._.$"))) sample <- sample_time(min = min, max = max, by = by, prob = prob) - assign(i, sum_time(name, sample, circular = TRUE)) + assign(i, sum_time(name, sample, cycle = lubridate::ddays())) } # Create `nap_w_*` and `nap_f_*` ----- diff --git a/R/sd.R b/R/sd.R index 53c2fd6..9703e37 100644 --- a/R/sd.R +++ b/R/sd.R @@ -102,7 +102,8 @@ sdu <- function(so, se) { checkmate::assert_class(se, "hms") assert_identical(so, se, type = "length") - sum_time(se, - so, class = "Duration", circular = TRUE, vectorize = TRUE) + sum_times(se, - so, cycle = lubridate::ddays()) %>% + lubridate::as.duration() } #' Compute MCTQ nap duration (only for MCTQ\eqn{^{Shift}}{ Shift}) @@ -188,8 +189,8 @@ napd <- function(napo, nape) { checkmate::assert_class(nape, "hms") assert_identical(napo, nape, type = "length") - sum_time(nape, - napo, class = "Duration", circular = TRUE, - vectorize = TRUE) + sum_times(nape, - napo, cycle = lubridate::ddays()) %>% + lubridate::as.duration() } #' Compute MCTQ 24 hours sleep duration (only for MCTQ\eqn{^{Shift}}{ Shift}) @@ -300,8 +301,7 @@ sd24 <- function(sd, napd, nap) { dplyr::case_when( nap == FALSE ~ sd, - TRUE ~ sum_time(sd, napd, class = "Duration", circular = FALSE, - vectorize = TRUE) + TRUE ~ lubridate::as.duration(sum_times(sd, napd)) ) } diff --git a/R/sjl.R b/R/sjl.R index 6f4cf7a..da390db 100644 --- a/R/sjl.R +++ b/R/sjl.R @@ -217,8 +217,8 @@ sjl <- function(msw, msf, abs = TRUE, method = "shorter") { assert_identical(msw, msf, type = "length") if (method == "difference") { - out <- sum_time(msf, - msw, class = "Duration", circular = FALSE, - vectorize = TRUE) + out <- sum_times(msf, - msw) %>% + lubridate::as.duration() } else { if (method == "shorter") { interval <- shush(shorter_interval(msw, msf)) diff --git a/R/sloss_week.R b/R/sloss_week.R index ff80c16..6c9806a 100644 --- a/R/sloss_week.R +++ b/R/sloss_week.R @@ -108,8 +108,8 @@ sloss_week <- function(sd_w, sd_f, wd) { wd <- as.integer(wd) sd_week <- sd_week(sd_w, sd_f, wd) - sum_1 <- sum_time(sd_week, - sd_w, class = "Duration", vectorize = TRUE) - sum_2 <- sum_time(sd_week, - sd_f, class = "Duration", vectorize = TRUE) + sum_1 <- sum_times(sd_week, - sd_w) %>% lubridate::as.duration() + sum_2 <- sum_times(sd_week, - sd_f) %>% lubridate::as.duration() dplyr::case_when( sd_week > sd_w ~ sum_1 * wd, diff --git a/R/so.R b/R/so.R index ecc42a1..40a3f01 100644 --- a/R/so.R +++ b/R/so.R @@ -104,5 +104,5 @@ so <- function(sprep, slat) { assert_duration(slat) assert_identical(sprep, slat, type = "length") - sum_time(sprep, slat, class = "hms", circular = TRUE, vectorize = TRUE) + sum_times(sprep, slat, cycle = lubridate::ddays()) } diff --git a/R/sum_time.R b/R/sum_time.R index d0cbed3..b1e6ca6 100644 --- a/R/sum_time.R +++ b/R/sum_time.R @@ -7,43 +7,117 @@ #' `sum_time()` returns the sum of the time from different kinds of date/time #' objects. #' -#' This function supports vectorized operations and can also be set to work with -#' a circular time frame of 24 hours. +#' `sum_times()` returns the vectorized sum of the time from different kinds of +#' date/time objects. #' -#' @details -#' -#' ## `class` argument -#' -#' `sum_time()` is integrated with [mctq::convert()]. That way you can choose -#' what class of object you prefer as output. +#' Both functions can be set to work with a circular time frame (see Details to +#' learn more). #' -#' Valid `class` values are: `"character"`, `"integer"`, `"double"`, -#' `"numeric"`, `"Duration"`, `"Period"`, `"difftime"`, and `"hms"` (case -#' insensitive). -#' -#' ## `vectorize` argument -#' -#' If `vectorize = FALSE` (default), `sum_time` will combine and sum all time -#' values in `...`. That is, `sum_time(c(x, y), z)` will have the same -#' output as `sum_time(x, y, z)`. +#' @details #' -#' However, if `vectorize = TRUE`, `sum_time()` will require that all objects in -#' `...` have the same length, and will perform a paired sum between elements. -#' That is, `sum_time(c(x, y), c(w, z))` will return a vector like -#' `c(sum_time(x, w), sum_time(y, z))`. +#' ## `sum_time()` versus `sum_times()` +#' +#' `sum_time()` behaves similar to `sum()`, in the sense that it aggregates the +#' time lengths of values in `...` into a single data point. For example, +#' `sum_time(c(x, y), z)` will have the same output as `sum_time(x, y, z)`. +#' +#' `sum_times()` performs a different type of sum (a vectorized one). Instead of +#' aggregate the time lengths, the function perform a paired sum between +#' elements. For example, `sum_time(c(x, y), c(w, z))` will return a vector like +#' `c(sum_time(x, w), sum_time(y, z))`. Because of that, `sum_times()` requires +#' that all objects in `...` have the same length. +#' +#' ## Linear versus circular sum +#' +#' Time can have different "shapes". +#' +#' If the objective is to measure the duration of an event, time is usually +#' measured considering a linear frame, with a fixed point of +#' [origin](https://en.wikipedia.org/wiki/Origin_(mathematics)). In this +#' context, the time value distance itself to infinity in relation to the +#' origin. +#' +#' ``` +#' B +#' |----------| +#' A +#' |---------------------| +#' - inf inf + +#' <----------------------------|----------|----------|-------> +#' s 0 5 10 s +#' origin +#' +#' A + B = 10 + 5 = 15s +#' ``` +#' +#' But that's not the only possible "shape" of time, as it can also be measured +#' in other contexts. +#' +#' In a "time of day" context, time will be linked to the rotation of the +#' earth, "resetting" when a new rotation cycle starts. That brings a different +#' kind of shape to time: a circular shape. With this shape the time value +#' encounters the origin at the end of the cycle (every 24 hours or 86400 +#' seconds). +#' +#' ``` +#' - <--- h ---> + +#' origin +#' . . . 0 . . . +#' . . +#' . . +#' . . +#' . . +#' . . +#' 18 6 +#' . . +#' . . +#' . . +#' . . +#' . . +#' . . . 12 . . . +#' +#' 18 + 6 = 0h +#' ``` +#' +#' If we transpose this circular time frame to a linear one, it would look like +#' this: +#' +#' +#' ``` +#' -----|---------------|---------------|---------------|-----> +#' 0h 12h 0h 12h +#' origin origin +#' ``` +#' +#' Note that now the origin is not fixed, but cyclical. +#' +#' `sum_time()` and `sum_times()` can both operate in either a linear or a +#' circular fashion. If `cycle = NULL` (default), the function will use a +#' linear approach. Else, the function will use a circular approach relative to +#' the cycle length (e.g, `cycle = 86400` (1 day)). #' #' ## `POSIXt` objects #' #' `POSIXt` values in `...` will be stripped of their dates. Only the time will #' be considered. #' +#' Both `POSIXct` and `POSIXlt` are objects that inherits the class `POSIXt`. +#' Learn more about it in [base::DateTimeClasses]. +#' #' ## `Period` objects #' -#' `Period` objects are a special time of object developed by the +#' `Period` objects are a special type of object developed by the #' [lubridate][lubridate::lubridate-package] team that represents "human units", #' ignoring possible time irregularities. That is to say that 1 day as `Period` -#' will always represent 1 day in the timeline. `sum_time()` ignores that -#' property of `Period` objects, treating them like objects of class `Duration`. +#' will always represent 1 day in the timeline. `sum_time()` and `sum_times()` +#' ignores that property of `Period` objects, treating them like objects of +#' class `Duration`. +#' +#' ## `Interval` objects +#' +#' By using `Interval` objects in `...`, `sum_time()` and `sum_times()` will +#' consider only their time lengths. That is, the amount of seconds of the +#' intervals. #' #' ## Timeline irregularities #' @@ -53,65 +127,50 @@ #' #' @param ... Objects belonging to one of the following classes: `Duration`, #' `Period`, `difftime`, `hms`, `POSIXct`, `POSIXlt`, or `Interval`. -#' @param class (optional) a string indicating the output class (default: -#' `"hms"`). -#' @param circular (optional) a `logical` value indicating whether the sum -#' should be made in a circular time frame of 24 hours (clock hours) (default: -#' `FALSE`). -#' @param vectorize (optional) a `logical` value indicating if the function must -#' operate in a vectorized fashion (default: `FALSE`). -#' @param na.rm (optional) a `logical` value indicating if the function must +#' @param cycle (optional) a number indicating the cycle length in seconds. If +#' `NULL` the function will perform a linear sum (see Details to learn +#' more) (default: `NULL`). +#' @param na_rm (optional) a `logical` value indicating if the function must #' remove `NA` values while performing the sum (default: `FALSE`). #' #' @return #' -#' * If `circular = TRUE` and `vectorize = FALSE`, an object of -#' the indicated class in `class` (default: `"hms"`) with the sum of the time -#' from objects in `...` in a circular time frame of 24 hours. -#' -#' * If `circular = FALSE` and `vectorize = FALSE`, an -#' object of the indicated class in `class` (default: `"hms"`) with a -#' linear sum of the time from objects in `...`. -#' -#' * If `circular = TRUE` and `vectorize = TRUE`, an object of the indicated -#' class in `class` (default: `"hms"`) with a vectorized sum of the time from -#' objects in `...` in a circular time frame of 24 hours. -#' -#' * If `circular = FALSE` and `vectorize = TRUE`, an object of the -#' indicated class in `class` (default: `"hms"`) with a vectorized and -#' linear sum of the time from objects in `...`. +#' * If `cycle = NULL`, an `hms` object with a linear sum of the time from +#' objects in `...`. +#' * If `cycle != NULL`, an `hms` object with a circular sum of the time +#' from objects in `...`. #' #' @family utility functions #' @export #' #' @examples -#' ## Linear non-vectorized sum +#' ## Non-vectorized sum in an linear time frame #' #' x <- c(as.POSIXct("2020-01-01 15:00:00"), as.POSIXct("1999-05-04 17:30:00")) #' y <- lubridate::as.interval(lubridate::dhours(7), as.Date("1970-05-08")) -#' sum_time(x, y, class = "duration") -#' #> [1] "142200s (~1.65 days)" # Expected +#' sum_time(x, y) +#' #> 39:30:00 # Expected #' #' ## Non-vectorized sum in a circular time frame of 24 hours #' #' x <- c(lubridate::hours(25), lubridate::dhours(5), lubridate::minutes(50)) -#' sum_time(x, circular = TRUE) +#' sum_time(x, cycle = lubridate::ddays()) #' #> 06:50:00 # Expected #' #' x <- c(hms::parse_hm("00:15"), hms::parse_hm("02:30"), hms::as_hms(NA)) -#' sum_time(x, circular = TRUE) +#' sum_time(x, cycle = lubridate::ddays()) #' #> NA # Expected -#' sum_time(x, circular = TRUE, na.rm = TRUE) +#' sum_time(x, cycle = lubridate::ddays(), na_rm = TRUE) #' #> 02:45:00 # Expected #' -#' ## Linear vectorized sum +#' ## Vectorized sum in an linear time frame #' #' x <- c(lubridate::dhours(6), NA) #' y <- c(hms::parse_hm("23:00"), hms::parse_hm("10:00")) -#' sum_time(x, y, vectorize = TRUE) +#' sum_times(x, y) #' #> 29:00:00 # Expected #' #> NA # Expected -#' sum_time(x, y, vectorize = TRUE, na.rm = TRUE) +#' sum_times(x, y, na_rm = TRUE) #' #> 29:00:00 # Expected #' #> 10:00:00 # Expected #' @@ -119,14 +178,23 @@ #' #' x <- c(lubridate::dhours(6), NA) #' y <- c(hms::parse_hm("23:00"), hms::parse_hm("10:00")) -#' sum_time(x, y, circular = TRUE, vectorize = TRUE) +#' sum_times(x, y, cycle = lubridate::ddays()) #' #> 05:00:00 # Expected #' #> NA # Expected -#' sum_time(x, y, circular = TRUE, vectorize = TRUE, na.rm = TRUE) +#' sum_times(x, y, cycle = lubridate::ddays(), na_rm = TRUE) #' #> 05:00:00 # Expected #' #> 10:00:00 # Expected -sum_time <- function(..., class = "hms", circular = FALSE, vectorize = FALSE, - na.rm = FALSE) { +sum_time <- function(..., cycle = NULL, na_rm = FALSE) { + build_sum(..., vectorize = FALSE, cycle = cycle, na_rm = na_rm) +} + +#' @rdname sum_time +#' @export +sum_times <- function(..., cycle = NULL, na_rm = FALSE) { + build_sum(..., vectorize = TRUE, cycle = cycle, na_rm = na_rm) +} + +build_sum <- function(..., vectorize = FALSE, cycle = NULL, na_rm = FALSE) { out <- list(...) assert_custom <- function(x) { @@ -136,22 +204,14 @@ sum_time <- function(..., class = "hms", circular = FALSE, vectorize = FALSE, checkmate::assert_multi_class(x, classes) } - choices <- tolower( - c("character", "integer", "double", "numeric", "Duration", - "Period", "difftime", "hms")) - - checkmate::assert_choice(tolower(class), choices) - checkmate::assert_flag(circular) checkmate::assert_flag(vectorize) - checkmate::assert_flag(na.rm) + checkmate::assert_number(cycle, lower = 0, null.ok = TRUE) + checkmate::assert_flag(na_rm) lapply(out, assert_custom) if (isTRUE(vectorize) && !(length(unique(vapply(out, length, integer(1)))) == 1)) { - cli::cli_abort(paste0( - "When 'vectorize' is 'TRUE', all values in '...' must have ", - "the same length." - )) + cli::cli_abort("All values in '...' must have the same length.") } normalize <- function(x) { @@ -162,18 +222,20 @@ sum_time <- function(..., class = "hms", circular = FALSE, vectorize = FALSE, } } - zero_nas <- function(x) dplyr::if_else(is.na(x), 0, x) - out <- lapply(out, normalize) - if(isTRUE(na.rm)) out <- lapply(out, zero_nas) + + if (isTRUE(na_rm)) { + out <- lapply(out, function(x) dplyr::if_else(is.na(x), 0, x)) + } if (isTRUE(vectorize)) { out <- Reduce("+", out) } else { out <- do.call("c", out) - out <- sum(out, na.rm = na.rm) + out <- sum(out, na.rm = na_rm) } - if (isTRUE(circular)) out <- flat_posixt(lubridate::as_datetime(out)) - convert(out, class, quiet = TRUE) + if (!is.null(cycle)) out <- out %% cycle + + hms::hms(out) } diff --git a/R/tbt.R b/R/tbt.R index 84e7522..55b8f48 100644 --- a/R/tbt.R +++ b/R/tbt.R @@ -96,5 +96,6 @@ tbt <- function(bt, gu) { checkmate::assert_class(gu, "hms") assert_identical(bt, gu, type = "length") - sum_time(gu, - bt, class = "Duration", circular = TRUE, vectorize = TRUE) + sum_times(gu, - bt, cycle = lubridate::ddays()) %>% + lubridate::as.duration() } diff --git a/data-raw/random_mctq.R b/data-raw/random_mctq.R index c126d90..c86bd71 100644 --- a/data-raw/random_mctq.R +++ b/data-raw/random_mctq.R @@ -70,23 +70,23 @@ std_mctq_par <- function() { ms_mean <- i$ms_mean ms_sd <- i$ms_sd - ms_min <- sum_time(ms_mean, - (3 * ms_sd), circular = TRUE) - ms_max <- sum_time(ms_mean, + (3 * ms_sd), circular = TRUE) + ms_min <- sum_time(ms_mean, - (3 * ms_sd), cycle = lubridate::ddays()) + ms_max <- sum_time(ms_mean, + (3 * ms_sd), cycle = lubridate::ddays()) sd_mean <- i$sd_mean sd_sd <- i$sd_sd - sd_min <- sum_time(sd_mean, - (3 * sd_sd), circular = TRUE) - sd_max <- sum_time(sd_mean, + (3 * sd_sd), circular = TRUE) + sd_min <- sum_time(sd_mean, - (3 * sd_sd), cycle = lubridate::ddays()) + sd_max <- sum_time(sd_mean, + (3 * sd_sd), cycle = lubridate::ddays()) cli::cli_h3(paste0( "Local time of sleep onset (SO_", i$suffix, ")" )) cli::cat_line() - mean <- sum_time(ms_mean, - (sd_mean / 2), circular = TRUE) - sd <- sum_time((ms_sd + sd_sd) / 2, circular = TRUE) - min <- sum_time(ms_min, - (sd_mean / 2), circular = TRUE) - max <- sum_time(ms_max, - (sd_mean / 2), circular = TRUE) + mean <- sum_time(ms_mean, - (sd_mean / 2), cycle = lubridate::ddays()) + sd <- sum_time((ms_sd + sd_sd) / 2, cycle = lubridate::ddays()) + min <- sum_time(ms_min, - (sd_mean / 2), cycle = lubridate::ddays()) + max <- sum_time(ms_max, - (sd_mean / 2), cycle = lubridate::ddays()) cat_(min, max, mean, sd) cli::cli_h3(paste0( @@ -94,10 +94,10 @@ std_mctq_par <- function() { )) cli::cat_line() - mean <- sum_time(ms_mean, + (sd_mean / 2), circular = TRUE) - sd <- sum_time((ms_sd + sd_sd) / 2, circular = TRUE) - min <- sum_time(ms_min, + (sd_mean / 2), circular = TRUE) - max <- sum_time(ms_max, + (sd_mean / 2), circular = TRUE) + mean <- sum_time(ms_mean, + (sd_mean / 2), cycle = lubridate::ddays()) + sd <- sum_time((ms_sd + sd_sd) / 2, cycle = lubridate::ddays()) + min <- sum_time(ms_min, + (sd_mean / 2), cycle = lubridate::ddays()) + max <- sum_time(ms_max, + (sd_mean / 2), cycle = lubridate::ddays()) cat_(min, max, mean, sd) cli::cli_h3(paste0( @@ -264,8 +264,10 @@ shift_mctq_par <- function() { sprep_mean <- i$sprep_mean sprep_sd <- i$sprep_sd - sprep_min <- sum_time(sprep_mean, - (3 * sprep_sd), circular = TRUE) - sprep_max <- sum_time(sprep_mean, + (3 * sprep_sd), circular = TRUE) + sprep_min <- sum_time(sprep_mean, - (3 * sprep_sd), + cycle = lubridate::ddays()) + sprep_max <- sum_time(sprep_mean, + (3 * sprep_sd), + cycle = lubridate::ddays()) cat_(sprep_min, sprep_max, sprep_mean, sprep_sd) cli::cli_h3(paste0( @@ -285,10 +287,10 @@ shift_mctq_par <- function() { )) cli::cat_line() - mean <- sum_time(sprep_mean, slat_mean, circular = TRUE) + mean <- sum_time(sprep_mean, slat_mean, cycle = lubridate::ddays()) sd <- sum_time(sprep_sd + slat_sd) - min <- sum_time(sprep_min, slat_min, circular = TRUE) - max <- sum_time(sprep_max, slat_max, circular = TRUE) + min <- sum_time(sprep_min, slat_min, cycle = lubridate::ddays()) + max <- sum_time(sprep_max, slat_max, cycle = lubridate::ddays()) cat_(min, max, mean, sd) cli::cli_h3(paste0( @@ -623,8 +625,8 @@ cat_ <- function(min, max, mean, sd) { } min_max <- function(mean, sd) { - min <- sum_time(mean, - (3 * sd), circular = TRUE) - max <- sum_time(mean, + (3 * sd), circular = TRUE) + min <- sum_time(mean, - (3 * sd), cycle = lubridate::ddays()) + max <- sum_time(mean, + (3 * sd), cycle = lubridate::ddays()) cat_(min, max, mean, sd) } diff --git a/man/mctq-package.Rd b/man/mctq-package.Rd index 4c71d92..005ee96 100644 --- a/man/mctq-package.Rd +++ b/man/mctq-package.Rd @@ -8,9 +8,7 @@ \description{ \if{html}{\figure{logo.png}{options: align='right' alt='logo' width='120'}} -A complete and consistent toolkit to process the Munich - ChronoType Questionnaire (MCTQ) for its three versions (standard, - micro, and shift). +A complete and consistent toolkit to process the Munich ChronoType Questionnaire (MCTQ) for its three versions (standard, micro, and shift). } \seealso{ Useful links: diff --git a/man/sum_time.Rd b/man/sum_time.Rd index 7f4f708..3dbf102 100644 --- a/man/sum_time.Rd +++ b/man/sum_time.Rd @@ -2,47 +2,30 @@ % Please edit documentation in R/sum_time.R \name{sum_time} \alias{sum_time} +\alias{sum_times} \title{Sum time objects} \usage{ -sum_time( - ..., - class = "hms", - circular = FALSE, - vectorize = FALSE, - na.rm = FALSE -) +sum_time(..., cycle = NULL, na_rm = FALSE) + +sum_times(..., cycle = NULL, na_rm = FALSE) } \arguments{ \item{...}{Objects belonging to one of the following classes: \code{Duration}, \code{Period}, \code{difftime}, \code{hms}, \code{POSIXct}, \code{POSIXlt}, or \code{Interval}.} -\item{class}{(optional) a string indicating the output class (default: -\code{"hms"}).} - -\item{circular}{(optional) a \code{logical} value indicating whether the sum -should be made in a circular time frame of 24 hours (clock hours) (default: -\code{FALSE}).} +\item{cycle}{(optional) a number indicating the cycle length in seconds. If +\code{NULL} the function will perform a linear sum (see Details to learn +more) (default: \code{NULL}).} -\item{vectorize}{(optional) a \code{logical} value indicating if the function must -operate in a vectorized fashion (default: \code{FALSE}).} - -\item{na.rm}{(optional) a \code{logical} value indicating if the function must +\item{na_rm}{(optional) a \code{logical} value indicating if the function must remove \code{NA} values while performing the sum (default: \code{FALSE}).} } \value{ \itemize{ -\item If \code{circular = TRUE} and \code{vectorize = FALSE}, an object of -the indicated class in \code{class} (default: \code{"hms"}) with the sum of the time -from objects in \code{...} in a circular time frame of 24 hours. -\item If \code{circular = FALSE} and \code{vectorize = FALSE}, an -object of the indicated class in \code{class} (default: \code{"hms"}) with a -linear sum of the time from objects in \code{...}. -\item If \code{circular = TRUE} and \code{vectorize = TRUE}, an object of the indicated -class in \code{class} (default: \code{"hms"}) with a vectorized sum of the time from -objects in \code{...} in a circular time frame of 24 hours. -\item If \code{circular = FALSE} and \code{vectorize = TRUE}, an object of the -indicated class in \code{class} (default: \code{"hms"}) with a vectorized and -linear sum of the time from objects in \code{...}. +\item If \code{cycle = NULL}, an \code{hms} object with a linear sum of the time from +objects in \code{...}. +\item If \code{cycle != NULL}, an \code{hms} object with a circular sum of the time +from objects in \code{...}. } } \description{ @@ -51,45 +34,110 @@ linear sum of the time from objects in \code{...}. \code{sum_time()} returns the sum of the time from different kinds of date/time objects. -This function supports vectorized operations and can also be set to work with -a circular time frame of 24 hours. +\code{sum_times()} returns the vectorized sum of the time from different kinds of +date/time objects. + +Both functions can be set to work with a circular time frame (see Details to +learn more). } \details{ -\subsection{\code{class} argument}{ +\subsection{\code{sum_time()} versus \code{sum_times()}}{ + +\code{sum_time()} behaves similar to \code{sum()}, in the sense that it aggregates the +time lengths of values in \code{...} into a single data point. For example, +\code{sum_time(c(x, y), z)} will have the same output as \code{sum_time(x, y, z)}. + +\code{sum_times()} performs a different type of sum (a vectorized one). Instead of +aggregate the time lengths, the function perform a paired sum between +elements. For example, \code{sum_time(c(x, y), c(w, z))} will return a vector like +\code{c(sum_time(x, w), sum_time(y, z))}. Because of that, \code{sum_times()} requires +that all objects in \code{...} have the same length. +} + +\subsection{Linear versus circular sum}{ -\code{sum_time()} is integrated with \code{\link[=convert]{convert()}}. That way you can choose -what class of object you prefer as output. +Time can have different "shapes". -Valid \code{class} values are: \code{"character"}, \code{"integer"}, \code{"double"}, -\code{"numeric"}, \code{"Duration"}, \code{"Period"}, \code{"difftime"}, and \code{"hms"} (case -insensitive). +If the objective is to measure the duration of an event, time is usually +measured considering a linear frame, with a fixed point of +\href{https://en.wikipedia.org/wiki/Origin_(mathematics)}{origin}. In this +context, the time value distance itself to infinity in relation to the +origin.\preformatted{ B + |----------| + A + |---------------------| + - inf inf + +<----------------------------|----------|----------|-------> + s 0 5 10 s + origin + +A + B = 10 + 5 = 15s +} + +But that's not the only possible "shape" of time, as it can also be measured +in other contexts. + +In a "time of day" context, time will be linked to the rotation of the +earth, "resetting" when a new rotation cycle starts. That brings a different +kind of shape to time: a circular shape. With this shape the time value +encounters the origin at the end of the cycle (every 24 hours or 86400 +seconds).\preformatted{ - <--- h ---> + + origin + . . . 0 . . . + . . + . . + . . + . . + . . + 18 6 + . . + . . + . . + . . + . . + . . . 12 . . . + +18 + 6 = 0h } -\subsection{\code{vectorize} argument}{ +If we transpose this circular time frame to a linear one, it would look like +this:\preformatted{-----|---------------|---------------|---------------|-----> + 0h 12h 0h 12h + origin origin +} -If \code{vectorize = FALSE} (default), \code{sum_time} will combine and sum all time -values in \code{...}. That is, \code{sum_time(c(x, y), z)} will have the same -output as \code{sum_time(x, y, z)}. +Note that now the origin is not fixed, but cyclical. -However, if \code{vectorize = TRUE}, \code{sum_time()} will require that all objects in -\code{...} have the same length, and will perform a paired sum between elements. -That is, \code{sum_time(c(x, y), c(w, z))} will return a vector like -\code{c(sum_time(x, w), sum_time(y, z))}. +\code{sum_time()} and \code{sum_times()} can both operate in either a linear or a +circular fashion. If \code{cycle = NULL} (default), the function will use a +linear approach. Else, the function will use a circular approach relative to +the cycle length (e.g, \code{cycle = 86400} (1 day)). } \subsection{\code{POSIXt} objects}{ \code{POSIXt} values in \code{...} will be stripped of their dates. Only the time will be considered. + +Both \code{POSIXct} and \code{POSIXlt} are objects that inherits the class \code{POSIXt}. +Learn more about it in \link[base:DateTimeClasses]{base::DateTimeClasses}. } \subsection{\code{Period} objects}{ -\code{Period} objects are a special time of object developed by the +\code{Period} objects are a special type of object developed by the \link[lubridate:lubridate-package]{lubridate} team that represents "human units", ignoring possible time irregularities. That is to say that 1 day as \code{Period} -will always represent 1 day in the timeline. \code{sum_time()} ignores that -property of \code{Period} objects, treating them like objects of class \code{Duration}. +will always represent 1 day in the timeline. \code{sum_time()} and \code{sum_times()} +ignores that property of \code{Period} objects, treating them like objects of +class \code{Duration}. +} + +\subsection{\code{Interval} objects}{ + +By using \code{Interval} objects in \code{...}, \code{sum_time()} and \code{sum_times()} will +consider only their time lengths. That is, the amount of seconds of the +intervals. } \subsection{Timeline irregularities}{ @@ -100,33 +148,33 @@ it must be considered when doing time arithmetic. } } \examples{ -## Linear non-vectorized sum +## Non-vectorized sum in an linear time frame x <- c(as.POSIXct("2020-01-01 15:00:00"), as.POSIXct("1999-05-04 17:30:00")) y <- lubridate::as.interval(lubridate::dhours(7), as.Date("1970-05-08")) -sum_time(x, y, class = "duration") -#> [1] "142200s (~1.65 days)" # Expected +sum_time(x, y) +#> 39:30:00 # Expected ## Non-vectorized sum in a circular time frame of 24 hours x <- c(lubridate::hours(25), lubridate::dhours(5), lubridate::minutes(50)) -sum_time(x, circular = TRUE) +sum_time(x, cycle = lubridate::ddays()) #> 06:50:00 # Expected x <- c(hms::parse_hm("00:15"), hms::parse_hm("02:30"), hms::as_hms(NA)) -sum_time(x, circular = TRUE) +sum_time(x, cycle = lubridate::ddays()) #> NA # Expected -sum_time(x, circular = TRUE, na.rm = TRUE) +sum_time(x, cycle = lubridate::ddays(), na_rm = TRUE) #> 02:45:00 # Expected -## Linear vectorized sum +## Vectorized sum in an linear time frame x <- c(lubridate::dhours(6), NA) y <- c(hms::parse_hm("23:00"), hms::parse_hm("10:00")) -sum_time(x, y, vectorize = TRUE) +sum_times(x, y) #> 29:00:00 # Expected #> NA # Expected -sum_time(x, y, vectorize = TRUE, na.rm = TRUE) +sum_times(x, y, na_rm = TRUE) #> 29:00:00 # Expected #> 10:00:00 # Expected @@ -134,10 +182,10 @@ sum_time(x, y, vectorize = TRUE, na.rm = TRUE) x <- c(lubridate::dhours(6), NA) y <- c(hms::parse_hm("23:00"), hms::parse_hm("10:00")) -sum_time(x, y, circular = TRUE, vectorize = TRUE) +sum_times(x, y, cycle = lubridate::ddays()) #> 05:00:00 # Expected #> NA # Expected -sum_time(x, y, circular = TRUE, vectorize = TRUE, na.rm = TRUE) +sum_times(x, y, cycle = lubridate::ddays(), na_rm = TRUE) #> 05:00:00 # Expected #> 10:00:00 # Expected } diff --git a/tests/testthat/test-sum_time.R b/tests/testthat/test-sum_time.R index da3fa33..a79056f 100644 --- a/tests/testthat/test-sum_time.R +++ b/tests/testthat/test-sum_time.R @@ -1,4 +1,4 @@ -test_that("sum_time() | non-vectorized test", { +test_that("sum_time() | linear test", { expect_equal(sum_time(c(lubridate::dhours(1), lubridate::dminutes(30)), lubridate::hours(1), c(as.difftime(1, units = "hours"), @@ -9,12 +9,11 @@ test_that("sum_time() | non-vectorized test", { "1970-01-01 01:00:00")), lubridate::as.interval(lubridate::dhours(1), as.Date("1970-01-01")), - class = "duration", - circular = FALSE, - vectorize = FALSE, - na.rm = FALSE), - lubridate::dhours(30)) # 30:00:00 - expect_equal(sum_time(c(lubridate::dhours(1), lubridate::dminutes(30)), + cycle = NULL, + na_rm = FALSE), + hms::hms(as.numeric(lubridate::dhours(30)))) # 30:00:00 + expect_equal(sum_time(hms::as_hms(NA), # ! + c(lubridate::dhours(1), lubridate::dminutes(30)), lubridate::hours(1), c(as.difftime(1, units = "hours"), as.difftime(30, units = "mins")), @@ -24,11 +23,9 @@ test_that("sum_time() | non-vectorized test", { "1970-01-01 01:00:00")), lubridate::as.interval(lubridate::dhours(1), as.Date("1970-01-01")), - class = "hms", - circular = TRUE, # ! - vectorize = FALSE, - na.rm = FALSE), - hms::parse_hm("06:00")) # 06:00 | 30 - 24 + cycle = NULL, + na_rm = FALSE), # ! + hms::as_hms(NA)) expect_equal(sum_time(hms::as_hms(NA), # ! c(lubridate::dhours(1), lubridate::dminutes(30)), lubridate::hours(1), @@ -40,13 +37,13 @@ test_that("sum_time() | non-vectorized test", { "1970-01-01 01:00:00")), lubridate::as.interval(lubridate::dhours(1), as.Date("1970-01-01")), - class = "period", - circular = FALSE, - vectorize = FALSE, - na.rm = FALSE), # ! - lubridate::as.period(NA)) - expect_equal(sum_time(hms::as_hms(NA), # ! - c(lubridate::dhours(1), lubridate::dminutes(30)), + cycle = NULL, + na_rm = TRUE), # ! + hms::hms(as.numeric(lubridate::dhours(30)))) # 30:00:00 +}) + +test_that("sum_time() | circular test", { + expect_equal(sum_time(c(lubridate::dhours(1), lubridate::dminutes(30)), lubridate::hours(1), c(as.difftime(1, units = "hours"), as.difftime(30, units = "mins")), @@ -56,11 +53,9 @@ test_that("sum_time() | non-vectorized test", { "1970-01-01 01:00:00")), lubridate::as.interval(lubridate::dhours(1), as.Date("1970-01-01")), - class = "period", - circular = FALSE, - vectorize = FALSE, - na.rm = TRUE), # ! - lubridate::as.period(hms::hms(108000))) # 30:00:00 + cycle = lubridate::ddays(), # ! + na_rm = FALSE), + hms::parse_hm("06:00")) # 06:00 | 30 - 24 expect_equal(sum_time(hms::as_hms(NA), c(lubridate::dhours(1), lubridate::dminutes(30)), lubridate::hours(1), @@ -72,137 +67,25 @@ test_that("sum_time() | non-vectorized test", { "1970-01-01 01:00:00")), lubridate::as.interval(lubridate::dhours(1), as.Date("1970-01-01")), - class = "difftime", - circular = TRUE, # ! - vectorize = FALSE, - na.rm = TRUE), - lubridate::as.difftime(21600, units = "secs")) -}) - -test_that("sum_time()| vectorized test", { - expect_equal(sum_time(c(lubridate::dhours(1), lubridate::dminutes(30)), - c(lubridate::hours(1), lubridate::hours(1)), - c(as.difftime(1, units = "hours"), - as.difftime(30, units = "mins")), - c(hms::parse_hm("02:00"), hms::parse_hm("02:00")), - c(lubridate::as_datetime("1970-01-01 20:00:00"), - lubridate::as_datetime("1970-01-01 10:00:00")), - as.POSIXlt( - c(lubridate::as_datetime("1970-01-01 01:00:00"), - lubridate::as_datetime("1970-01-01 02:00:00"))), - c(lubridate::as.interval(lubridate::dhours(4), - as.Date("1970-01-01")), - lubridate::as.interval(lubridate::dhours(1), - as.Date("1970-01-01"))), - class = "duration", - circular = FALSE, - vectorize = TRUE, - na.rm = FALSE), - c(lubridate::dhours(30), - lubridate::dhours(17))) # 30:00:00 | 17:00:00 - expect_equal(sum_time(c(lubridate::dhours(1), lubridate::dminutes(30)), - c(lubridate::hours(1), lubridate::hours(1)), - c(as.difftime(1, units = "hours"), - as.difftime(30, units = "mins")), - c(hms::parse_hm("02:00"), hms::parse_hm("02:00")), - c(lubridate::as_datetime("1970-01-01 20:00:00"), - lubridate::as_datetime("1970-01-01 10:00:00")), - as.POSIXlt( - c(lubridate::as_datetime("1970-01-01 01:00:00"), - lubridate::as_datetime("1970-01-01 02:00:00"))), - c(lubridate::as.interval(lubridate::dhours(4), - as.Date("1970-01-01")), - lubridate::as.interval(lubridate::dhours(1), - as.Date("1970-01-01"))), - class = "hms", - circular = TRUE, # ! - vectorize = TRUE, - na.rm = FALSE), - c(hms::parse_hm("06:00"), - hms::parse_hm("17:00"))) # 06:00:00 | 17:00:00 - expect_equal(sum_time(c(hms::as_hms(NA), hms::as_hms(NA)), # ! - c(lubridate::dhours(1), lubridate::dminutes(30)), - c(lubridate::hours(1), lubridate::hours(1)), - c(as.difftime(1, units = "hours"), - as.difftime(30, units = "mins")), - c(hms::parse_hm("02:00"), hms::parse_hm("02:00")), - c(lubridate::as_datetime("1970-01-01 20:00:00"), - lubridate::as_datetime("1970-01-01 10:00:00")), - as.POSIXlt( - c(lubridate::as_datetime("1970-01-01 01:00:00"), - lubridate::as_datetime("1970-01-01 02:00:00"))), - c(lubridate::as.interval(lubridate::dhours(4), - as.Date("1970-01-01")), - lubridate::as.interval(lubridate::dhours(1), - as.Date("1970-01-01"))), - class = "period", - circular = FALSE, - vectorize = TRUE, - na.rm = FALSE), # ! - c(lubridate::as.period(NA), - lubridate::as.period(NA))) - expect_equal(sum_time(c(hms::as_hms(NA), hms::as_hms(NA)), # ! - c(lubridate::dhours(1), lubridate::dminutes(30)), - c(lubridate::hours(1), lubridate::hours(1)), - c(as.difftime(1, units = "hours"), - as.difftime(30, units = "mins")), - c(hms::parse_hm("02:00"), hms::parse_hm("02:00")), - c(lubridate::as_datetime("1970-01-01 20:00:00"), - lubridate::as_datetime("1970-01-01 10:00:00")), - as.POSIXlt( - c(lubridate::as_datetime("1970-01-01 01:00:00"), - lubridate::as_datetime("1970-01-01 02:00:00"))), - c(lubridate::as.interval(lubridate::dhours(4), - as.Date("1970-01-01")), - lubridate::as.interval(lubridate::dhours(1), - as.Date("1970-01-01"))), - class = "period", - circular = FALSE, - vectorize = TRUE, - na.rm = TRUE), # ! - c(lubridate::as.period(hms::hms(108000)), - lubridate::as.period(hms::hms(61200)))) # 30:00:00 | 17:00:00 - expect_equal(sum_time(c(hms::as_hms(NA), hms::as_hms(NA)), - c(lubridate::dhours(1), lubridate::dminutes(30)), - c(lubridate::hours(1), lubridate::hours(1)), - c(as.difftime(1, units = "hours"), - as.difftime(30, units = "mins")), - c(hms::parse_hm("02:00"), hms::parse_hm("02:00")), - c(lubridate::as_datetime("1970-01-01 20:00:00"), - lubridate::as_datetime("1970-01-01 10:00:00")), - as.POSIXlt( - c(lubridate::as_datetime("1970-01-01 01:00:00"), - lubridate::as_datetime("1970-01-01 02:00:00"))), - c(lubridate::as.interval(lubridate::dhours(4), - as.Date("1970-01-01")), - lubridate::as.interval(lubridate::dhours(1), - as.Date("1970-01-01"))), - class = "difftime", - circular = TRUE, # ! - vectorize = TRUE, - na.rm = TRUE), - c(lubridate::as.difftime(21600, units = "secs"), - lubridate::as.difftime(61200, units = "secs"))) + cycle = lubridate::ddays(), + na_rm = TRUE), + hms::parse_hm("06:00")) }) -test_that("sum_time() | error test", { - expect_error(sum_time(1, class = "hms", circular = TRUE, - vectorize = TRUE, na.rm = TRUE), +test_that("build_sum() | error test", { + expect_error(build_sum(1, vectorize = FALSE, cycle = NULL, na_rm = FALSE), "Assertion on 'x' failed") - expect_error(sum_time(hms::hms(1), class = 1, circular = TRUE, - vectorize = TRUE, na.rm = TRUE), - "Assertion on 'tolower\\(class\\)' failed") - expect_error(sum_time(hms::hms(1), class = "hms", circular = "", - vectorize = TRUE, na.rm = TRUE), - "Assertion on 'circular' failed") - expect_error(sum_time(hms::hms(1), class = "hms", circular = TRUE, - vectorize = "", na.rm = TRUE), + expect_error(build_sum(hms::hms(1), vectorize = "", cycle = NULL, + na_rm = TRUE), "Assertion on 'vectorize' failed") - expect_error(sum_time(hms::hms(1), class = "hms", circular = TRUE, - vectorize = TRUE, na.rm = ""), - "Assertion on 'na.rm' failed") + expect_error(build_sum(hms::hms(1), vectorize = FALSE, cycle = "", + na_rm = FALSE), + "Assertion on 'cycle' failed") + expect_error(build_sum(hms::hms(1), vectorize = FALSE, cycle = NULL, + na_rm = ""), + "Assertion on 'na_rm' failed") - expect_error(sum_time(hms::hms(1), c(hms::hms(1), hms::hms(1)), + expect_error(build_sum(hms::hms(1), c(hms::hms(1), hms::hms(1)), vectorize = TRUE), - "When 'vectorize' is 'TRUE', all values in '...' must ") + "All values in '...' must ") }) diff --git a/tests/testthat/test-sum_times.R b/tests/testthat/test-sum_times.R new file mode 100644 index 0000000..044a045 --- /dev/null +++ b/tests/testthat/test-sum_times.R @@ -0,0 +1,98 @@ +test_that("sum_times()| linear test", { + expect_equal(sum_times(c(lubridate::dhours(1), lubridate::dminutes(30)), + c(lubridate::hours(1), lubridate::hours(1)), + c(as.difftime(1, units = "hours"), + as.difftime(30, units = "mins")), + c(hms::parse_hm("02:00"), hms::parse_hm("02:00")), + c(lubridate::as_datetime("1970-01-01 20:00:00"), + lubridate::as_datetime("1970-01-01 10:00:00")), + as.POSIXlt( + c(lubridate::as_datetime("1970-01-01 01:00:00"), + lubridate::as_datetime( + "1970-01-01 02:00:00"))), + c(lubridate::as.interval(lubridate::dhours(4), + as.Date("1970-01-01")), + lubridate::as.interval(lubridate::dhours(1), + as.Date("1970-01-01"))), + cycle = NULL, + na_rm = FALSE), + c(hms::hms(108000), + hms::hms(61200))) # 30:00:00 | 17:00:00 + expect_equal(sum_times(c(hms::as_hms(NA), hms::as_hms(NA)), # ! + c(lubridate::dhours(1), lubridate::dminutes(30)), + c(lubridate::hours(1), lubridate::hours(1)), + c(as.difftime(1, units = "hours"), + as.difftime(30, units = "mins")), + c(hms::parse_hm("02:00"), hms::parse_hm("02:00")), + c(lubridate::as_datetime("1970-01-01 20:00:00"), + lubridate::as_datetime("1970-01-01 10:00:00")), + as.POSIXlt( + c(lubridate::as_datetime("1970-01-01 01:00:00"), + lubridate::as_datetime("1970-01-01 02:00:00"))), + c(lubridate::as.interval(lubridate::dhours(4), + as.Date("1970-01-01")), + lubridate::as.interval(lubridate::dhours(1), + as.Date("1970-01-01"))), + cycle = NULL, + na_rm = FALSE), # ! + c(hms::as_hms(NA), hms::as_hms(NA))) + expect_equal(sum_times(c(hms::as_hms(NA), hms::as_hms(NA)), # ! + c(lubridate::dhours(1), lubridate::dminutes(30)), + c(lubridate::hours(1), lubridate::hours(1)), + c(as.difftime(1, units = "hours"), + as.difftime(30, units = "mins")), + c(hms::parse_hm("02:00"), hms::parse_hm("02:00")), + c(lubridate::as_datetime("1970-01-01 20:00:00"), + lubridate::as_datetime("1970-01-01 10:00:00")), + as.POSIXlt( + c(lubridate::as_datetime("1970-01-01 01:00:00"), + lubridate::as_datetime("1970-01-01 02:00:00"))), + c(lubridate::as.interval(lubridate::dhours(4), + as.Date("1970-01-01")), + lubridate::as.interval(lubridate::dhours(1), + as.Date("1970-01-01"))), + cycle = NULL, + na_rm = TRUE), # ! + c(hms::hms(108000), + hms::hms(61200))) # 30:00:00 | 17:00:00 +}) + +test_that("sum_times()| circular test", { + expect_equal(sum_times(c(lubridate::dhours(1), lubridate::dminutes(30)), + c(lubridate::hours(1), lubridate::hours(1)), + c(as.difftime(1, units = "hours"), + as.difftime(30, units = "mins")), + c(hms::parse_hm("02:00"), hms::parse_hm("02:00")), + c(lubridate::as_datetime("1970-01-01 20:00:00"), + lubridate::as_datetime("1970-01-01 10:00:00")), + as.POSIXlt( + c(lubridate::as_datetime("1970-01-01 01:00:00"), + lubridate::as_datetime( + "1970-01-01 02:00:00"))), + c(lubridate::as.interval(lubridate::dhours(4), + as.Date("1970-01-01")), + lubridate::as.interval(lubridate::dhours(1), + as.Date("1970-01-01"))), + cycle = lubridate::ddays(), # ! + na_rm = FALSE), + c(hms::parse_hm("06:00"), + hms::parse_hm("17:00"))) # 06:00:00 | 17:00:00 + expect_equal(sum_times(c(hms::as_hms(NA), hms::as_hms(NA)), + c(lubridate::dhours(1), lubridate::dminutes(30)), + c(lubridate::hours(1), lubridate::hours(1)), + c(as.difftime(1, units = "hours"), + as.difftime(30, units = "mins")), + c(hms::parse_hm("02:00"), hms::parse_hm("02:00")), + c(lubridate::as_datetime("1970-01-01 20:00:00"), + lubridate::as_datetime("1970-01-01 10:00:00")), + as.POSIXlt( + c(lubridate::as_datetime("1970-01-01 01:00:00"), + lubridate::as_datetime("1970-01-01 02:00:00"))), + c(lubridate::as.interval(lubridate::dhours(4), + as.Date("1970-01-01")), + lubridate::as.interval(lubridate::dhours(1), + as.Date("1970-01-01"))), + cycle = lubridate::ddays(), # ! + na_rm = TRUE), + c(hms::hms(21600), hms::hms(61200))) +})