Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
krlmlr opened this issue Jan 30, 2023 · 1 comment
Open

Comments

@krlmlr
Copy link
Member

krlmlr commented Jan 30, 2023

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:

  • date: Date
  • time: hms
  • tz: the "zone", "isdst" and "gmtoff" components from POSIXlt

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
Copy link
Member Author

krlmlr commented Jan 30, 2023

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant