[Maturing]

sum_time() returns the sum of the time from different kinds of date/time objects.

vct_sum_time() 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).

sum_time(..., cycle = NULL, reverse = TRUE, na_rm = FALSE)

vct_sum_time(..., cycle = NULL, reverse = TRUE, na_rm = FALSE)

Arguments

...

Objects belonging to one of the following classes: Duration, difftime, or hms, POSIXct, POSIXlt, or Interval.

cycle

(optional) A numeric or Duration object of length 1, equal or greater than 0, indicating the cycle length in seconds. If NULL the function will perform a linear sum (see Details to learn more) (default: NULL).

reverse

(optional) A logical value indicating if the function must use a reverse cycle for negative sums (see Details to learn more) (default: TRUE).

na_rm

(optional) a logical value indicating if the function must remove NA values while performing the sum (default: FALSE).

Value

  • If cycle = NULL, a Duration object with a linear sum of the time from objects in ....

  • If cycle != NULL, a Duration object with a circular sum of the time from objects in ....

Details

sum_time() versus vct_sum_time()

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).

vct_sum_time() performs a different type of sum (a vectorized one). Instead of aggregating the time lengths, the function perform a paired sum between elements. For example, vct_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, vct_sum_time() requires that all objects in ... have the same length.

Linear versus circular time

Time can have different "shapes".

If the objective is to measure the duration (time span) of an event, time is usually measured considering a linear frame, with a fixed point of origin. 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 each cycle.

               - <--- 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 fix, but cyclical.

sum_time() and vct_sum_time() 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)).

Fractional time

sum_time() uses the %% operator to cycle values. Hence, it can be subject to catastrophic loss of accuracy if values in ... are fractional and much larger than cycle. A warning is given if this is detected.

%% is a builtin R function that operates like this:

function(a, b) {
    a - floor(a / b) * b
}

Negative time cycling

If the sum of the time is negative, with a cycle assigned and reverse = FALSE, sum_time() and vtc_sum_time() will perform the cycle considering the absolute value of the sum and return the result with a negative signal.

However, If the sum of the time have a negative value, with a cycle assigned and reverse = TRUE (default), sum_time() and vtc_sum_time() will perform the cycle in reverse, relative to its origin.

Example: If the sum of the time have a -30h time span in a reversed cycle of 24h, the result will be 18h. By removing the full cycles of -30h you will get -6h (-30 + 24), and -6h relative to the origin will be 18h.

               - <--- h ---> +
                    origin
                . . . 0 . . .
              .                 .
            .                   .
           .                     .
          .                       .
         .                         .
    (-6) 18                        6 (-18)
         .                         .
          .                       .
           .                     .
            .                   .
             .                 .
                . . . 12 . . .
                    (-12)

Period objects

Period objects are special type of objects developed by the lubridate team that represents "human units", ignoring possible timeline irregularities. That is to say that 1 day as Period can have different time spans, when looking to a timeline after a irregularity event.

Since the time span of a Period object can fluctuate, sum_time() and vct_sum_time() don't accept this kind of object. You can transform it to a Duration object and still use the functions, but beware that this can produce errors.

Learn more about Period objects in the Dates and times chapter of Wickham & Grolemund book (n.d.).

POSIXt objects

POSIXt objects 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 ?DateTimeClasses.

Interval objects

By using Interval objects in ..., sum_time() and vct_sum_time() will consider only their time spans. That is, the amount of seconds of the intervals.

Learn more about Interval objects in the Dates and times chapter of Wickham & Grolemund (n.d.).

Timeline irregularities

This function does not take into account timeline irregularities (e.g., leap years, DST, leap seconds). This may not be an issue for most people, but it must be considered when doing time arithmetic.

References

Wickham, H., & Grolemund, G. (n.d.). R for data science. (n.p.). https://r4ds.had.co.nz

Examples

## 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)
#> [1] "142200s (~1.65 days)"
#> [1] "142200s (~1.65 days)" # 39:30:00 # Expected

## Non-vectorized sum in a circular time frame of 24 hours

x <- c(lubridate::dhours(25), lubridate::dhours(5), lubridate::dminutes(50))
sum_time(x, cycle = lubridate::ddays())
#> [1] "24600s (~6.83 hours)"
#> [1] "24600s (~6.83 hours)" # 06:50:00 # Expected

x <- c(hms::parse_hm("00:15"), hms::parse_hm("02:30"), hms::as_hms(NA))
sum_time(x, cycle = lubridate::ddays())
#> [1] NA
#> NA # Expected
sum_time(x, cycle = lubridate::ddays(), na_rm = TRUE)
#> [1] "9900s (~2.75 hours)"
#> [1] "9900s (~2.75 hours)" # 02:45:00 # Expected

x <- c(lubridate::dhours(-12), lubridate::dhours(-13))
sum_time(x, cycle = lubridate::ddays(), reverse = FALSE)
#> [1] "-3600s (~-1 hours)"
#> [1] "-3600s (~-1 hours)" # -01:00:00 # Expected

x <- c(lubridate::dhours(-12), lubridate::dhours(-13))
sum_time(x, cycle = lubridate::ddays(), reverse = TRUE)
#> [1] "82800s (~23 hours)"
#> [1] "82800s (~23 hours)" # 23:00:00 # Expected

## 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"))
vct_sum_time(x, y)
#> [1] "104400s (~1.21 days)" NA                    
#> [1] "104400s (~1.21 days)" NA # 29:00:00 NA # Expected
vct_sum_time(x, y, na_rm = TRUE)
#> [1] "104400s (~1.21 days)" "36000s (~10 hours)"  
#> [1] "104400s (~1.21 days)" "36000s (~10 hours)" # Expected

## Vectorized sum in a circular time frame of 24 hours

x <- c(lubridate::dhours(6), NA)
y <- c(hms::parse_hm("23:00"), hms::parse_hm("10:00"))
vct_sum_time(x, y, cycle = lubridate::ddays())
#> [1] "18000s (~5 hours)" NA                 
#> [1] "18000s (~5 hours)" NA  # Expected
vct_sum_time(x, y, cycle = lubridate::ddays(), na_rm = TRUE)
#> [1] "18000s (~5 hours)"  "36000s (~10 hours)"
#> [1] "18000s (~5 hours)"  "36000s (~10 hours)" # Expected

x <- c(lubridate::dhours(-49), lubridate::dhours(-24))
y <- c(hms::parse_hm("24:00"), - hms::parse_hm("06:00"))
vct_sum_time(x, y, cycle = lubridate::ddays(), reverse = FALSE)
#> [1] "-3600s (~-1 hours)"  "-21600s (~-6 hours)"
#> [1] "-3600s (~-1 hours)"  "-21600s (~-6 hours)" # Expected

x <- c(lubridate::dhours(-49), lubridate::dhours(-24))
y <- c(hms::parse_hm("24:00"), - hms::parse_hm("06:00"))
vct_sum_time(x, y, cycle = lubridate::ddays(), reverse = TRUE)
#> [1] "82800s (~23 hours)" "64800s (~18 hours)"
#> [1] "82800s (~23 hours)" "64800s (~18 hours)" # Expected