Seconds since midnight to time of day - r

There is a post that shows how use lubridate to go from time to seconds since midnight. How about going from seconds since midnight to time of day?
So, instead of using 06:52:32 to get 24752.05, how do I use 24752.05 to get 06:52:32?

Use as.POSIXct() on the sum of the midnight timestamp and your offset, possibly adjusting by setting TZ.
In my default time zone:
R> as.POSIXct(trunc(Sys.time(), units="days") + 24752.05)
[1] "2013-01-16 06:52:32.04 CST"
R>
Note that the answer (just like the preceding question) can be solved just fine in base R, persistent rumors that every time question has to involve lubridate not withstanding.

Another option:
format(as.POSIXct('1900-1-1') + 24752.05, '%H:%M:%S')
# [1] "6:52:32"

Using lubridate just for variety:
library(lubridate)
Sys.time() + seconds(24752.05)
or from midnight:
as.Date(Sys.time()) + seconds(24752.05)

And of course, you can compute the hours, minutes and seconds directly. This is based on the definition of POSIX time. Midnight always has a time_t value that is divisible by 86400.
x <- 24752.05
paste(sprintf("%02d", floor(x / 3600)),
sprintf("%02d", floor(x %% 3600 / 60)),
sprintf("%02d", floor(x %% 60 )),
sep=":"
)
## [1] "06:52:32"

Related

Converting milliseconds to hh:mm format by considering the rounding in r

I am trying to convert milliseconds to hh:mm format. I tried this below:
x<-c(3159763, 2839300, 3821900)
t.adj <- 0
final <- strftime(as.POSIXlt.numeric(x/1000, format="%OS", origin="1970-01-01") - t.adj*3600,
format="%R", tz="GMT")
> final
[1] "00:52" "00:47" "01:03"
The issue here is the first element actual value is 52 mins 38sec so since it passes halfway, it should be 53 mins as 00:53. The second element is 47min 19sec so it can stay as 00:47 and the last one is 1 H 3 Min 41 s so it should be printed 01:04.
Does anyone have any idea to fix it in this function or possibly have another solution to this rounding issue?
Thanks!
You can use round_date from the lubridate package to round to the nearest minute after converting milliseconds to a POSIXct object and before formatting it with strftime
library(lubridate)
x <- c(3159763, 2839300, 3821900)
y <- as.POSIXct.numeric(x/1000, origin = '1970-01-01')
z <- lubridate::round_date(y, unit = 'minute')
strftime(z, format = '%R', tz='GMT')
[1] "00:53" "00:47" "01:04"
with(as.POSIXlt(x/1000, 'GMT', Sys.Date()),sprintf("%02d:%02d", hour,min+sec%/%30))
[1] "00:53" "00:47" "01:04"
EDIT:
You could do:
sprintf("%02d:%02d",(y<-round(x / 60000)) %/% 60, y %% 60)

How to format a difftime object to a string with HH:MM:SS

I think this is quite a simple question, I am however incapable of solving it, so any help would be greatly appreciated.
I have a difftime object, generated as follows:
> duration <- difftime(end_time, start_time)
> duration
Time difference of 15.74106 secs
The end_time and start_time objects are POSIXct objects and look like this:
> c(start_time, end_time)
[1] "2018-07-08 20:07:56 EDT" "2018-07-08 20:08:12 EDT"
I need duration to be displayed in HH:MM:SS format - i.e. like this, in a string:
"00:00:15"
Is there a simple answer to this question? I've played around with the format argument, but it's not working.
Thanks in advance for your help,
Nik
One possible solution is:
library(hms)
duration <- difftime("2018-07-08 20:08:12 EDT", "2018-07-08 20:07:56 EDT")
as.hms(duration)
# 00:00:16
A general solution, using base R, inspired by G. Grothendieck's answer to the question:
Outputting difftime as HH:MM:SS:mm in R
duration <- difftime(end_time, start_time, units="secs")
x <- abs(as.numeric(duration))
sprintf("%02d:%02d:%02d:%02d", x %/% 86400, x %% 86400 %/% 3600, x %% 3600 %/%
60, x %% 60 %/% 1)
This link maybe helpful. It explains little or more of what you want.

Compute the time since the beginning of the week?

I have a bunch of datetime data and I'd like to see if there is any weekly pattern in it. So I'd like to compute the elapsed time (in seconds) since the beginning of the week with R.
How can I do that? I did the same thing for a daily pattern using difftime(time,as.Date(time)) but I can't use the same trick for the week as there is no as.Week().
You can do it in base R, and you already gave yourself the answer: difftime() with a proper offset.
Even midnight is good enough as you simply need to add dayOfTheWeek * 24 * 60 * 60 to is, and dayOfTheWeek is a field in POSIXlt.
If you want higher-end helper packages, my RcppBDT has a few functions from Boost Date_Time too.
Illustration:
R> now <- Sys.time()
R> midnight <- trunc(now, "days") # elegant way to get midnight; thanks #flodel
R> today <- as.POSIXlt(Sys.Date())
R> today$wday # it is Sunday
[1] 0
R>
R> difftime(now, midnight, unit="secs")
Time difference of 56630.6 secs
R>
So you could add today$wday * 24 * 60 * 60
R> as.numeric(difftime(now, midnight, unit="secs")) + today$wday*24*60*60
[1] 56630.6
R>
Here's my solution as well:
secs.in.week <- function(t) {
d <- as.integer(format(t, "%w"))
d <- ifelse(d, d-1, 6)
weekstart <- as.POSIXct(as.Date(t)-d) - 2 * 3600 # Convert UTC -> Local time. I'm on UTC+2h
as.numeric(difftime(t,weekstart), units="secs")
}

R test for morning rush hour - time vector in interval

I'm trying to create a clean function in R to return TRUE/FALSE if a vector of POSIXlt times are in morning rush hour, i.e. 7.30am to 9.30am Monday to Friday. This is what I've done so far, but it seems a bit long and convoluted. Is it possible to improve on this while keeping the code itself readable?
library(lubridate)
morning.rush.hour <- function(tm) {
# between 7.30am and 9.30am Monday to Friday
# vectorised...
# HARDCODED times here!
tm.mrh.start <- update(tm, hour=7, minute=30, second=0)
tm.mrh.end <- update(tm, hour=9, minute=30, second=0)
mrh <- new_interval(tm.mrh.start, tm.mrh.end)
# HARDCODED weekdays here!
((tm$wday %in% 1:5) & # a weekday?
(tm %within% mrh))
}
# for test purposes...
# nb I'm forcing UTC to avoid the error message "Error in as.POSIXlt.POSIXct(x, tz) : invalid 'tz' value"
# - bonus points for solving this too :-)
tm <- with_tz(as.POSIXlt(as.POSIXlt('2012-07-15 00:00:01', tz='UTC') + (0:135)*3000), 'UTC')
data.frame(tm, day=wday(tm, label=TRUE, abbr=FALSE), morning.rush.hour(tm))
Even better if there is a clean function definition for weekday time ranges like this, as I also have evening rush hour, and daytime which is not rush hour and finally not any of these!
I would do something way simpler than that using difftime and cut. You can do the following (using base functions):
morning.rush.hour<-function(tm){
difftime(tm, cut(tm, breaks="days"), units="hours") -> dt #This is to transform the time of day into a numeric (7:30 and 9:30 being respectively 7.5 and 9.5)
(tm$wday %in% 1:5) & (dt <= 9.5) & (dt >= 7.5) #So: Is it a weekday, it is before 9:30 and is it after 7:30?
}
Edit: You can also add a time-zone parameter to difftime if needed:
difftime(tm, cut(tm, breaks="days"), units="hours", tz="UTC")

Length of lubridate interval

What's the best way to get the length of time represented by an interval in lubridate, in specified units? All I can figure out is something like the following messy thing:
> ival
[1] 2011-01-01 03:00:46 -- 2011-10-21 18:33:44
> difftime(attr(ival, "start") + as.numeric(ival), attr(ival, "start"), 'days')
Time difference of 293.6479 days
(I also added this as a feature request at https://github.com/hadley/lubridate/issues/105, under the assumption that there's no better way available - but maybe someone here knows of one.)
Update - apparently the difftime function doesn't handle this either. Here's an example.
> (d1 <- as.POSIXct("2011-03-12 12:00:00", 'America/Chicago'))
[1] "2011-03-12 12:00:00 CST"
> (d2 <- d1 + days(1)) # Gives desired result
[1] "2011-03-13 12:00:00 CDT"
> (i2 <- d2 - d1)
[1] 2011-03-12 12:00:00 -- 2011-03-13 12:00:00
> difftime(attr(i2, "start") + as.numeric(i2), attr(i2, "start"), 'days')
Time difference of 23 hours
As I mention below, I think one nice way to handle this would be to implement a /.interval function that doesn't first cast its input to a period.
The as.duration function is what lubridate provides. The interval class is represented internally as the number of seconds from the start, so if you wanted the number of hours you could simply divide as.numeric(ival) by 3600, or by (3600*24) for days.
If you want worked examples of functions applied to your object, you should provide the output of dput(ival). I did my testing on the objects created on the help(duration) page which is where ?interval sent me.
date <- as.POSIXct("2009-03-08 01:59:59") # DST boundary
date2 <- as.POSIXct("2000-02-29 12:00:00")
span <- date2 - date #creates interval
span
#[1] 2000-02-29 12:00:00 -- 2009-03-08 01:59:59
str(span)
#Classes 'interval', 'numeric' atomic [1:1] 2.85e+08
# ..- attr(*, "start")= POSIXct[1:1], format: "2000-02-29 12:00:00"
as.duration(span)
#[1] 284651999s (9.02y)
as.numeric(span)/(3600*24)
#[1] 3294.583
# A check against the messy method:
difftime(attr(span, "start") + as.numeric(span), attr(span, "start"), 'days')
# Time difference of 3294.583 days
This question is really old, but I'm adding an update because this question has been viewed many times and when I needed to do something like this today, I found this page. In lubridate you can now do the following:
d1 <- ymd_hms("2011-03-12 12:00:00", tz = 'America/Chicago')
d2 <- ymd_hms("2011-03-13 12:00:00", tz = 'America/Chicago')
(d1 %--% d2)/dminutes(1)
(d1 %--% d2)/dhours(1)
(d1 %--% d2)/ddays(1)
(d1 %--% d2)/dweeks(1)
Ken, Dividing by days(1) will give you what you want. Lubridate doesn't coerce periods to durations when you divide intervals by periods. (Although the algorithm for finding the exact number of whole periods in the interval does begin with an estimate that uses the interval divided by the analagous number of durations, which might be what you are noticing).
The end result is the number of whole periods that fit in the interval. The warning message alerts the user that it is an estimate because there will be some fraction of a period that is dropped from the answer. Its not sensible to do math with a fraction of a period since we can't modify a clock time with it unless we convert it to multiples of a shorter period - but there won't be a consistent way to make the conversion. For example, the day you mention would be equal to 23 hours, but other days would be equal to 24 hours. You are thinking the right way - periods are an attempt to respect the variations caused by DST, leap years, etc. but they only do this as whole units.
I can't reproduce the error in subtraction that you mention above. It seems to work for me.
three <- force_tz(ymd_hms("2011-03-12 12:00:00"), "")
# note: here in TX, "" *is* CST
(four <- three + days(1))
> [1] "2011-03-13 12:00:00 CDT"
four - days(1)
> [1] "2011-03-12 12:00:00 CST"
Be careful when divinding time in seconds to obtain days as then you are no longer working with abstract representations of time but in bare numbers, which can lead to the following:
> date_f <- now()
> date_i <- now() - days(23)
> as.duration(date_f - date_i)/ddays(1)
[1] 22.95833
> interval(date_i,date_f)/ddays(1)
[1] 22.95833
> int_length(interval(date_i,date_f))/as.numeric(ddays(1))
[1] 22.95833
Which leads to consider that days or months are events in a calendar, not time amounts that can be measured in seconds, miliseconds, etc.
The best way to calculate differences in days is avoiding the transformation into seconds and work with days as a unit:
> e <- now()
> s <- now() - days(23)
> as.numeric(as.Date(s))
[1] 18709
> as.numeric(as.Date(e) - as.Date(s))
[1] 23
However, if you are considering a day as a pure 86400 seconds time span, as ddays() does, the previous approach can lead to the following:
> e <- ymd_hms("2021-03-13 00:00:10", tz = 'UTC')
> s <- ymd_hms("2021-03-12 23:59:50", tz = 'UTC')
> as.duration(e - s)
[1] "20s"
> as.duration(e - s)/ddays(1)
[1] 0.0002314815
> as.numeric(as.Date(e) - as.Date(s))
[1] 1
Hence, it depends on what you are looking for: time difference or calendar difference.

Resources