Reputation: 959
I have created a time sequence from 00:00:00 hrs to 23:00:00 hrs. Adding any number of hours is easy until the sum is less than or equal to 23:00:00 hrs. After the sum crosses 23:00:00 it start displaying the time in number of days which is quite intuitive.But I want the output inside a clock time irrespective of whether I do subtraction or addition
Suppose I want to do addition like below
library(chron)
times("23:00:00")+ times("01:00:00")
Time in days:
[1] 1
My desired output is below one. Instead of getting days I want
00:00:00
I also tried subtraction
times("00:00:00")- times("01:00:00")
[1] -0.04166667
Desired output
"23:00:00"
I also tried it with POSIXct but it gives various error at various instances
as.POSIXct("00:00:00", format = "%H:%M:%S", tz = "UTC")
[1] "2017-02-07 UTC" #Not printing time. Only dates
Subtraction using POSIXct
as.POSIXct("00:00:00", format = "%H:%M:%S", tz = "UTC") -as.POSIXct("01:00:00", format = "%H:%M:%S", tz = "UTC")
Time difference of -1 hours
Warning message:
In 1:0:0 : numerical expression has 2 elements: only the first used
Addition using POSIXct
as.POSIXct("23:00:00", format = "%H:%M:%S", tz = "UTC") + as.POSIXct("01:00:00", format = "%H:%M:%S", tz = "UTC")
Error in `+.POSIXt`(as.POSIXct("23:00:00", format = "%H:%M:%S", tz = "UTC"), :
binary '+' is not defined for "POSIXt" objects
Please help me to crack this problem. Also please help me to print the time along with date in POSIXct shown above Please let me know if anything doesn't make sense to you instead of down vote my question.
Upvotes: 7
Views: 414
Reputation: 32548
Note that times
of chron
package internally represents time as a numeric value (decimal days). What you are trying to do is retain the values after the decimal and remove the integer before it. This can be achieved by taking modulus by 1 (try 2.5 %% 1
). So, you can just take modulus by times(1)
and that will get rid of the day.
library(chron)
(times("23:00:00")+ times("01:00:00")) %% times(1)
#[1] 00:00:00
> (times("00:00:00") - times("01:00:00")) %% times(1)
#[1] 23:00:00
Using POSIXct
I would not prefer to use POSIXct
for this since POSIXct
complicates things due to dates and timezones. Internally, POSIXct
are numeric values of seconds from some origin (usually 1970-01-01). So, if you want to add a time interval to a POSIXct
object, just add that time interval in seconds.
To add 1 hour to "23:00:00", you can do
format(as.POSIXct(
as.numeric(as.POSIXct("23:00:00", format = "%H:%M:%S", tz = "UTC")) + 3600,
origin = "1970-01-01", tz = "UTC"), "%H:%M:%S")
#[1] "00:00:00"
Subtraction can work in the same way
format(as.POSIXct(
as.numeric(as.POSIXct("24:00:00", format = "%H:%M:%S", tz = "UTC")) - 3600,
origin = "1970-01-01", tz = "UTC"), "%H:%M:%S")
#[1] "23:00:00"
Upvotes: 3
Reputation: 3174
You can use the lubridate package easily enough.
hms()
, which converts to time format.today()
).Examples:
library(lubridate)
x <- today() + hms("23:00:00") + hms("2:00:00")
format(x, "%H:%M:%S")
#> [1] "01:00:00"
x <- today() + hms("00:00:00") - hms("1:00:00")
format(x, "%H:%M:%S")
#> [1] "23:00:00"
Upvotes: 0
Reputation: 31452
If you actually want to change the way that the times are stored in the variable, so that they are within 0 to 24 hrs, then a simple one line function will suffice:
clock = function(x) x - floor(x)
clock(times("23:00:00") + times("02:00:00"))
# [1] 01:00:00
If you just want the printed display to be always in 24hr format, then the simplest is to edit the S3 print method for objects of class times
.
Typing chron:::print.times
will show you how this function is written in the package. You can make one simple tweak to get what you want (note I also include the full code for the edited function at the end of this post, so you can just copy/paste rather than changing it yourself): at start of print.times
, add the line x = x-floor(x)
.
Testing it works:
times("00:00:00") - times("01:00:00")
# [1] 23:00:00
times("23:00:00") + times("01:00:00")
# [1] 00:00:00
times("23:00:00") + times("02:00:00")
# [1] 01:00:00
times("3:00:00") - times("22:00:00")
# [1] 05:00:00
NB, one advantage of doing it this way is that it will also work with operations other than +
and -
. For example:
sum(c(times("23:00:00"), times("22:00:00"), times("21:00:00"), times("20:00:00")))
# [1] 14:00:00
mean(c(times("23:00:00"), times("22:00:00"), times("21:00:00"), times("20:00:00")))
# [1] 21:30:00
cumsum(c(times("23:00:00"), times("22:00:00"), times("21:00:00"), times("20:00:00")))
# [1] 23:00:00 21:00:00 18:00:00 14:00:00
Note that I also added a parameter normalise
to the function, so that the original functionality of the method is remains available if you specify normalise = FALSE
when printing.
print.times = function (x, digits, quote = FALSE, prefix = "", simplify, normalise = TRUE, ...)
{
if (normalise) x = x-floor(x)
if (!as.logical(length(x))) {
cat("times(0)\n")
return(invisible(x))
}
if (missing(simplify) && is.null(simplify <- getOption("chron.simplify")))
simplify <- FALSE
xo <- x
if (all(is.na(x)) || any(x[!is.na(x)] >= 1))
cat("Time in days:\n")
x <- format.times(x, simplify = simplify)
NextMethod("print", quote = quote)
invisible(xo)
}
environment(print.times) <- environment(chron:::print.times)
Upvotes: 1
Reputation: 37641
For both addition and subtraction, what you need is to keep only the fractional part of the results, throwing away the integer part. It is easy to write simple functions to do this.
library(chron)
hoursum = function(t1, t2) { t1 + t2 - floor(t1+t2) }
hourdiff = function(t1, t2) { t1 - t2 - floor(t1-t2) }
Now you can do all of the computations you want
hoursum(times("23:00:00"), times("01:00:00"))
## [1] 00:00:00
hoursum(times("23:00:00"), times("02:00:00"))
## [1] 01:00:00
hourdiff(times("00:00:00"), times("01:00:00"))
## [1] 23:00:00
hourdiff(times("02:00:00"), times("03:00:00"))
## [1] 23:00:00
hourdiff(times("02:00:00"), times("03:15:00"))
## [1] 22:45:00
Upvotes: 1