lubridate时间间隔的长度

10

如何以特定单位获取 lubridate 中表示时间间隔的时长?我能想到的只有以下这段代码:

> ival
[1] 2011-01-01 03:00:46 -- 2011-10-21 18:33:44

> difftime(attr(ival, "start") + as.numeric(ival), attr(ival, "start"), 'days')
Time difference of 293.6479 days
我还在https://github.com/hadley/lubridate/issues/105中提出了此功能请求,以假设没有更好的方法可用。但也许这里有人知道更好的方法。另外,最新消息是,difftime函数也无法处理这种情况。以下是一个示例。
> (d1 <- as.POSIXct("2011-03-12 12:00:00", 'America/Chicago'))
[1] "2011-03-12 12:00:00 CST"
> (d2 <- d1 + days(1))  # Gives desired result
[1] "2011-03-13 12:00:00 CDT"
> (i2 <- d2 - d1)
[1] 2011-03-12 12:00:00 -- 2011-03-13 12:00:00 
> difftime(attr(i2, "start") + as.numeric(i2), attr(i2, "start"), 'days')
Time difference of 23 hours

正如我下面所提到的,我认为处理这个问题的一种好方法是实现一个/.interval函数,它不会先将其输入转换为period


你说你想用特定单位表示一个区间,但从下面的评论上下文来看,似乎你想把它四舍五入到最近的整个单位。如果是这样,请进一步编辑以使其清晰明确。 - IRTFM
不,我不想它四舍五入。也许我刚刚做的修改有助于澄清? - Ken Williams
4个回答

13
as.duration函数是lubridate提供的。内部表示间隔类(interval class)是从起始时间开始计算的秒数,所以如果你想知道小时数,可以通过将 as.numeric(ival)除以3600来获得,或者通过除以(3600*24)来获取天数。
如果您想要对您的对象应用函数并获得相关实例,请提供dput(ival)的输出结果。我在help(duration)页面上创建的对象上进行了测试,这也是?interval命令将我带到的地方。
 date <- as.POSIXct("2009-03-08 01:59:59") # DST boundary
 date2 <- as.POSIXct("2000-02-29 12:00:00")
 span <- date2 - date  #creates interval 
 span
#[1] 2000-02-29 12:00:00 -- 2009-03-08 01:59:59 
 str(span)
#Classes 'interval', 'numeric'  atomic [1:1] 2.85e+08
#  ..- attr(*, "start")= POSIXct[1:1], format: "2000-02-29 12:00:00"
 as.duration(span)
#[1] 284651999s (9.02y) 
 as.numeric(span)/(3600*24)
#[1] 3294.583
# A check against the messy method:
difftime(attr(span, "start") + as.numeric(span), attr(span, "start"), 'days')
# Time difference of 3294.583 days

谢谢,但那不正确——3600*24并不总是一天的秒数。我想做的是利用已经处理夏令时等事项的系统。 - Ken Williams
@KenWilliams,我不明白你为什么认为这是错误的。?duration似乎意味着它确实尊重DST,就像difftime一样。当我使用as.duration转换你的示例时,我得到了与你使用difftime得到的完全相同的答案。 - joran
您还可以将持续时间除以天数(1)。 - hadley
哎呀,连difftime()也不能计算我要的结果。=( 我需要一个计算方法,在America/Chicago时区中,2011-03-12 12:00:002011-03-13 12:00:00之间相差的天数为1天,即使那一天只有23个小时。我会更新我的问题。 - Ken Williams
@hadley - 我想要的可以通过将一个区间除以days(1)来实现,但目前在lubridate中需要先将其转换为duration,然后(非常正确地)发出警告。 - Ken Williams
请注意,将 days(1) 添加到 2011-03-12 12:00:00 CST 实际上会得到 2011-03-13 12:00:00 CDT,即使这不是 24 小时的时间跨度,但是相同的逻辑似乎不适用于减法/intervals。 - Ken Williams

11

这个问题很旧了,但我添加了更新,因为这个问题已经被查看很多次,而当我今天需要做类似的事情时,我发现了这个页面。在lubridate中,您现在可以执行以下操作:

d1 <- ymd_hms("2011-03-12 12:00:00", tz = 'America/Chicago')
d2 <- ymd_hms("2011-03-13 12:00:00", tz = 'America/Chicago')

(d1 %--% d2)/dminutes(1)
(d1 %--% d2)/dhours(1)
(d1 %--% d2)/ddays(1)
(d1 %--% d2)/dweeks(1)

3
肯,按天数(1)除以时间间隔可以得到你想要的结果。当您将时间间隔除以时间段时,Lubridate不会强制将期间转换为持续时间。(尽管找到间隔中整个时间段的确切数量的算法始于使用将间隔除以类似持续时间的估计值,这可能是您注意到的)。
最终结果是适合该时间间隔的完整周期数。警告消息向用户发出警报,因为它是一个估计值,因为答案中将丢失一些分数周期。使用分数时间段进行计算是不明智的,因为我们无法使用它来修改时钟时间,除非我们将其转换为更短时间段的倍数 - 但是没有一种一致的方法来进行转换。例如,您提到的那一天将等于23小时,但其他天将等于24小时。你的想法是正确的 - 时间段是尝试尊重夏令时、闰年等带来的变化,但只能作为整体单位进行。
我不能重现您在上面提到的减法错误。对我来说看起来可以工作。
    three <- force_tz(ymd_hms("2011-03-12 12:00:00"), "") 
    # note: here in TX, "" *is* CST
    (four <- three + days(1))
    > [1] "2011-03-13 12:00:00 CDT"
    four - days(1)
    > [1] "2011-03-12 12:00:00 CST"

然而,对于一个时间间隔来说,确实有一种方法可以进行转换,因为它是基于一个确切的瞬间。你不知道某个任意的日子是否有24小时,但你确实知道这个具体的日子是否有24小时,所以计算应该是可行的。 - Ken Williams
@KenWilliams 我理解你的意思。目前Lubridate还没有做这个计算,但也许应该做一下。我的想法是余数可能是区间第一天的1/2,或者是区间最后一天的12/23。也许只有最后一天是重要的。 - Garrett

1

在将时间以秒为单位分割以获取天数时要小心,因为这样你不再使用抽象的时间表示,而是使用裸数字,这可能会导致以下问题:

> date_f <- now()
> date_i <- now() - days(23)
> as.duration(date_f - date_i)/ddays(1)
[1] 22.95833
> interval(date_i,date_f)/ddays(1)
[1] 22.95833
> int_length(interval(date_i,date_f))/as.numeric(ddays(1))
[1] 22.95833

这表明,天数或月份是日历中的事件,而不是可以用秒、毫秒等单位测量的时间量。

计算天数差异的最佳方法是避免将其转换为秒,并使用天作为单位进行计算:

> e <- now()
> s <- now() - days(23)  
> as.numeric(as.Date(s))
[1] 18709
> as.numeric(as.Date(e) - as.Date(s))
[1] 23

然而,如果您将一天视为纯粹的86400秒时间跨度,就像ddays()一样,前面的方法可能会导致以下结果:
> e <- ymd_hms("2021-03-13 00:00:10", tz = 'UTC')
> s <- ymd_hms("2021-03-12 23:59:50", tz = 'UTC')
> as.duration(e - s)
[1] "20s"
> as.duration(e - s)/ddays(1)
[1] 0.0002314815
> as.numeric(as.Date(e) - as.Date(s))
[1] 1

因此,这取决于您要寻找什么:时间差异还是日历差异。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接