tidyverse / hms

A simple class for storing time-of-day values
https://hms.tidyverse.org/
Other
138 stars 25 forks source link

Tools for decomposing a datetime into date + hms + timezone and back #113

Open krlmlr opened 1 year ago

krlmlr commented 1 year ago

Whenever I need to work with date-times and want to split the data into a date and its time component, I feel frustrated because the existing tools I'm familiar with (lubridate, clock) make this task quite difficult. Perhaps I'm missing something.

I think hms could offer lightweight tools for splitting and recombining, e.g., hms_separate() and hms_unite() . The result of hms_separate() would be a three-column tibble with components:

This implementation would be oblivious of leap seconds and such, but instead aim at being "good enough" for most practical cases.

The experiment below demonstrates a roundtrip for this decomposition for a timespan of over four years, with at least one timepoint in each hour.

ct <- structure(1675058104.91011, class = c("POSIXct", "POSIXt"), tzone = "") - seq(0, 14e7, by = 59)
# ct <- ct[2213:2217]
# ct <- head(ct, 100)

ct[[1]]
#> [1] "2023-01-30 06:55:04 CET"

t <- as.POSIXlt(ct)

# Get components that are sufficient for a roundtrip
tp <- hms::as_hms(t)
dp <- as.Date(t)
tzp <- unclass(t)[c("zone", "isdst", "gmtoff")]

# Helper
tp_decompose <- function(x) {
  x <- as.numeric(x)

  seconds <- x %% 60
  x <- x - seconds
  x <- as.integer(round(x / 60))

  minutes <- as.integer(round(x %% 60))
  x <- x - minutes
  x <- as.integer(round(x / 60))

  hours <- x

  list(hour = hours, min = minutes, sec = seconds)
}

# Implement roundtrip
tpd <- tp_decompose(tp)

ptt <- as.POSIXlt(dp)
attr(ptt, "tzone") <- attr(t, "tzone")

ptt <- unclass(ptt)
ptt[names(tpd)] <- tpd
ptt[names(tzp)] <- tzp
class(ptt) <- c("POSIXlt", "POSIXt")

# Check roundtrip
identical(t, ptt)
#> [1] TRUE

tt <- as.POSIXct(ptt)
identical(ct, tt)
#> [1] TRUE

Created on 2023-01-30 with reprex v2.0.2

krlmlr commented 1 year ago

The test fails with times before the Unix epoch due to https://bugs.r-project.org/show_bug.cgi?id=16856.