Tibble not accepting lubridate hms()


#1

I’m trying to mutate a tibble with lubridate’s hms() function but it’s throwing an error.

Basic examples:

library(tidyverse)
library(lubridate)

x <- c("09:10:01", "09:10:02", "09:10:03")

# This works...
hms(x)
#> [1] "9H 10M 1S" "9H 10M 2S" "9H 10M 3S"

# But not this...
tibble(time = hms(x))
#> Error in x < 0: cannot compare Period to Duration:
#> coerce with 'as.numeric' first.

There’s no problem with mdy_hms, though:

library(tidyverse)
library(lubridate)

x <- c("10/01/2018 09:10:01", "10/01/2018 09:10:02", "10/01/2018 09:10:03")
tibble(time = mdy_hms(x))
#> # A tibble: 3 x 1
#>   time               
#>   <dttm>             
#> 1 2018-10-01 09:10:01
#> 2 2018-10-01 09:10:02
#> 3 2018-10-01 09:10:03

Nor with a regular data frame…

library(tidyverse)
library(lubridate)

x <- c("09:10:01", "09:10:02", "09:10:03")

str(data.frame(time =  hms(x)))
#> 'data.frame':    3 obs. of  1 variable:
#>  $ time:Formal class 'Period' [package "lubridate"] with 6 slots
#>   .. ..@ .Data : num  1 2 3
#>   .. ..@ year  : num  0 0 0
#>   .. ..@ month : num  0 0 0
#>   .. ..@ day   : num  0 0 0
#>   .. ..@ hour  : num  9 9 9
#>   .. ..@ minute: num  10 10 10

#2

Hmm, I can’t quite figure this one out. I thought the difference between mdy_hms() and hms() was that the latter is a duration (i.e. the number of seconds, converted to readable units of time, but that doesn’t seem to be the case, as you’ve shown it.

You can convert it to seconds and store it as a tibble, but that doesn’t strike me as being the desired behaviour (though I admittedly don’t know much about it).

If you don’t get help here, you might want to open an issue on GH.

library(tidyverse)
library(lubridate)
#> 
#> Attaching package: 'lubridate'
#> The following object is masked from 'package:base':
#> 
#>     date

x <- c("09:10:01", "09:10:02", "09:10:03")

tibble(time = hms(x))
#> Error in x < 0: cannot compare Period to Duration:
#> coerce with 'as.numeric' first.

tibble(time = as.numeric(hms(x)))
#> # A tibble: 3 x 1
#>    time
#>   <dbl>
#> 1 33001
#> 2 33002
#> 3 33003

Created on 2018-01-18 by the reprex package (v0.1.1.9000).


#3

Just noticed something else so editing this…

The are different, as @mara says, because their classes are very different

x <- lubridate::hms(c("09:10:01", "09:10:02", "09:10:03"))
class(x)
#> [1] "Period"
#> attr(,"package")
#> [1] "lubridate"

y <- lubridate::mdy_hms(c("09:10:01", "09:10:02", "09:10:03"))
#> Warning: All formats failed to parse. No formats found.
class(y)
#> [1] "POSIXct" "POSIXt"

There is an issue on git that seems to be vaguely related to this:

which has been combined with another similar issue.

I dug into the code a tiny bit and couldn’t see where tibble discriminates between a POSIX time and a Period class. There are a lot of places in tibble that seem to strip away the class before processing.

There is a comment in the doc’s about what tibble will accept:

#' # data frames can only contain 1d atomic vectors and lists
#' # and can not contain POSIXlt

x, the instance of a Period class, has mulitple slots in it but a POSIX time does not:

x <- lubridate::hms(c("09:10:01", "09:10:02", "09:10:03"))
str(x)
#> Formal class 'Period' [package "lubridate"] with 6 slots
#>   ..@ .Data : num [1:3] 1 2 3
#>   ..@ year  : num [1:3] 0 0 0
#>   ..@ month : num [1:3] 0 0 0
#>   ..@ day   : num [1:3] 0 0 0
#>   ..@ hour  : num [1:3] 9 9 9
#>   ..@ minute: num [1:3] 10 10 10

y <- lubridate::mdy_hms(c("09:10:01", "09:10:02", "09:10:03"))
#> Warning: All formats failed to parse. No formats found.
str(y)
#>  POSIXct[1:3], format: NA NA NA

Maybe that is why x is rejected?

Maybe it is a oversight in the implementation since there are parts of the impl that strips away the class of the input before processing. Might be worth posting an issue on git?

y, the POSIX time is not a 1d vector but that case is specially excepted in the doc’s as long as it is not a POSXlt


#4

One easy alternative is to use the hms package rather than lubridate. It relies on the S3 system rather than S4.

tibble printing is still a bit too aggressive and you can’t see the seconds but they are there.

library(tidyverse)
library(hms)

x <- c("09:10:01", "09:10:02", "09:10:03")

tibble(time = as.hms(x))
#> # A tibble: 3 x 1
#>   time  
#>   <time>
#> 1 09:10 
#> 2 09:10 
#> 3 09:10

Created on 2018-01-18 by the reprex package (v0.1.1.9000).


#5

Thanks for the help all. The hms package will have to do for now. I also learned a little more about tibbles in the process!