timechange provides rounding to the nearest unit or multiple of a unit with fractional support whenever makes sense. Units can be specified flexibly as strings. All common abbreviations are supported - secs, min, mins, 2 minutes, 3 years, 2s, 1d etc.
Usage
time_round(
time,
unit = "second",
week_start = getOption("timechange.week_start", 1),
origin = unix_origin
)
time_floor(
time,
unit = "seconds",
week_start = getOption("timechange.week_start", 1),
origin = unix_origin
)
time_ceiling(
time,
unit = "seconds",
change_on_boundary = inherits(time, "Date"),
week_start = getOption("timechange.week_start", 1),
origin = unix_origin
)Arguments
- time
a date-time vector (
Date,POSIXctorPOSIXlt)- unit
a character string specifying a time unit or a multiple of a unit. Valid base periods for civil time rounding are
second,minute,hour,day,week,month,bimonth,quarter,season,halfyearandyear. The only units for absolute time rounding areasecond,aminuteandahour. Other absolute units can be achieved with multiples ofasecond(e.g. "24ah"). See "Details" and examples. Arbitrary unique English abbreviations are allowed. One letter abbreviations followstrptimeformats "y", "m", "d", "M", "H", "S". Multi-unit rounding of weeks is currently not supported.Rounding for a unit is performed from the parent's unit origin. For example when rounding to seconds origin is start of the minute. When rounding to days, origin is first date of the month. See examples.
With fractional sub-unit (unit < 1) rounding with child unit is performed instead. For example 0.5mins == 30secs, .2hours == 12min etc.
Please note that for fractions which don't match exactly to integer number of the child units only the integer part is used for computation. For example .7days = 16.8hours will use 16 hours during the computation.
- week_start
When unit is
weeks, this is the first day of the week. Defaults to 1 (Monday).- origin
Origin with respect to which to perform the rounding operation. For absolute units only. Can be a vector of the same length as the input
timevector. Defaults to the Unix origin "1970-01-01 UTC".- change_on_boundary
If NULL (the default) don't change instants on the boundary (
time_ceiling(ymd_hms('2000-01-01 00:00:00'))is2000-01-01 00:00:00), but round upDateobjects to the next boundary (time_ceiling(ymd("2000-01-01"), "month")is"2000-02-01"). WhenTRUE, instants on the boundary are rounded up to the next boundary. WhenFALSE, date-time on the boundary are never rounded up (this was the default for lubridate prior tov1.6.0. See sectionRounding Up Date Objectsbelow for more details.
Value
An object of the same class as the input object. When input is a Date
object and unit is smaller than day a POSIXct object is returned.
Civil Time vs Absolute Time rounding
Rounding in civil time is done on actual clock time (ymdHMS) and is affected by civil time irregularities like DST. One important characteristic of civil time rounding is that floor (ceiling) does not produce civil times that are bigger (smaller) than the original civil time.
Absolute time rounding (with aseconds, aminutes and ahours) is done on the
absolute time (number of seconds since origin), thus, allowing for fractional seconds
and arbitrary multi-units. See examples of rounding around DST transition where
rounding in civil time does not give the same result as rounding with the
corresponding absolute units. Also note that round.POSIXt() rounds on absolute
time.
Please note that absolute rounding to fractions smaller than 1ms will result in large precision errors due to the floating point representation of the POSIXct objects.
Note on time_round()
For rounding date-times which is exactly halfway between two consecutive units,
the convention is to round up. Note that this is in line with the behavior of R's
base::round.POSIXt() function but does not follow the convention of the base
base::round() function which "rounds to the even digit" per IEC 60559.
Ceiling of Date objects
By default rounding up Date objects follows 3 steps:
Convert to an instant representing lower bound of the Date:
2000-01-01–>2000-01-01 00:00:00Round up to the next closest rounding unit boundary. For example, if the rounding unit is
monththen next closest boundary of2000-01-01is2000-02-01 00:00:00.The motivation for this is that the "partial"
2000-01-01is conceptually an interval (2000-01-01 00:00:00–2000-01-02 00:00:00) and the day hasn't started clocking yet at the exact boundary00:00:00. Thus, it seems wrong to round up a day to its lower boundary.The behavior on the boundary can be changed by setting
change_on_boundaryto a non-NULLvalue.If rounding unit is smaller than a day, return the instant from step 2 (
POSIXct), otherwise convert to and return aDateobject.
Examples
## print fractional seconds
options(digits.secs=6)
x <- as.POSIXct("2009-08-03 12:01:59.23")
time_round(x, ".5 asec")
#> [1] "2009-08-03 12:01:59 UTC"
time_round(x, "sec")
#> [1] "2009-08-03 12:01:59 UTC"
time_round(x, "second")
#> [1] "2009-08-03 12:01:59 UTC"
time_round(x, "asecond")
#> [1] "2009-08-03 12:01:59 UTC"
time_round(x, "minute")
#> [1] "2009-08-03 12:02:00 UTC"
time_round(x, "5 mins")
#> [1] "2009-08-03 12:00:00 UTC"
time_round(x, "5M") # "M" for minute "m" for month
#> [1] "2009-08-03 12:00:00 UTC"
time_round(x, "hour")
#> [1] "2009-08-03 12:00:00 UTC"
time_round(x, "2 hours")
#> [1] "2009-08-03 12:00:00 UTC"
time_round(x, "2H")
#> [1] "2009-08-03 12:00:00 UTC"
time_round(x, "day")
#> [1] "2009-08-04 UTC"
time_round(x, "week")
#> [1] "2009-08-03 UTC"
time_round(x, "month")
#> [1] "2009-08-01 UTC"
time_round(x, "bimonth")
#> [1] "2009-09-01 UTC"
time_round(x, "quarter") == time_round(x, "3 months")
#> [1] TRUE
time_round(x, "halfyear")
#> [1] "2009-07-01 UTC"
time_round(x, "year")
#> [1] "2010-01-01 UTC"
x <- as.POSIXct("2009-08-03 12:01:59.23")
time_floor(x, ".1 asec")
#> [1] "2009-08-03 12:01:59.2 UTC"
time_floor(x, "second")
#> [1] "2009-08-03 12:01:59 UTC"
time_floor(x, "minute")
#> [1] "2009-08-03 12:01:00 UTC"
time_floor(x, "M")
#> [1] "2009-08-03 12:01:00 UTC"
time_floor(x, "hour")
#> [1] "2009-08-03 12:00:00 UTC"
time_floor(x, ".2 ahour")
#> [1] "2009-08-03 12:00:00 UTC"
time_floor(x, "day")
#> [1] "2009-08-03 UTC"
time_floor(x, "week")
#> [1] "2009-08-03 UTC"
time_floor(x, "m")
#> [1] "2009-08-01 UTC"
time_floor(x, "month")
#> [1] "2009-08-01 UTC"
time_floor(x, "bimonth")
#> [1] "2009-07-01 UTC"
time_floor(x, "quarter")
#> [1] "2009-07-01 UTC"
time_floor(x, "season")
#> [1] "2009-06-01 UTC"
time_floor(x, "halfyear")
#> [1] "2009-07-01 UTC"
time_floor(x, "year")
#> [1] "2009-01-01 UTC"
x <- as.POSIXct("2009-08-03 12:01:59.23")
time_ceiling(x, ".1 asec")
#> [1] "2009-08-03 12:01:59.299999 UTC"
time_ceiling(x, "second")
#> [1] "2009-08-03 12:02:00 UTC"
time_ceiling(x, "minute")
#> [1] "2009-08-03 12:02:00 UTC"
time_ceiling(x, "5 mins")
#> [1] "2009-08-03 12:05:00 UTC"
time_ceiling(x, "hour")
#> [1] "2009-08-03 13:00:00 UTC"
time_ceiling(x, ".2 ahour")
#> [1] "2009-08-03 12:12:00 UTC"
time_ceiling(x, "day")
#> [1] "2009-08-04 UTC"
time_ceiling(x, "week")
#> [1] "2009-08-10 UTC"
time_ceiling(x, "month")
#> [1] "2009-09-01 UTC"
time_ceiling(x, "bimonth") == time_ceiling(x, "2 months")
#> [1] TRUE
time_ceiling(x, "quarter")
#> [1] "2009-10-01 UTC"
time_ceiling(x, "season")
#> [1] "2009-09-01 UTC"
time_ceiling(x, "halfyear")
#> [1] "2010-01-01 UTC"
time_ceiling(x, "year")
#> [1] "2010-01-01 UTC"
## behavior on the boundary
x <- as.Date("2000-01-01")
time_ceiling(x, "month")
#> [1] "2000-02-01"
time_ceiling(x, "month", change_on_boundary = FALSE)
#> [1] "2000-01-01"
## As of R 3.4.2 POSIXct printing of fractional seconds is wrong
as.POSIXct("2009-08-03 12:01:59.3", tz = "UTC") ## -> "2009-08-03 12:01:59.2 UTC"
#> [1] "2009-08-03 12:01:59.299999 UTC"
time_ceiling(x, ".1 asec") ## -> "2009-08-03 12:01:59.2 UTC"
#> [1] "2000-01-01"
## Civil Time vs Absolute Time Rounding
# "2014-11-02 01:59:59.5 EDT" before 1h backroll at 2AM
x <- .POSIXct(1414907999.5, tz = "America/New_York")
x
#> [1] "2014-11-02 01:59:59.5 EDT"
time_ceiling(x, "hour") # "2014-11-02 02:00:00 EST"
#> [1] "2014-11-02 02:00:00 EST"
time_ceiling(x, "ahour") # "2014-11-02 01:00:00 EST"
#> [1] "2014-11-02 01:00:00 EST"
time_ceiling(x, "minute")
#> [1] "2014-11-02 02:00:00 EST"
time_ceiling(x, "aminute")
#> [1] "2014-11-02 01:00:00 EST"
time_ceiling(x, "sec")
#> [1] "2014-11-02 02:00:00 EST"
time_ceiling(x, "asec")
#> [1] "2014-11-02 01:00:00 EST"
time_round(x, "hour") # "2014-11-02 01:00:00 EDT" !!
#> [1] "2014-11-02 01:00:00 EDT"
time_round(x, "ahour") # "2014-11-02 01:00:00 EST"
#> [1] "2014-11-02 01:00:00 EST"
round.POSIXt(x, "hour") # "2014-11-02 01:00:00 EST"
#> [1] "2014-11-02 01:00:00 EST"
# "2014-11-02 01:00:00.5 EST" .5s after 1h backroll at 2AM
x <- .POSIXct(1414908000.5, tz = "America/New_York")
x
#> [1] "2014-11-02 01:00:00.5 EST"
time_floor(x, "hour") # "2014-11-02 01:00:00 EST"
#> [1] "2014-11-02 01:00:00 EST"
time_floor(x, "ahour") # "2014-11-02 01:00:00 EST"
#> [1] "2014-11-02 01:00:00 EST"
## Behavior on the boundary when rounding multi-units
x <- as.POSIXct("2009-08-28 22:56:59.23", tz = "UTC")
time_ceiling(x, "3.4 secs") # "2009-08-28 22:57:03.4"
#> [1] "2009-08-28 22:57:03.4 UTC"
time_ceiling(x, "50.5 secs") # "2009-08-28 22:57:50.5"
#> [1] "2009-08-28 22:57:50.5 UTC"
time_ceiling(x, "57 min") # "2009-08-28 22:57:00"
#> [1] "2009-08-28 22:57:00 UTC"
time_ceiling(x, "56 min") # "2009-08-28 23:56:00"
#> [1] "2009-08-28 23:56:00 UTC"
time_ceiling(x, "7h") # "2009-08-29 07:00:00"
#> [1] "2009-08-29 07:00:00 UTC"
time_ceiling(x, "7d") # "2009-08-29 00:00:00"
#> [1] "2009-08-29 UTC"
time_ceiling(x, "8d") # "2009-09-09 00:00:00"
#> [1] "2009-09-09 UTC"
time_ceiling(x, "8m") # "2009-09-01 00:00:00"
#> [1] "2009-09-01 UTC"
time_ceiling(x, "6m") # "2010-01-01 00:00:00"
#> [1] "2010-01-01 UTC"
time_ceiling(x, "7m") # "2010-08-01 00:00:00"
#> [1] "2010-08-01 UTC"
x <- as.POSIXct("2010-11-25 22:56:57", tz = "UTC")
time_ceiling(x, "6sec") # "2010-11-25 22:57:00"
#> [1] "2010-11-25 22:57:00 UTC"
time_ceiling(x, "60sec") # "2010-11-25 22:57:00"
#> [1] "2010-11-25 22:57:00 UTC"
time_ceiling(x, "6min") # "2010-11-25 23:00:00"
#> [1] "2010-11-25 23:00:00 UTC"
time_ceiling(x, "60min") # "2010-11-25 23:00:00"
#> [1] "2010-11-25 23:00:00 UTC"
time_ceiling(x, "4h") # "2010-11-26 00:00:00"
#> [1] "2010-11-26 UTC"
time_ceiling(x, "15d") # "2010-12-01 00:00:00"
#> [1] "2010-12-01 UTC"
time_ceiling(x, "15d") # "2010-12-01 00:00:00"
#> [1] "2010-12-01 UTC"
time_ceiling(x, "6m") # "2011-01-01 00:00:00"
#> [1] "2011-01-01 UTC"
## custom origin
x <- as.POSIXct(c("2010-10-01 01:00:01", "2010-11-02 02:00:01"), tz = "America/New_York")
# 50 minutes from the day or month start
time_floor(x, "50amin")
#> [1] "2010-10-01 00:40:00 EDT" "2010-11-02 01:50:00 EDT"
time_floor(x, "50amin", origin = time_floor(x, "day"))
#> [1] "2010-10-01 00:50:00 EDT" "2010-11-02 01:40:00 EDT"
time_floor(x, "50amin", origin = time_floor(x, "month"))
#> [1] "2010-10-01 00:50:00 EDT" "2010-11-02 01:50:00 EDT"
time_ceiling(x, "50amin")
#> [1] "2010-10-01 01:30:00 EDT" "2010-11-02 02:40:00 EDT"
time_ceiling(x, "50amin", origin = time_floor(x, "day"))
#> [1] "2010-10-01 01:40:00 EDT" "2010-11-02 02:30:00 EDT"
time_ceiling(x, "50amin", origin = time_floor(x, "month"))
#> [1] "2010-10-01 01:40:00 EDT" "2010-11-02 02:40:00 EDT"