Converting Lubridate Period Class Time to Time Stamp Format in R - r

My code reads in a .txt file that holds a series of time stamps in one column. I needed to account for daylight savings for the column, so I used the lubridate package to subtract an hour from these time stamps. I'm struggling with converting the period class from lubridate back into a time format of %I:%M%:S %p.
Here is my code.
# Changing from 24 Hr to 12 Hr Format #
raw_data_sample$Time <- format(strptime(raw_data_sample$Time, format='%H:%M:%S'), '%I:%M:%S %p')
# Subtracting an Hour for Daylight Savings
raw_data_sample$Time <- hms(raw_data_sample$Time)
raw_data_sample$Time <- raw_data_sample$Time - hours(1)
Here is my current output.
c("1H 41M 54S", "1H 42M 4S", "1H 42M 14S", "1H 42M 31S", "1H 42M 41S", "1H 43M 1S")
I'm hoping to get an output like
1:41:54 PM, 1:42:40 PM
Any advice? Thank you!

You can use parse_date_time function to convert your period object to POSIXct then use format to get in your suitable format.
library(lubridate)
raw_data_sample$Time1 <- format(parse_date_time(raw_data_sample$Time, 'HMS'), '%I:%M:%S %p')
For example,
x <- period(c("1H 41M 54S", "1H 42M 4S", "1H 42M 14S", "1H 42M 31S", "1H 42M 41S", "1H 43M 1S"))
format(parse_date_time(x, 'HMS'), '%I:%M:%S %p')
#[1] "01:41:54 AM" "01:42:04 AM" "01:42:14 AM" "01:42:31 AM" "01:42:41 AM" "01:43:01 AM"

If we need to subtract an hour, do this on the original datetime object, and then do the formatting
library(lubridate)
# // convert to Datetime class
raw_data_sample$Time <- as.POSIXct(raw_data_sample$Time, format = "%H:%M:%S")
# // subtract 1 hour from the Datetime and use format to change the format
format(raw_data_sample$Time %m-% hours(1), "%I:%M:%S %p")

Related

How to change format from seconds to hh:mm:ss?

I need to transform a seconds to hh:mm:ss format in R.
I use the difftime() function to have the difference between two dates and I specify units="mins".
But for the final result, I want it as hh:mm:ss and not only by specifying hour, or minutes, o seconds.
Try using lubridate.
library(lubridate)
seconds_to_period(86400)
#[1] "1d 0H 0M 0S"
seconds_to_period(48000)
#[1] "13H 20M 0S"
Then format the date:
td <- seconds_to_period(86400)
sprintf('%02d %02d:%02d:%02d', day(td), td#hour, minute(td), second(td))
#[1] "01 00:00:00"
Try using hms package:
library(hms)
hms::as_hms(99)
#[1] 00:01:39

Convert and round column of string times in h:m format to time

I have a column of "times" in string format in hour and minute (no seconds)
time ...
<char>
18:40
12:20
23:59
2:15
...
Is there a way to convert these into times and then round them down such that my data will look like this
time ...
<time>
18:00
12:00
23:00
2:00
...
POSIXct class needs both date and time, so if date is not provided it by default takes today's date. You can then use floor_date to round it down at the nearest hour.
library(lubridate)
floor_date(as.POSIXct(df$time, 'UTC', format = '%H:%M'), 'hour')
#[1] "2020-07-06 18:00:00 UTC" "2020-07-06 12:00:00 UTC" "2020-07-06 23:00:00 UTC"
#[4] "2020-07-06 02:00:00 UTC"
You can then use format to keep part that you are interested in.
format(floor_date(as.POSIXct(df$time, 'UTC', format = '%H:%M'), 'hour'), '%H:%M')
#[1] "18:00" "12:00" "23:00" "02:00"
A solution without date-time manipulation using regex :
sub(':.*', ':00', df$time)
#[1] "18:00" "12:00" "23:00" "2:00"
However, note that manipulating date and times using regex is probably not the best option.
data
df <- structure(list(time = c("18:40", "12:20", "23:59", "2:15")),
class = "data.frame", row.names = c(NA, -4L))
Maybe Period class in lubridate is what you need:
library(lubridate)
Parse periods with hour and minute
hm(df$time)
# [1] "18H 40M 0S" "12H 20M 0S" "23H 59M 0S" "2H 15M 0S"
Extract hours component
hour(hm(df$time))
# [1] 18 12 23 2
Create a new period object
hours(hour(hm(df$time)))
# [1] "18H 0M 0S" "12H 0M 0S" "23H 0M 0S" "2H 0M 0S"

Parsing 12-hour times using lubridate

I'm trying to get my head around parsing 12-hour times using lubridate. If I run
library(lubridate)
times <- c("1:30 AM", "6:29 AM", "6:59 AM", "9:54 AM", "2:45 PM")
hm(times)
I get
[1] "1H 30M 0S" "6H 29M 0S" "6H 59M 0S" "9H 54M 0S" "2H 45M 0S"
Note that the AM/PM designation is not used. However, if if the time strings also includes a date then the parsing works
ymd_hm(paste("01-01-01", times))
[1] "2001-01-01 01:30:00 UTC" "2001-01-01 06:29:00 UTC"
[3] "2001-01-01 06:59:00 UTC" "2001-01-01 09:54:00 UTC"
[5] "2001-01-01 14:45:00 UTC"
It seems to me that the time parsing functions: hm, hms, ... doesn't recognize the AM/PM, but the date functions do. Is it possible to allow for 12-hour parsing without going through the dates?
[I know I can do this by parsing the strings but I was wondering it it was possible within lubidate]
The two objects belong to different classes each one designed for a specific purpose.
With the first function you create a period class object. This kind of class if designed to represent times, like time of a race, or "how many hours Bolt runs 100 meters?" 0 hours 0 minutes 9 seconds 58 and so on.
See:
a <- hm(times)
class(a)
[1] "Period"
attr(,"package")
[1] "lubridate"
The second object with the function ymd_hm creates an object of class:
b <- ymd_hm(paste("01-01-01", times))
class(b)
[1] "POSIXct" "POSIXt"
This class of object is designed to represent "time", in the sense of Gregorian calendar (or maybe other kind of calendars). It does parse also AM/PM that are vital to differentiate hours of the day in a 12 hours clock.

Storing time without date but not as class character

I have a local.time column in my data frame of class character containing elements like this :
> a$local.time
[1] "1:30 AM" "6:29 AM" "6:59 AM" "9:54 AM" "10:14 AM" "10:34 AM" "12:54 PM" "1:15 PM" "1:20 PM"
[10] "1:20 PM" "2:15 PM" "2:15 PM" "4:23 AM" "6:28 AM" "2:45 PM" "3:08 PM" "3:23 PM" "3:58 PM"
I wanted to convert them from class character to time variables. So I used:
> as.POSIXct(a$local.time, tz = "", format = "%I:%M %p", usetz = FALSE)
This resulted in :
[1] "2014-10-31 01:30:00 EDT" "2014-10-31 06:29:00 EDT" "2014-10-31 06:59:00 EDT" "2014-10-31 09:54:00 EDT"
[5] "2014-10-31 10:14:00 EDT" "2014-10-31 10:34:00 EDT" "2014-10-31 12:54:00 EDT" "2014-10-31 13:15:00 EDT"
I have a date variable in a different column and the intention is to provide the capability of filtering by date and zooming on time bands to the minute in a dynamic dashboard.
I want to remove the date and time zone from a$local.time but keep it in a time format so the chronology is maintained i.e. 18:57 is the 19th hour and 57th minute of the day etc.
If I use
a$local.time <- format(a$local.time, "%Y-%m-%d %H:%M:%S", usetz = FALSE)
a$local.time <- strftime(a$local.time, format = "%H:%m") ,
the class changes to character ! What's the right approach?
The chron package has a "times" class that might be helpful for you. Starting with something similar to what you have so far:
x <- c("1:30 AM", "6:29 AM", "6:59 AM", "9:54 AM", "10:14 AM", "3:15 PM"))
a <- as.POSIXct(x, tz = "", format = "%I:%M %p", usetz = FALSE)
Then we can use the times function with format
library(chron)
(tms <- times(format(a, "%H:%M:%S")))
# [1] 01:30:00 06:29:00 06:59:00 09:54:00 10:14:00 15:15:00
attributes(tms)
# $format
# [1] "h:m:s"
#
# $class
# [1] "times"
You can use the hms (hour-minute-second) series of functions in the lubridate package.
library(lubridate)
times = c("1:30 AM", "6:29 AM", "6:59 AM", "9:54 AM", "2:45 PM")
I was hoping you could just do:
hm(times)
[1] "1H 30M 0S" "6H 29M 0S" "6H 59M 0S" "9H 54M 0S" "2H 45M 0S"
But notice that hm doesn't recognize the AM/PM distinction. So here's a more convoluted method that requires first using strptime, which does recognize AM/PM, and then putting the result in a form hm recognizes.
hm(paste0(hour(strptime(times, "%I:%M %p")),":",
minute(strptime(times, "%I:%M %p"))))
[1] "1H 30M 0S" "6H 29M 0S" "6H 59M 0S" "9H 54M 0S" "14H 45M 0S"
There's probably a better way, but this seems to work.
UPDATE: To address your comment, you can use the hour and minute functions to get the hours and minutes (although I like #RichardScriven's answer better). For example:
hour(times)
[1] 1 6 6 9 14
mean(hour(times) + minute(times)/60)
[1] 7.923333

Convert hour:minute:second (HH:MM:SS) string to proper time class

In the following data frame the 'time' column is character in the format hour:minute:second
id <- c(1, 2, 3, 4)
time <- c("00:00:01", "01:02:00", "09:30:01", "14:15:25")
df <- data.frame(id, time)
How can I convert 'time' column to a dedicated time class, so that I can perform arithmetic calculations on it?
Use the function chron in package chron:
time<-c("00:00:01", "01:02:00", "09:30:01", "14:15:25")
library(chron)
x <- chron(times=time)
x
[1] 00:00:01 01:02:00 09:30:01 14:15:25
Do some useful things, like calculating the difference between successive elements:
diff(x)
[1] 01:01:59 08:28:01 04:45:24
chron objects store the values internally as a fraction of seconds per day. Thus 1 second is equivalent to 1/(60*60*24), or 1/86400, i.e. 1.157407e-05.
So, to add times, one simple option is this:
x + 1/86400
[1] 00:00:02 01:02:01 09:30:02 14:15:26
Using base R you could convert it to an object of class POSIXct, but this does add a date to the time:
id<-c(1,2,3,4)
time<-c("00:00:01","01:02:00","09:30:01","14:15:25")
df<-data.frame(id,time,stringsAsFactors=FALSE)
as.POSIXct(df$time,format="%H:%M:%S")
[1] "2012-08-20 00:00:01 CEST" "2012-08-20 01:02:00 CEST"
[3] "2012-08-20 09:30:01 CEST" "2012-08-20 14:15:25 CEST"
But that does allow you to perform arithmetic calculations on them.
Another possible alternative could be:
time <- c("00:00:01","01:02:00","09:30:01","14:15:25")
converted.time <- as.difftime(time, units = "mins") #"difftime" class
secss <- as.numeric(converted.time, units = "secs")
hourss <- as.numeric(converted.time, units = "hours")
dayss <- as.numeric(converted.time, units="days")
Or even:
w <- strptime(x = time, format = "%H:%M:%S") #"POSIXlt" "POSIXt" class
Using the ITime class in data.table package:
ITime is a time-of-day class stored as the integer number of seconds in the day.
library(data.table)
(it <- as.ITime(time))
# [1] "00:00:01" "01:02:00" "09:30:01" "14:15:25"
it + 10
# [1] "00:00:11" "01:02:10" "09:30:11" "14:15:35"
diff(it)
# [1] "01:01:59" "08:28:01" "04:45:24"
lubridate allows good flexibility on the time format :
library(lubridate)
time_hms_1<-c("00:00:01", "01:02:00", "09:30:01", "14:15:25")
hms(time_hms_1)
#> [1] "1S" "1H 2M 0S" "9H 30M 1S" "14H 15M 25S"
time_hms_2<-c("0:00:01", "1:02:00", "9:30:01", "14:15:25")
hms(time_hms_2)
#> [1] "1S" "1H 2M 0S" "9H 30M 1S" "14H 15M 25S"
time_hm_1<-c("00:00", "01:02", "09:30", "14:15")
hm(time_hm_1)
#> [1] "0S" "1H 2M 0S" "9H 30M 0S" "14H 15M 0S"
time_hm_2<-c("0:00", "1:02", "9:30", "14:15")
hm(time_hm_2)
#> [1] "0S" "1H 2M 0S" "9H 30M 0S" "14H 15M 0S"
Created on 2020-07-03 by the reprex package (v0.3.0)
Yet another alternative using the hms package.
id <- c(1, 2, 3, 4)
time <- c("00:00:01", "01:02:00", "09:30:01", "14:15:25")
df <- data.frame(id, time, stringsAsFactors = FALSE)
Convert column time to class hms
# install.packages("hms")
library(hms)
df$time <- as.hms(df$time)
Perform arithmetic calculations
diff(df$time)
#01:01:59
#08:28:01
#04:45:24

Resources