POSIXlt 12-hour time with-out leading zeros - r

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.

Related

How to convert a date/time object into a decimal?

I have an object in R that I have converted to a POSIXct object:
data<- data.frame(date_time= c('2021-06-24 18:37:00', '2021-06-24 19:07:00', '2021-06-24 19:37:00', '2021-06-24 20:07:00','2021-06-24 20:37:00'))
data$date_time<- as.POSIXct(data$date_time, format = "%Y-%m-%d %H:%M:%S")
I want to convert this column to a decimal that gets bigger as the time passes. For example, '2021-06-24 18:37:00' should be smaller than '2021-06-24 19:07:00' and so on. However everything that I have tried so far does yield a decimal, but it does not get bigger as the time goes on. I have tried this:
data$date_time2<- yday(data$date_time) + hour(data$date_time)/24 + minute(data$date_time)/60
However this yields:
[1] 176.3667 175.9083 176.4083 175.9500 176.4500
I need the numbers to increase incrementally as minutes go by. Any help?
A datetime object is an integer counting the number of seconds from 1/1/1970. So this works as.integer(data$date_time) to create an integer value. Note the datetime is reference to GMT timezone.
To get the date as a decimal, requires the use of some integer math. The end result is the number of days from 1/1/1970 and the time as fraction.
data<- data.frame(date_time= c('2021-06-24 18:37:00', '2021-06-24 19:07:00', '2021-06-24 19:37:00', '2021-06-24 20:07:00','2021-06-24 20:37:00'))
data$date_time<- as.POSIXct(data$date_time, format = "%Y-%m-%d %H:%M:%S", tz="GMT")
intvalue <- as.integer(data$date_time)
#numbers of seconds, take the MOD with the seconds per day
#divide the result by seconds per day to make the decimal part
decfraction <- intvalue%%(3600*24)/(3600*24)
#perform integer division to get the number of days
days <- intvalue%/%(3600*24)
# or as.integer(as.Date(data$date_time))
#put together for the final answer
dateAsDecimal <- days + decfraction
#result
#18802.78 18802.80 18802.82 18802.84 18802.86
If you are only concerned that the number mapped to preserves order then xtfrm will map objects to order preserving numbers. In the case of POSIXct objects it just returns the internal numeric representation, i.e. seconds since the UNIX Epoch.
xtfrm(data$date_time)

How to format time zone offset in lubridate

I want to format the date in ISO 8601 format using lubridate. At the moment the code I have parses the date almost the way I want. The only thing I want to change is to have a colon in the time zone offset. My code at the moment:
dateTime <- str_match(fileName, dateTimeRegex)[2] %>% ymd_hms() %>% strftime(format = "%y-%m-%dT%H:%M:%S%z", tz = "UTC")
Sample output:
"19-09-26T10:45:00+0000"
Expected output:
"19-09-26T10:45:00+00:00"
Is there a simple way to do it, without parsing this manually? %z creates 0000, but I need a colon there
From Wikipedia (emphasis mine):
The UTC offset is the difference in hours and minutes from Coordinated Universal Time (UTC, or GMT) for a particular place and date. It is generally shown in the format ±[hh]:[mm], ±[hh][mm], or ±[hh]. So if the time being described is one hour ahead of UTC (such as the time in Berlin during the winter), the UTC offset would be "+01:00", "+0100", or simply "+01".
HH:MM is just one way to format time offsets, the others being HHMM and HH, so your output conforms to ISO 8601.
We can use regex to achieve your desired output. Using sub
x <- "19-09-26T10:45:00+0000"
sub("(.*\\+)(\\d{2})(\\d{2})", "\\1\\2:\\3", x)
#[1] "19-09-26T10:45:00+00:00"

Switch between dates and seconds in a well defined manner

I run discrete event simulations where the time originates from dates. I think that simulations run much faster, when I convert all the dates to integers (relative time in seconds).
What is the best way, to switch between date and seconds in a well definied way where I want to
set the reference time (e.g. "1970-01-01 00:00:00 GMT" or "2016-01-01 00:00:00 GMT") manually,
the time zone and
the origin (Not possible in lubridate?)
I thought I can use the origin for this purpose but it does not influence the result:
> as.numeric(as.POSIXct("2016-01-01 00:00:00 GMT",origin="2016-01-01",tz="GMT"))
> as.numeric(as.POSIXct("2016-01-01 00:00:00 GMT",origin="1970-01-01",tz="GMT"))
both result in [1] 1451606400.
(Only the tz argument changes the result, which is ok of course:
> as.numeric(as.POSIXct("2016-01-01 00:00:00 CEST", tz= "America/Chicago"))
[1] 1451628000)
You can use difftime() to calculate the difference between some timestamp and a reference time:
as.numeric(difftime(as.POSIXct("2016-01-01 00:00:00",tz="GMT"),
as.POSIXct("1970-01-01 00:00:00",tz="GMT"), units = "secs"))
## [1] 1451606400
By choosing another value for units, you could also get the number of minutes, hours, etc.
The reason that you get the same result for both choices of origin is that this argument is only intended to be used when converting a number into a date. Then, the number is interpreted as seconds since the origin that you pass to the function.
Internally, a POSIXct object is always stored as seconds since 1970-01-01 00:00:00, UTC, independent of the origin that you specified when doing the conversion. And accordingly, converting to numeric gives the same result for any choice of origin.
You can have a look at the documentation of as.POSIXct():
## S3 method for class 'character'
as.POSIXlt(x, tz = "", format, ...)
## S3 method for class 'numeric'
as.POSIXlt(x, tz = "", origin, ...)
As you can see, origin is only an argument for the method for numeric, but not for character.

Converting time format to numeric with R

In most cases, we convert numeric time to POSIXct format using R. However, if we want to compare two time points, then we would prefer the numeric time format. For example, I have a date format like "2001-03-13 10:31:00",
begin <- "2001-03-13 10:31:00"
Using R, I want to covert this into a numeric (e.g., the Julian time), perhaps something like the passing seconds between 1970-01-01 00:00:00 and 2001-03-13 10:31:00.
Do you have any suggestions?
The Julian calendar began in 45 BC (709 AUC) as a reform of the Roman calendar by Julius Caesar. It was chosen after consultation with the astronomer Sosigenes of Alexandria and was probably designed to approximate the tropical year (known at least since Hipparchus). see http://en.wikipedia.org/wiki/Julian_calendar
If you just want to remove ":" , " ", and "-" from a character vector then this will suffice:
end <- gsub("[: -]", "" , begin, perl=TRUE)
#> end
#[1] "20010313103100"
You should read the section about 1/4 of the way down in ?regex about character classes. Since the "-" is special in that context as a range operator, it needs to be placed first or last.
After your edit then the answer is clearly what #joran wrote, except that you would need first to convert to a DateTime class:
as.numeric(as.POSIXct(begin))
#[1] 984497460
The other point to make is that comparison operators do work for Date and DateTime classed variables, so the conversion may not be necessary at all. This compares 'begin' to a time one second later and correctly reports that begin is earlier:
as.POSIXct(begin) < as.POSIXct(begin) +1
#[1] TRUE
Based on the revised question this should do what you want:
begin <- "2001-03-13 10:31:00"
as.numeric(as.POSIXct(begin))
The result is a unix timestamp, the number of seconds since epoch, assuming the timestamp is in the local time zone.
Maybe this could also work:
library(lubridate)
...
df <- '24:00:00'
as.numeric(hms(df))
hms() will convert your data from one time format into another, this will let you convert it into seconds. See full documentation.
I tried this because i had trouble with data which was in that format but over 24 hours.
The example from ?as.POSIX help gives
as.POSIXct(strptime(begin, "%Y-%m-%d %H:%M:%S"))
so for you it would be
as.numeric(as.POSIXct(strptime(begin, "%Y-%m-%d %H:%M:%S")))

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