Arithmetic with hms objects

I'm trying to add a specified number of minutes to a time (formatted as an hms object) and then compare it with another time (also formatted as hms). Please see my reprex below.

library(tidyverse)
library(hms)

df <- tribble(~ Time1, ~Minutes, ~Time2,
              "16:45:00", 60, "17:00:00",
              "18:30:00", 15, "19:00:00") %>%
  mutate_at(c("Time1", "Time2"), as_hms)

print(df)
# A tibble: 2 x 3
  Time1  Minutes Time2 
  <time>   <dbl> <time>
1 16:45       60 17:00 
2 18:30       15 19:00 

I would like to check whether Time2 is earlier or later than the sum of Time1 and Minutes and report the difference between these two in hours. I'm able to successfully compare the two.

df %>% mutate(New_Time1 = if_else(Time2 > Time1 + hms(minutes = Minutes), "Before", "After"))
# A tibble: 2 x 4
  Time1  Minutes Time2  New_Time1
  <time>   <dbl> <time> <chr>    
1 16:45       60 17:00  After    
2 18:30       15 19:00  Before

However, when I try to calculate the actual time difference, I get the following error.

df %>% mutate(New_Time1 = difftime(Time2, Time1 + hms(minutes = Minutes), units = "hours"))
Error in as.POSIXct.default(time2) : 
  do not know how to convert 'time2' to class “POSIXct”

Clearly, difftime doesn't like something about the way I'm going about computing the difference but I'm not able to figure out how to fix it.

Can someone please help? A tidyverse solution would be preferred.

Since hms produces special type of time, difftime doesn't know how to deal with it and you get the error. Luckily, you can actually use literal arithmetic with it to get the result you need:

library(tidyverse)
library(hms)

df <- tribble(~ Time1, ~Minutes, ~Time2,
              "16:45:00", 60, "17:00:00",
              "18:30:00", 15, "19:00:00") %>%
  mutate_at(c("Time1", "Time2"), as_hms)

print(df)
#> # A tibble: 2 x 3
#>   Time1  Minutes Time2 
#>   <time>   <dbl> <time>
#> 1 16:45       60 17:00 
#> 2 18:30       15 19:00

df %>% 
  mutate(New_Time1 = (Time2 - (Time1 + hms(minutes = Minutes)))/lubridate::dhours(1)) 
#> # A tibble: 2 x 4
#>   Time1  Minutes Time2  New_Time1
#>   <time>   <dbl> <time>     <dbl>
#> 1 16:45       60 17:00      -0.75
#> 2 18:30       15 19:00       0.25

Created on 2020-01-21 by the reprex package (v0.3.0)

The part about /lubridate::dhours(1) converts resulting time into hours as I've noticed that this is the unit you've used in your example. By default it'll return seconds.

2 Likes

Pretty slick @mishabalyasin! Thanks very much.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.