Find previous hour and next hour in R - r

Suppose I pass "2015-01-01 01:50:50", then it should return "2015-01-01 01:00:00" and "2015-01-01 02:00:00". How to calculate these values in R?

Assuming your time were a variable "X", you can use round or trunc.
Try:
round(X, "hour")
trunc(X, "hour")
This would still require some work to determine whether the values had actually been rounded up or down (for round). So, If you don't want to have to think about that, you can consider using the "lubridate" package:
X <- structure(c(1430050590.96162, 1430052390.96162), class = c("POSIXct", "POSIXt"))
X
# [1] "2015-04-26 17:46:30 IST" "2015-04-26 18:16:30 IST"
library(lubridate)
ceiling_date(X, "hour")
# [1] "2015-04-26 18:00:00 IST" "2015-04-26 19:00:00 IST"
floor_date(X, "hour")
# [1] "2015-04-26 17:00:00 IST" "2015-04-26 18:00:00 IST"

I would go with the following wrapper using base R (you can specify your time zone using the tz argument within the strptime function)
Myfunc <- function(x){x <- strptime(x, format = "%F %H") ; c(x, x + 3600L)}
Myfunc("2015-01-01 01:50:50")
## [1] "2015-01-01 01:00:00 IST" "2015-01-01 02:00:00 IST"

Related

Trying to append dates in R but the time component is being dropped?

I have come across this problem. Consider an array of dates:
> y <- c("2012-01-02 10:00:00", "2012-01-03 09:00:00")
> as.POSIXct(y)
[1] "2012-01-02 10:00:00" "2012-01-03 09:00:00"
Now consider the same array with "2012-01-04 00:00:00". This vector has already undergone as.POSIXct, hence we get c("2012-01-02 10:00:00", "2012-01-03 09:00:00", "2012-01-04"). Then
> y <- c("2012-01-02 10:00:00", "2012-01-03 09:00:00", "2012-01-04")
> as.POSIXct(y)
[1] "2012-01-02 GMT" "2012-01-03 GMT" "2012-01-04 GMT"
Now the times have been dropped from all the dates rather than just adding back 00:00:00 to 2012-01-04. I've played around with formatting POSIXct and strptime, but am not getting anywhere. For example:
> as.POSIXct(strptime("2011-03-27 00:00:00", "%Y-%m-%d %H:%M:%S"))
[1] "2011-03-27 GMT"
How can I retrieve the correct values?
This can be solved by using anytime
anytime::anytime(y)
#[1] "2012-01-02 10:00:00 IST" "2012-01-03 09:00:00 IST" "2012-01-04 00:00:00 IST"
Note that anytime have a default set of formats. We can find those with getFormats(). If the format is not in the list, then use the addFormats to add a format
Or another way is by using parse_date_time from lubridate
lubridate::parse_date_time(y, guess_formats(y, c('ymd', 'ymd_HMS')))
#[1] "2012-01-02 10:00:00 UTC" "2012-01-03 09:00:00 UTC" "2012-01-04 00:00:00 UTC"
We guess the problem stems from the multiple format in the input vector and as.POSIXct takes only a single format, so probably it unifies the format to a single one by removing the time substring from the other elements. This was tested on R 3.4.0 and on R 3.1.3 and both get the same output as in the OP's post

Why does append convert timestamps in R?

I want to join a list of time zones into one long vector. Append seems to convert the time zones into the system time zone? Why?
> times = list(as.POSIXct("2015-01-01", tz = 'UTC', origin = "1970-01-01"),
+ as.POSIXct("2015-01-02", tz = 'UTC', origin = "1970-01-01"))
> times
[[1]]
[1] "2015-01-01 UTC"
[[2]]
[1] "2015-01-02 UTC"
> do.call(append, times)
[1] "2014-12-31 19:00:00 EST" "2015-01-01 19:00:00 EST"
I can't use unlist as it strips the list of the POSIXct class. What is the alternative?

How to add the time to a date when using as.date?

I have measurements that were taken at this time: 13880 and they represent "days since 1970-01-01 00:00:00"
So now I want to know the dat and time:
as.Date(13880, origin="1970-01-01")
[1] "2008-01-02" # works fine
Now to add the time:
as.Date(13880, origin="1970-01-01",tz = "UTC", format="%Y/%m/%d %H:%M:%S")
[1] NA
or
as.POSIXct(13880, origin="1970-01-01")
[1] "1970-01-01 04:51:20 CET"
as.POSIXlt(13879, origin="1970-01-01")
[1] "1970-01-01 04:51:19 CET"
None of these worked for me. Any idea?
as.POSIXct(as.Date("1970-01-01") + 13880) # returns "2008-01-01 19:00:00 EST"
as.POSIXct(as.Date("1970-01-01") + 13880.5) # returns "2008-01-02 07:00:00 EST"
You can also set your time zone:
How to change the default time zone in R?
also: http://blog.revolutionanalytics.com/2009/06/converting-time-zones.html

fastPOSIXct equivalent for converting non-UTC to UTC

Hi I have a character vector (rr) that is several million in length, and it represents time and date stamps in the format %Y-%m-%d %H:%M:%S recorded in Australia/Sydney.
How do get a POSIXct object (quickly) that represents this.
I have found fastPOSIXct in the fasttime package, but for this to be accurate, it requires the original character string to be in GMT/UTC, (which mine is not) and then converted back into the correct timezone using the tz arguement...
> head(rr)
[1] "2009-05-01 10:01:00" "2009-05-01 10:02:00" "2009-05-01 10:03:00" "2009-05-01 10:04:00"
[5] "2009-05-01 10:05:00" "2009-05-01 10:06:00"
> as.POSIXct(head(rr),tz="Australia/Sydney")
[1] "2009-05-01 10:01:00 EST" "2009-05-01 10:02:00 EST" "2009-05-01 10:03:00 EST"
[4] "2009-05-01 10:04:00 EST" "2009-05-01 10:05:00 EST" "2009-05-01 10:06:00 EST"
The above line takes ages if doing it on the full set of data...so any speed improvements would be appreciated. Thanks.
Inspired by Dirk's answer to this qn, I made this wrapper for handling a whole bunch of dates across the year:
fastPOSIXct_generic <- function(x, mytz = "America/New_York")
{
# Caution, read: ?DateTimeClasses
stopifnot(is.character(x))
times_UTC <- fastPOSIXct(x, tz='UTC')
num_times <- as.numeric(times_UTC)
t1 <- as.POSIXct(x[1], tz = mytz)
t2 <- as.POSIXct(x[1], tz = "UTC")
offset <- as.numeric(difftime(t1, t2, units = "secs"))
daylightoffset <- as.POSIXlt(t1)$isdst
# For this first 'time' in t1 and t2, remove possible impact of losing one hour by setting clocks one hour forward during summer months:
offset <- offset + daylightoffset * 3600
num_times <- num_times + offset
new_num_times <- as.POSIXct(num_times, tz = mytz, origin = '1970-01-01')
new_num_times2 <- new_num_times - as.POSIXlt(new_num_times)$isdst * 3600
return(new_num_times2)
}
# Test Sydney time
mm <- as.POSIXct(c("2015-03-15 15:00:00", "2015-4-10 15:00:00", "2014-10-01 15:00:00", "2015-10-15 15:00:00"), tz = "Australia/Sydney")
# "2015-03-15 15:00:00 AEDT" "2015-04-10 15:00:00 AEST" "2014-10-01 15:00:00 AEST" "2015-10-15 15:00:00 AEDT"
aus_stamps <- as.character(mm)
aus_back <- fastPOSIXct_generic(x = aus_stamps, mytz = "Australia/Sydney")
#"2015-03-15 15:00:00 AEDT" "2015-04-10 15:00:00 AEST" "2014-10-01 15:00:00 AEST" "2015-10-15 15:00:00 AEDT"
identical(mm, aus_back)
# TRUE
My use cases are nearly always UTC to America/New_York, where so far it has seemed to work fine. I don't know whether it works correctly for other time zones; just the cases where dst has time go forward an hour.
Here is one approach:
i) Lie to fasttime() and pretend the data was UTC, use to parse the data into a vector x
ii) Compute an offset to UTC using your first data point:
R> d1 <- "2009-05-01 10:01:01" ## or use `head(rr,1)`
R> t1 <- as.POSIXct(d1,tz="Australia/Sydney")
R> t2 <- as.POSIXct(d1,tz="UTC")
R> offset <- as.numeric(difftime(t2, t1, units="secs"))
R> offset
[1] 36000
iii) Apply the offset value to your data -- that is a quick addition as POSIXct really is a numeric type with (fractional) seconds (since epoch) as its unit.

Why is the epoch date correct, but does not work as an offset for POSIXct objects?

I have a vector of POSIXct objects:
> dates <- seq(as.POSIXct("2004-01-01", tz="EST"), as.POSIXct("2004-01-02", tz="EST"), as.difftime(6, units="hours"))
> dates
[1] "2004-01-01 00:00:00 EST" "2004-01-01 06:00:00 EST"
[3] "2004-01-01 12:00:00 EST" "2004-01-01 18:00:00 EST"
[5] "2004-01-02 00:00:00 EST"
I create an epoch variable that defines a POSIXct object for the UNIX epoch:
> epoch <- strptime("1970-01-01 00:00:00", "%Y-%m-%d %H:%M:%S", tz="EST")
> class(epoch)
[1] "POSIXct" "POSIXt"
> epoch
[1] "1970-01-01 EST"
I then loop through the dates vector and print out the value, offset from epoch:
> for (d in dates) { print(as.POSIXct(d, origin=epoch, tz="EST")) }
[1] "2004-01-01 05:00:00 EST"
[1] "2004-01-01 11:00:00 EST"
[1] "2004-01-01 17:00:00 EST"
[1] "2004-01-01 23:00:00 EST"
[1] "2004-01-02 05:00:00 EST"
There seems to be a five-hour offset error between the values in dates and the representation of those same values, relative to epoch.
There is a +5 hr difference between EST and UTC, but I specified the EST time zone for epoch with the tz option. Printing out epoch, there doesn't seem to be the time information, only the date.
Is there a bug with strptime or as.POSIXct, or am I calculating the offset or generating epoch incorrectly?
As mentioned in the answer to For loop style has effect on class coercion?, in the for loop, your dates are converted to numbers. That is the number of seconds since the "standard" epoch. This includes the 5 hour shift between EST and UTC. That is added as an offset to your epoch. See the source of as.POSIXct.numeric.
The following does work because it sets up dates which will be the right number of seconds when converted to numeric.
dates <- seq(as.POSIXct("2004-01-01", tz="UTC"),
as.POSIXct("2004-01-02", tz="UTC"),
as.difftime(6, units="hours"))
epoch <- strptime("1970-01-01 00:00:00", "%Y-%m-%d %H:%M:%S", tz="EST")
for (d in dates) { print(as.POSIXct(d, origin=epoch, tz="EST")) }
Which gives
[1] "2004-01-01 EST"
[1] "2004-01-01 06:00:00 EST"
[1] "2004-01-01 12:00:00 EST"
[1] "2004-01-01 18:00:00 EST"
[1] "2004-01-02 EST"

Resources