what is the best practice of handling time in R? - r

I am working with a survey dataset. It has two string vectors, start and finish, indicating the time of the day when the interview was started, and finished, respectively.
They are character strings that look like: "9:24 am", "12:35 pm", and so forth. I am trying to calculate the duration of the interview based on these two. What is the best way of doing this?
I know that, for dates, there are lots of classes or functions like as.date(), as.Date(), chron(), or as.POSIXct(). So I was looking for something like as.time(), but could not find it. Should I just append a made-up date and convert the whole thing into a POSIX() date-time class, then use difftime()?
What is the best practice of handling time in R?

You need to use strptime() to convert the string to a date. For example:
strptime("9:24 am",format="%I:%M %p")
Then you can take differences just by taking one away from the other:
strptime("9:24 am",format="%I:%M %p")-strptime("12:14 am",format="%I:%M %p")
Time difference of 9.166667 hours
You can store this and then do an as.numeric() if you just want the number out, otherwise you can pass around the time objects.
Hope this helps!

one option is to use regular expressions. if you are not familiar with them, they are used to parse strings using patterns. i would research regular expressions and then here are the functions in r
hope it helps

best practice is using lubridate package
https://www.rdocumentation.org/packages/lubridate/versions/1.5.6/topics/hm
hm(c("09:10", "09:02", "1:10"))
## [1] "9H 10M 0S" "9H 2M 0S" "1H 10M 0S
Then use difftime for difference in the date time formats created above
https://stat.ethz.ch/R-manual/R-devel/library/base/html/difftime.html
difftime(time1, time2, tz,
units = c("auto", "secs", "mins", "hours",
"days", "weeks"))

Related

adding hours and minutes to 24 hour time in R

Im trying to find a way to create a column of the hours of arrival of different individuals from 18:30, by adding the hours and minutes to 18:30, but it doesnt work, and each time I try to convert the column format, of the hours and minutes I want to add, from "factor" to a time format (such as chron or POSIXct)it adds today`s date and I want to add only the hours and minutes without considering date.
For example, I want to add 03:38 hours to 18:30, which will be 22:08. So, I want to find a way to have a column with all the final hours (hours of arrival of each individual). another example is adding 00:47 hours to 18:30 which is 19:17.
I tried to do it by converting the hours+minutes to a numeric format and than add it to 18.5 (which is 18:30, just in a numeric format), and I reached final hours, but they are in a numeric format, and I wish them to be in an hour format. For example:
I added 3.6425980 (which is 03:38) to 18.5 (which is 18:30) and the final hour was 22.14260 (which is 22:08), but I don`t know how to convert 22.14260 to 22:08.
I need to do it for a lot of data, so I need a code that does one of the above things.
Can someone help me?
thanks!
One option is to use lubridate
time <- c("18:30", "18:30")
duration <- c("3:38", "00:47")
library(lubridate)
hms(hm(time) + hm(duration), roll = T)
#[1] "22H 8M 0S" "19H 17M 0S"

Recording timer data - how to measure milliseconds without the date?

I have a list of times in a basketball game. Here are some sample time values to show how they're stored: "11:44.0", "10:03.0", "8:35.0", "6:19.0", "0:49.9", "0:03.9"
(Minutes - no leading 0):(Seconds - with leading 0).(Tenths of a second)
It responds well to the format %M:%OS, but I end up with a whole date to go with what was just timer data. This is inconvenient mostly because it looks bad (if any graphs or tables autogenerate to include the date), and if I'm working on it over multiple days, I'll have to make sure all my values have the same date in order to subtract them properly from each other.
> strptime("0:49.9", format = "%M:%OS")
[1] "2018-08-13 00:00:49.9 EDT"
I want to keep it in a time format so it's easy to manipulate. It seems that methods of truncating (like strptime's format) turn it into a string, and I don't think the times class from chron handles milliseconds (or tenths of a second, for my case).
We can use the ms function from lubridate which would make it easy for manipulation later.
library(lubridate)
ms(x)
#[1] "11M 44S" "10M 3S" "8M 35S" "6M 19S" "49.9S" "3.9S"
So let's say later you want to add 2 minutes to all of them, you could do
ms(x) + minutes(2)
#[1] "13M 44S" "12M 3S" "10M 35S" "8M 19S" "2M 49.9S" "2M 3.9S"
data
x <- c("11:44.0", "10:03.0", "8:35.0", "6:19.0", "0:49.9", "0:03.9")
After reading different threads about this issue, I've come to the conclusion that there's no good way to represent date-time without the date. To a certain extent, all date-time formats keep track of date, at least on a basal level even if it's not displayed. Any methods (that I could find) that truly truncate it end up converting the value back to a string.
I'll keep it in its current character format and call strptime on it whenever I need it. It will look something like:
graph <- ggplot(data, strptime(times, format = "%M:%OS"), scores) + geomline()
ggplot is good enough to automatically recognize they're all the same date, so it only displays the time. It might work to convert it to numeric, but then I'd need to measure it terms of tenths of a second (so 12:00.00 -> 7200 tenths of a second), and perform other transformations to display it in a palatable time format (easier to read "7:43" than "2570 tenths of a second into a quarter")

How to convert times over 24:00:00 in R

In R I have this data.frame
24:43:30 23:16:02 14:05:44 11:44:30 ...
Note that some of the times are over 24:00:00 ! In fact all my times are within 02:00:00 to 25:59:59.
I want to subtract all entries in my dataset data with 2 hours. This way I get a regular data-set. How can I do this?
I tried this
strptime(data, format="%H:%M:%S") - 2*60*60
and this work for all entries below 23:59:59. For all entries above I simply get NA since the strptime command produce NA to all entries above 23:59:59.
Using lubridate package can make the job easier!
> library(lubridate)
> t <- '24:43:30'
> hms(t) - hms('2:0:0')
[1] "22H 43M 30S"
Update:
Converting the date back to text!
> substr(strptime(hms(t) - hms('2:0:0'),format='%HH %MM %SS'),12,20)
[1] "22:43:30"
Adding #RHertel's update:
format(strptime(hms(t) - hms('2:0:0'),format='%HH %MM %SS'),format='%H:%M:%S')
Better way of formating the lubridate object:
s <- hms('02:23:58) - hms('2:0:0')
paste(hour(s),minute(s),second(s),sep=":")
"0:23:58"
Although the answer by #amrrs solves the main problem, the formatting could remain an issue because hms() does not provide a uniform output. This is best shown with an example:
library(lubridate)
hms("01:23:45")
#[1] "1H 23M 45S"
hms("00:23:45")
#[1] "23M 45S"
hms("00:00:45")
#[1] "45S"
Depending on the time passed to hms() the output may or may not contain an entry for the hours and for the minutes. Moreover leading zeros are omitted in single-digit values of hours, minutes and seconds. This can result pretty much in a formatting nightmare if one tries to put that data into a common form.
To resolve this difficulty one could first convert the time into a duration with lubridate's as.duration() function. Then, the duration in seconds can be transformed into a POSIXct object from which the hours, minutes, and seconds can be extracted easily with format():
times <- c("24:43:30", "23:16:02", "14:05:44", "11:44:30", "02:00:12")
shifted_times <- hms(times) - hms("02:00:00")
format(.POSIXct(as.duration(shifted_times),tz="GMT"), "%H:%M:%S")
#[1] "22:43:30" "21:16:02" "12:05:44" "09:44:30" "00:00:12"
The last entry "02:00:12" would have caused difficulties if shifted_times had been passed to strptime().

POSIXlt 12-hour time with-out leading zeros

I am attempting to eliminate the leading zero of a 12-hour time value, but for graphing purposes the result must be a POSIXlt value. Therefore, I can-not use regular expressions because they would leave the result as a character instead of a POSIXlt value.
My time value begins as a character.
a <- "02:57"
Then I use strptime to convert the character to the POSIXlt class. Within strptime, I use the conversion specification %l, which according to the strptime help, displays "12-hour clock time with single digits preceded by a blank".
b <- strptime(x = a, tz = "UTC", format = "%l")
The variable b is a POSIXlt value, and consists of "current date" + "02:57:00" + "local time zone". I can live with the date and time zone, but the leading zero of the 12-hour time value remains.
How can I eliminate the leading zero of the 12-hour time value and still retain POSIXlt class?
I appreciate any insight.
I would use lubridate, an excellent package for working with POSIXlt time objects.
library(lubridate)
a <- "02:57"
b <- hm(a)
yields
> b
[1] "2H 57M 0S"
no pesky 0 and sooo much other time goodness to boot. Good luck.

How to add/subtract time from a POSIXlt time while keeping its class in R?

I am manipulating some POSIXlt DateTime objects. For example I would like to add an hour:
my.lt = as.POSIXlt("2010-01-09 22:00:00")
new.lt = my.lt + 3600
new.lt
# [1] "2010-01-09 23:00:00 EST"
class(new.lt)
# [1] "POSIXct" "POSIXt"
The thing is I want new.lt to be a POSIXlt object. I know I could use as.POSIXlt to convert it back to POSIXlt, but is there a more elegant and efficient way to achieve this?
POSIXct-classed objects are internally a numeric value that allows numeric calculations. POSIXlt-objects are internally lists. Unfortunately for your desires, Ops.POSIXt (which is what is called when you use "+") coerces to POSIXct with this code:
if (inherits(e1, "POSIXlt") || is.character(e1))
e1 <- as.POSIXct(e1)
Fortunately, if you just want to and an hour there is a handy alternative to adding 3600. Instead use the list structure and add 1 to the hour element:
> my.lt$hour <- my.lt$hour +1
> my.lt
[1] "2010-01-09 23:00:00"
This approach is very handy when you want to avoid thorny questions about DST changes, at least if you want adding days to give you the same time-of-day.
Edit (adding #sunt's code demonstrating that Ops.POSIXlt is careful with time "overflow".))
my.lt = as.POSIXlt("2010-01-09 23:05:00")
my.lt$hour=my.lt$hour+1
my.lt
# [1] "2010-01-10 00:05:00"
Short answer: No
Long answer:
POSIXct and POSIXlt objects are two specific types of the more general POSIXt class (not in a strictly object oriented inheritance sense, but in a quasi-object oriented implementation sense). Code freely switches between these. When you add to a POSIXlt object, the actual function used is +.POSIXt, not one specifically for POSIXlt. Inside this function, the argument is converted into a POSIXct and then dealt with (added to).
Additionally, POSIXct is the number of seconds from a specific date and time. POSIXlt is a list of date parts (seconds, minutes, hours, day of month, month, year, day of week, day of year, DST info) so adding to that directly doesn't make any sense. Converting it to a number of seconds (POSIXct) and adding to that does make sense.
It may not be significantly more elegant, but
seq.POSIXt( from=Sys.time(), by="1 hour", length.out=2 )[2]
IMHO is more descriptive than
Sys.time()+3600; # 60 minutes * 60 seconds
because the code itself documents that you're going for a "POSIX" "seq"uence incremented "by 1 hour", but it's a matter of taste. Works just fine on POSIXlt, but note that it returns a POSIXct either way. Also works for "days". See help(seq.POSIXt) for details on how it handles months, daylight savings, etc.
?POSIXlt tells you that:
Any conversion that needs to go between the two date-time classes requires a timezone: conversion from "POSIXlt" to "POSIXct" will validate times in the selected timezone.
So I guess that 3600 not being a POSIXlt object, there is an automatic conversion.
I would stick with simple:
new.lt = as.POSIXlt(my.lt + 3600)
class(new.lt)
[1] "POSIXlt" "POSIXt"
It's not that much of a hassle to add as.POSIXlt before your time operation.

Resources