Giter Site home page Giter Site logo

hms's Introduction

hms

Lifecycle: stable rcc Codecov test coverage CRAN_Status_Badge

Overview

The hms package provides a simple class for storing durations or time-of-day values and displaying them in the hh:mm:ss format. This class is intended to simplify data exchange with databases, spreadsheets, and other data sources:

  • Stores values as a numeric vector that contains the number of seconds since midnight
  • Supports construction from explicit hour, minute, or second values
  • Supports coercion to and from various data types, including POSIXt
  • Can be used as column in a data frame
  • Based on the difftime class
  • Values can exceed the 24-hour boundary or be negative
  • By default, fractional seconds up to a microsecond are displayed, regardless of the value of the "digits.secs" option

Installation

# The easiest way to get hms is to install the whole tidyverse:
install.packages("tidyverse")

# Alternatively, install just hms:
install.packages("hms")

# Or the the development version from GitHub:
# install.packages("devtools")
devtools::install_github("tidyverse/hms")

Usage

The following example showcases ways of using the hms class standalone or as a data frame column.

library(hms)

hms(56, 34, 12)
#> 12:34:56
as_hms(Sys.time())
#> 11:55:02.553476
parse_hms("12:34:56")
#> 12:34:56
as.POSIXct(hms(1))
#> [1] "1970-01-01 00:00:01 UTC"

data.frame(hours = 1:3, hms = hms(hours = 1:3))
#>   hours      hms
#> 1     1 01:00:00
#> 2     2 02:00:00
#> 3     3 03:00:00

Internal representation

Objects of the hms and its underlying difftime classes are stored as number of seconds since 00:00:00. Use as.numeric() and as_hms() to convert to and from numbers.

times <- parse_hms(c("00:00:00.25", "00:00:01", "00:01:30", "01:00:00"))
times
#> 00:00:00.25
#> 00:00:01.00
#> 00:01:30.00
#> 01:00:00.00
times_num <- as.numeric(times)
times_num
#> [1]    0.25    1.00   90.00 3600.00
as_hms(times_num)
#> 00:00:00.25
#> 00:00:01.00
#> 00:01:30.00
#> 01:00:00.00

Please note that the ‘hms’ project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.

hms's People

Contributors

batpigandme avatar evanhaldane avatar hadley avatar hglanz avatar indrajeetpatil avatar jeroen avatar jimhester avatar joethorley avatar krlmlr avatar lionel- avatar qgeissmann avatar romainfrancois avatar vspinu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hms's Issues

as.hms parsing error

> as.hms("16:34:59")
16:34:59.000000 # Correct
> as.hms("16:35:00")
16:34:591 # Wrong
> as.hms("16:35:01")
16:35:01 # Correct

It seems the error will occur every 15 minutes, i.e.:
16:35
16:05
16:50
17:35
17:50
I am using hms_0.3

hms class dropped when combining

> c(hms(1:2), hms(3))
#>Time differences in secs
#> [1] 1 2 3

I expected combination of hms to return another hms object.
If it is a confirmed bug, I am quite happy to patch it

Is pillar printing too aggressive?

When you have hours, minutes, and seconds it seems like the seconds aren't (ever?) printed. Is this intended? Seems to me that when you have a regular sequence like below, you would want to know what the second is so you could uniquely identify that row.

(tibble 1.4.2, hms 0.4.1 dev)

library(tibble)
library(hms)

tibble(test = hms(seconds = 7200:8500))
#> # A tibble: 1,301 x 1
#>    test  
#>    <time>
#>  1 02:00 
#>  2 02:00 
#>  3 02:00 
#>  4 02:00 
#>  5 02:00 
#>  6 02:00 
#>  7 02:00 
#>  8 02:00 
#>  9 02:00 
#> 10 02:00 
#> # ... with 1,291 more rows

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

parser can be improved

  • "XX:YY" should implicitly mean, and be parsed as, "HH:MM". Now, we have as.hms("12:00") = NA
  • Negative durations such as -10:21:00 could be parsed as well as.hms("-12:00:01") = NA
  • Days should be parsed either with Dd, DdHH:MM, or DdHH:MM:SS (especially if we make a dhms class, see #12). Decimal number of days should only be allowed when no hour is provides (e.g. "0.5d12:00:11" does not make sense).
  • We should be able to parse durations greated that 24h. Now, we get as.hms("24:00:01") = NA. This is unexpected since we display hms(86401) as "24:00:01".
  • Check format more stingeantly. At the moment, as.hms("21:00:0012ABC") is "21:00:12". as.hms("21:1:00") is "21:01:12".
  • Give warnings or errors when format is not respected, or inconsistent. For now, we have NA without always knowing why. For instance as.hms("21:61:00") and as.hms("hello") both give NA, and no warning/error.

I attempted a prototypical parser that addresses these issues (https://github.com/qgeissmann/dhms). I am happy to work on it and merge it.

time column in spss is different from time column of the same file imported in R

I have a spss file with a time column called "tijdstartklin". When I view this file in spss, rows 11 and 14 (subject 15 and 19) have the times 0:23:48 and 0:29:35.
When I import the spss file with the command:

SpNm=haven::read_spss('example.sav') %>% mutate(parsed=hms::parse_hms(tijdstartklin))

The "tijdstartklin" fields of subjects 15 and 19 are now 00:23:471 and 00:29:341 and in the "parsed" column the times are 00:23:00 and 00:29:00.

Is this an error in the spss file or does haven import the column incorrectly?

example.zip

spss version 23

sessionInfo()
R version 3.5.0 (2018-04-23)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 7 x64 (build 7601) Service Pack 1

Matrix products: default

locale:
[1] LC_COLLATE=Dutch_Netherlands.1252 LC_CTYPE=Dutch_Netherlands.1252 LC_MONETARY=Dutch_Netherlands.1252
[4] LC_NUMERIC=C LC_TIME=Dutch_Netherlands.1252

attached base packages:
[1] stats graphics grDevices datasets utils methods base

other attached packages:
[1] haven_1.1.2 ggplot2_3.0.0 dplyr_0.7.6

loaded via a namespace (and not attached):
[1] Rcpp_0.12.18 rstudioapi_0.7 bindr_0.1.1 magrittr_1.5 hms_0.4.2 tidyselect_0.2.4 munsell_0.5.0
[8] colorspace_1.3-2 R6_2.2.2 rlang_0.2.1 plyr_1.8.4 tools_3.5.0 grid_3.5.0 gtable_0.2.0
[15] withr_2.1.2 yaml_2.2.0 lazyeval_0.2.1 assertthat_0.2.0 tibble_1.4.2 crayon_1.3.4 bindrcpp_0.2.2
[22] purrr_0.2.5 glue_1.3.0 compiler_3.5.0 pillar_1.3.0 forcats_0.3.0 scales_0.5.0 pkgconfig_2.0.1

Can't add to Date

#18 (comment)

as.Date("2016-03-31") + hms(minutes = 1)
#> Warning: Incompatible methods ("+.Date", "Ops.hms") for "+"
#> [1] "2016-05-30"

Edge-case for 0-length input

Sorry for the edge case, but I feel that as.hms.numeric should behave differently for length 0 inputs.

Currently it errors:

hms::as.hms(numeric(0)) #errors

However as.hms.character behaves the right way.

hms::as.hms(character(0)) #returns 0 length hms/difftime

This is biting me in code like this:

DBI::dbGetQuery(con, "select * from myTable where condition") %>% 
   mutate_(time = ~as.hms(time))

Because sometimes my condition yields a 0-row table sometimes.

Kind Regards

as.hms.POSIXct should respect time zone

Or there should be another function that allows you to extract the clock time from a POSIXct object (because it's often useful to separate a date time into a date and a time)

Implement colformat

Perhaps show seconds or sub-seconds with subtle style? Or the three significant digits?

is hms similar to between_time in Pandas?

Hello there!

I am wondering is one can use this package to slice a dataframe according to the time in the day.

I am looking for something similar to Pandas' between_time (http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.between_time.html)

If so, that would be very useful because there is no function to do cleanly that in R, except by converting the time into a string such as 05:00:02 and performing string-based comparisons (which can be error prone) like filter(time_str > "08:00:00")

Thanks!

as.hms fails with timezone?

Hello everyone,

Thanks again for this nice package. Consider this

library(hms)
library(lubridate)
library(tidyverse)

options(digits.secs=3)

dataframe <- data_frame(gmt_time = c('2016-07-08 04:30:10.690'), value = c(1))

dataframe <- dataframe %>% 
  mutate(gmt_time = ymd_hms(gmt_time),
         est_time = with_tz(gmt_time, 'America/New_York'),
         myhour1 = as.hms(est_time),
         myhour2 = format(est_time, format='%H:%M:%OS'),
         myhour3 = as.hms(myhour2))

> dataframe
# A tibble: 1 × 6
                gmt_time value               est_time         myhour1      myhour2  myhour3
                  <dttm> <dbl>                 <dttm>          <time>        <chr>   <time>
1 2016-07-08 04:30:10.69     1 2016-07-08 00:30:10.69 04:30:10.690000 00:30:10.690 00:30:10

Why does hms returns the wrong time?
It returns the GMT time instead of EST, although you can see that est_time is correctly converted.

Thanks for your help!

When passing lists to hms, if you pass hour, minutes, seconds, and days it returns a single value

When passing lists to just hour, minutes, and seconds (or any individually) it returns a vector of the same length:

> hms(1:10,1:10,1:10)
01:01:01
02:02:02
03:03:03
04:04:04
05:05:05
06:06:06
07:07:07
08:08:08
09:09:09
10:10:10

But if you also include a list of days, it will return one number:

> hms(1:10,1:10,1:10,1:10)
1375:55:55

In a dataframe it does behaves similarly, filling the column with all the same number:

> foo <- data.frame(sec  = 1:10,
                    min  = 1:10,
                    hour = 1:10,
                    day  = 1:10)

> foo %>% dplyr::mutate(hms(sec,min,hour,day))
    sec min hour day hms(sec, min, hour, day)
1    1   1    1   1               1375:55:55
2    2   2    2   2               1375:55:55
3    3   3    3   3               1375:55:55
4    4   4    4   4               1375:55:55
5    5   5    5   5               1375:55:55
6    6   6    6   6               1375:55:55
7    7   7    7   7               1375:55:55
8    8   8    8   8               1375:55:55
9    9   9    9   9               1375:55:55
10  10  10   10  10               1375:55:55

I would have expected to receive a vector of equal length when also passing days (or to get an error telling me not to do that, similarly to if you try to pass just seconds and days).

I'm not sure how often this use case would come up. I came across it while trying to make hms vectors to test with in another project so I am perhaps outside of your normal scope. Wanted to give you a heads up though in case it's helpful.

Thanks for the great package!

hms class dropped when doing arithmetic on hms objects

late_night <- hms::hms(seconds = 22 * 3600 + 20 * 60)
str(late_night + 5)
#> Class 'difftime'  atomic [1:1] 80405
#>   ..- attr(*, "units")= chr "secs"

I think you just need to define Ops.hms that calls Ops.difftime and preserves the hms class.

couldn't install hms

Hi,

when I ran devtools::install_github('krlmlr/hms'), I got the following readout:

Downloading GitHub repo krlmlr/hms@master from URL https://api.github.com/repos/krlmlr/hms/zipball/master Installing hms Downloading GitHub repo jimhester/covr@v2 from URL https://api.github.com/repos/jimhester/covr/zipball/v2 Error: Could not find build tools necessary to build covr

I went to his repo page, but I'm not really sure where to start to build covr. Any help would be greatly appreciated, thanks!

Negative times

Should be printed with a -.

Also decide about times >= 24h: print with a day prefix, or only hours.

Addition respects 24hour cycles

Would it be useful to have a modification that permitted hms objects to roll over at 24 hour boundaries? For example

22:00:00 + 03:00:00 = 01:00:00

At the moment it equals 25:00:00

I have a specific case use at the moment that would make this adjustment incredibly useful.

argument order in `hms()`

I would like to ask for the argument order in hms() to reflect that suggested by the function/pkg name: hms.

library(hms)
# hours, minutes, seconds
x <- "11:10:09"
hms::as.hms(x)

So then I'd expect the creator function named hms() to have the arguments, in order, hours, minutes, seconds.

#> 11:10:09
hms::hms(11, 10, 9) # nope.
#> 09:10:11
hms::hms(9, 10, 11) # seconds, minutes, hours?
#> 11:10:09

shouldn't it be smh() then?

If not some justification of the design choice would be fanstastic to help me remember next time I go to use it 😃

Need as.data.frame method

Something like this:

as.data.frame.hms <- function(x, row.names = NULL, optional = FALSE, ...) {
  df <- list(x)
  names(df) <- deparse(substitute(x))
  class(df) <- "data.frame"
  attr(df, "row.names") <- .set_row_names(length(x))
  x
}

as.data.frame(x = hms(100))

(but that doesn't work so I've forgotten something important)

Implicit coercion when operating with strings

I personally find it quite useful to be able to implicitly coerce strings to time objects.
I believe this is implemented in POSIXct. For instance: as.POSIXct("2016-01-01") == "2016-01-01" is TRUE.
In hms, we could be writing x > "23:50:21.100" instead of x > hms(21.100, 50,23) (or explicitly using as.hms()).
Now, we have:

> hms(1) > "00:00:02"
#> TRUE 

I am happy to implement this new feature if you want

Conflict between `lubridate::hms` and `hms::hms`

Depending on the order of package loading, hms may refer to either lubridate::hms or hms::hms, which have incompatible syntax and functionality. Since both are part of tidyverse, is this by design? I wasn't sure which package to post the issue in, so I've cross-posted at present (tidyverse/lubridate#673). It's certainly a cause for confusion.

library(lubridate)
#Attaching package: ‘lubridate’
#The following object is masked from ‘package:base’:
#
#    date
library(hms)
#Attaching package: ‘hms’
#
#The following object is masked from ‘package:lubridate’:
#
#    hms
hms("11:23:00")
#Error: All arguments must be numeric or NA
library(hms)
library(lubridate)
#Attaching package: ‘lubridate’
#
#The following object is masked from ‘package:hms’:

#    hms

#The following object is masked from ‘package:base’:

#    date
hms("11:23:00")
#An object of class "Period"
#[1] 0
#Slot "year":
#[1] 0
#
#Slot "month":
#[1] 0
#
#Slot "day":
#[1] 0
#
#Slot "hour":
#[1] 11
#
#Slot "minute":
#[1] 23

parse_hm("12:34") shouldn't equal "13:34:00"

ℝ> library(hms) 
ℝ> packageVersion("hms")
[1] ‘0.3.0.9003’
ℝ> parse_hm("12:34")
13:34:00
ℝ> as.difftime(as.character("1:00"), format = "%H:%M", units = "secs")
Time difference of 3600 secs
ℝ> as.difftime(as.character("2:00"), format = "%H:%M", units = "secs")
Time difference of 10800 secs

I encountered the problem when adding up hours with hms, but it is something in base R? This is with R 3.4.2 on Ubuntu 17.04.

`dhms` class should be implemented

When durations are larger than several days, the "HH:MM:SS" becomes quite hard to grasp.
Most people will not intuitively get that "162:00:00"= "6d18:00:00". As discussed in #12. it could be very useful to provide another class, dhms, that displays durations in days and HMS, as well.
I am happy to implement it

Invalid timestamp - overflow issue

When having nanosecond resolution, the generated hms value can be a non-valid timestamp:

> hms::as.hms(35968.999)
09:59:28.999
> hms::as.hms(35968.9999)
09:59:28.9999
> hms::as.hms(35968.99999)
09:59:28.99999
> hms::as.hms(35968.999999)
09:59:28.999999
> hms::as.hms(35968.9999999)
09:59:281.000000

Add vctrs support

Something like this:

library(vctrs)

vec_cast.hms <- function(x, to) UseMethod("vec_cast.hms")
vec_cast.hms.default <- function(x, to) stop_incompatible_cast(x, to)
vec_cast.hms.logical <- function(x, to) vec_unspecified_cast(x, to)
vec_cast.hms.hms <- function(x, to) x

vec_cast.hms.difftime <- function(x, to) hms::as.hms(x)
vec_cast.difftime.hms <- function(x, to) as.difftime(x)

vec_type2.hms <- function(x, y) UseMethod("vec_type2.hms")
vec_type2.hms.default           <- function(x, y) stop_incompatible_type(x, y)
vec_type2.hms.vctrs_unspecified <- function(x, y) x
vec_type2.hms.vctrs_percent     <- function(x, y) x
vec_type2.hms.hms               <- function(x, y) hms::hms()

vec_type2.difftime.hms          <- function(x, y) new_difftime(units = units(x))
vec_type2.hms.difftime          <- function(x, y) new_difftime(units = units(y))


library(hms)
time <- hms(5)
vec_c(time, time + 0:10 * 900)
#> 00:00:05
#> 00:00:05
#> 00:15:05
#> 00:30:05
#> 00:45:05
#> 01:00:05
#> 01:15:05
#> 01:30:05
#> 01:45:05
#> 02:00:05
#> 02:15:05
#> 02:30:05

Created on 2018-10-30 by the reprex package (v0.2.1)

space between minus sign and first number

Not sure this is a feature or a bug, but when we print a vector of negative durations, minus signs are left justified -- whilst numbers are right justified.
Personally, I find it harder to read if - is not immediatly in front of a number:

- hms(hours = c(1, 1000))
#> -  01:00:00
#> -1000:00:00

can hms handle milliseconds?

I am using hms to parse the time (hour, minutes, seconds, milliseconds) from timestamps in my data.
Unfortunately, I notice that hms seems to forget about the millisecond part of a time (in character format).

> as.hms("12:34:56")
12:34:56
> as.hms("12:34:56.542")
12:34:56
> options(digits.secs=3)
> as.hms("12:34:56.542")
12:34:56

Is that a bug? Having milliseconds is important because I sort the data according to time...

Adding to Date objects gives strange results

> Sys.Date()
[1] "2016-03-31"
> Sys.Date() + hms::hms(1)
[1] "2016-04-01"
  • Double-check addition and subtraction from other types (POSIX times, lubridate, ...)
  • Consider inheriting from difftime.

License?

Currently GPL-3, does that allow haven et al. using this package?

hms needs better error

> hms::hms("05:00")
Error in .Primitive("*")(dots[[1L]][[1L]], dots[[2L]][[1L]]) : 
  non-numeric argument to binary operator

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.