获取星期,月份,季度和年份之间的日期差


77

我有两个约会,分别是14.01.201326.03.2014

我想得到这两个日期之间的区别,分别是星期(?),月份(在示例14中),季度(4)和年份(1)。

您知道获得此信息的最佳方法吗?


在这几周中,我发现了以下difftime(time1,time2,units =“ weeks”)。不幸的是,这几个月,几个季度,几年都无法正常工作。
ddg

Answers:


76

那这个呢:

# get difference between dates `"01.12.2013"` and `"31.12.2013"`

# weeks
difftime(strptime("26.03.2014", format = "%d.%m.%Y"),
strptime("14.01.2013", format = "%d.%m.%Y"),units="weeks")
Time difference of 62.28571 weeks

# months
(as.yearmon(strptime("26.03.2014", format = "%d.%m.%Y"))-
as.yearmon(strptime("14.01.2013", format = "%d.%m.%Y")))*12
[1] 14

# quarters
(as.yearqtr(strptime("26.03.2014", format = "%d.%m.%Y"))-
as.yearqtr(strptime("14.01.2013", format = "%d.%m.%Y")))*4
[1] 4

# years
year(strptime("26.03.2014", format = "%d.%m.%Y"))-
year(strptime("14.01.2013", format = "%d.%m.%Y"))
[1] 1

as.yearmon()并且as.yearqtr()在包装中zooyear()在包装中lubridate。你怎么看?


12
此答案要求谨慎...它将认为2013年12月31日与第二天(2014年1月1日)相差1年。
格里戈尔·托马斯

1
扩大Gregor的警告:仅year会在日历年中产生差异,因此,如果您需要知道一年中的某个差异,就不适合。
Umaomamaomao

'format'+默认值可以使其更容易键入:difftime(format(“ 2014-03-26”),format(“ 2013-01-14”),units =“ weeks”)时差62.28571周
tim

51

现有的所有答案都是不完善的(IMO),它们要么对所需的输出进行假设,要么不为所需的输出提供灵活性。

根据OP中的示例以及OP规定的预期答案,我认为这些是您正在寻找的答案(加上一些易于推断的其他示例)。

(这仅需要基数R,而无需使用Zoo或lubridate)

转换为日期时间对象

date_strings = c("14.01.2013", "26.03.2014")
datetimes = strptime(date_strings, format = "%d.%m.%Y") # convert to datetime objects

天数差异

您可以在几天之内使用diff来获取我们以后的一些答案

diff_in_days = difftime(datetimes[2], datetimes[1], units = "days") # days
diff_in_days
#Time difference of 435.9583 days

周差

差异周是一个特殊的情况下,units = "weeks"difftime()

diff_in_weeks = difftime(datetimes[2], datetimes[1], units = "weeks") # weeks
diff_in_weeks
#Time difference of 62.27976 weeks

请注意,这与将diff_in_days除以7(一周7天)相同

as.double(diff_in_days)/7
#[1] 62.27976

年差异

按照类似的逻辑,我们可以从diff_in_days中得出年份

diff_in_years = as.double(diff_in_days)/365 # absolute years
diff_in_years
#[1] 1.194406

您似乎期望年份的差异为“ 1”,所以我假设您只想计算绝对日历年或类似的年份,您可以使用轻松地做到这一点 floor()

# get desired output, given your definition of 'years'
floor(diff_in_years)
#[1] 1

季度差异

# get desired output for quarters, given your definition of 'quarters'
floor(diff_in_years * 4)
#[1] 4

月份差异

可以将其计算为diff_years的转换

# months, defined as absolute calendar months (this might be what you want, given your question details)
months_diff = diff_in_years*12
floor(month_diff)
#[1] 14

我知道这个问题很旧,但是鉴于我现在仍然必须解决这个问题,我想我会补充我的答案。希望能帮助到你。


我觉得这是不行的,当months_diff是<0
timat

@timat您能举两个不适合您的日期字符串的特定示例吗?
rysqui '16

1
date_strings = c("14.07.2014", "10.03.2015")-4代替7个月根据第一定义..
timat

@timat你是对的!我不确定为什么当我写这篇文章时,我不仅直接从中计算了几个月diff_in_years,例如,在您的示例中,真正的答案是已经过去了将近8个月。仅在diff_in_years*12 = 7.857534我更正我的答案后,您才能得到正确的答案-谢谢。
rysqui

2
请记住,将天数除以365年后,由于to年,它仅适用于4年中的3年。除法365.25将更精确,尤其是对于计算年龄。
MS Berends

14

在数周内,您可以使用function difftime

date1 <- strptime("14.01.2013", format="%d.%m.%Y")
date2 <- strptime("26.03.2014", format="%d.%m.%Y")
difftime(date2,date1,units="weeks")
Time difference of 62.28571 weeks

difftime不适用于持续数周的时间。
以下是在cut.POSIXt这些期间使用的非常不理想的解决方案,但是您可以解决该问题:

seq1 <- seq(date1,date2, by="days")
nlevels(cut(seq1,"months"))
15
nlevels(cut(seq1,"quarters"))
5
nlevels(cut(seq1,"years"))
2

但是,这是时间间隔跨度的月,季度或年数,而不是时间间隔的持续时间,以月,季度,年表示(因为它们的持续时间不固定)。考虑到您对@SvenHohenstein答案的评论,我认为您可以将其nlevels(cut(seq1,"months")) - 1用于尝试实现的目标。


14

我只是为另一个问题写的,然后在这里偶然发现。

library(lubridate)

#' Calculate age
#' 
#' By default, calculates the typical "age in years", with a
#' \code{floor} applied so that you are, e.g., 5 years old from
#' 5th birthday through the day before your 6th birthday. Set
#' \code{floor = FALSE} to return decimal ages, and change \code{units}
#' for units other than years.
#' @param dob date-of-birth, the day to start calculating age.
#' @param age.day the date on which age is to be calculated.
#' @param units unit to measure age in. Defaults to \code{"years"}. Passed to \link{\code{duration}}.
#' @param floor boolean for whether or not to floor the result. Defaults to \code{TRUE}.
#' @return Age in \code{units}. Will be an integer if \code{floor = TRUE}.
#' @examples
#' my.dob <- as.Date('1983-10-20')
#' age(my.dob)
#' age(my.dob, units = "minutes")
#' age(my.dob, floor = FALSE)
age <- function(dob, age.day = today(), units = "years", floor = TRUE) {
    calc.age = interval(dob, age.day) / duration(num = 1, units = units)
    if (floor) return(as.integer(floor(calc.age)))
    return(calc.age)
}

用法示例:

my.dob <- as.Date('1983-10-20')

age(my.dob)
# [1] 31

age(my.dob, floor = FALSE)
# [1] 31.15616

age(my.dob, units = "minutes")
# [1] 16375680

age(seq(my.dob, length.out = 6, by = "years"))
# [1] 31 30 29 28 27 26

'new_interval'已过时;使用“间隔”代替。在版本“ 1.5.0”中已弃用。
马诺·库玛

我只是为另一个问题写的,然后在这里偶然发现。我也是!小建议:在if语句后使用大括号:if (floor) { ... }并且仅return在函数返回一半时使用。最后一行应该是calc.age
MS Berends

@MSBerends这些只是样式准则。我更喜欢显式地return使用我的功能-我发现它更清晰。当然,在您自己的代码中,请使用适合您的样式。
格雷戈尔·托马斯

非常真实 关于功能:在这种情况下,它不能正常工作:1950-01-172015-01-01。它会返回65,但此人在2015年1月17日之前不会满65岁...知道为什么吗?
MS Berends

真奇怪!我会进一步研究。问题似乎是2013年,如果您定义yy = seq.Date(from = as.Date("2010-01-01"), to = as.Date("2015-01-01"), by = "year")然后尝试age(dob = as.Date("1950-01-17"), age.day = yy),结果将超过62。并且只有DOB在1949年至1952年之间。非常奇怪...
Gregor Thomas

5

这是一个解决方案:

dates <- c("14.01.2013", "26.03.2014")

# Date format:
dates2 <- strptime(dates, format = "%d.%m.%Y")

dif <- diff(as.numeric(dates2)) # difference in seconds

dif/(60 * 60 * 24 * 7) # weeks
[1] 62.28571
dif/(60 * 60 * 24 * 30) # months
[1] 14.53333
dif/(60 * 60 * 24 * 30 * 3) # quartes
[1] 4.844444
dif/(60 * 60 * 24 * 365) # years
[1] 1.194521

谢谢您,但是您的解决方案并非在所有情况下都有效。例如,如果您输入的日期<-c(“ 01.12.2013”​​,“ 31.12.2013”​​),则月份的差异为1,而我希望差异为0(两个日期均发生在12月13日)。
ddg

3
虽然仍然NOG准确,我建议在一年内使用365.242的天量,而不是365
CousinCocaine

4

日期是这里的大话题,给出的答案也很棒。这里仍然是出色的lubridate答案(尽管@Gregor的功能正在使用此软件包)

lubridate时间跨度文档对于理解时间和持续时间之间的区别非常有帮助。我也喜欢lubridate速查表这个非常有用的线程

library(lubridate)

dates <- c(dmy('14.01.2013'),dmy('26.03.2014'))

span <- dates[1] %--% dates[2] #creating an interval object

#creating period objects 
as.period(span, unit = 'year') 
#> [1] "1y 2m 12d 0H 0M 0S"
as.period(span, unit = 'month')
#> [1] "14m 12d 0H 0M 0S"
as.period(span, unit = 'day')
#> [1] "436d 0H 0M 0S"

期间不接受以周为单位。但是您可以将持续时间转换为几周:

as.duration(span)/ dweeks(1)
#makes duration object (in seconds) and divides by duration of a week (in seconds)
#> [1] 62.28571

reprex软件包(v0.3.0)创建于2019-11-04


1

试试这个几个月的解决方案

StartDate <- strptime("14 January 2013", "%d %B %Y") 
EventDates <- strptime(c("26 March 2014"), "%d %B %Y") 
difftime(EventDates, StartDate) 

嗨,雷切尔,非常感谢,但是这不起作用。当我运行strptime(“ 2013年1月14日”,“%d%B%Y”)时,得到NA。
ddg

同样在这里。如果我使用此步骤,
则会

此解决方案仅适用于英语语言环境。使用%m数字月份(例如1月份为1)代替使用更安全%B
Frank Schmitt 2015年

1

更精确的计算。也就是说,不完整的周/月/季度/年的周/月/季度/年的数量是该周/月/季度/年的日历天的分数。例如,2016-02-22和2016-03-31之间的月份数是8/29 + 31/31 = 1.27586

内联代码说明

#' Calculate precise number of periods between 2 dates
#' 
#' @details The number of week/month/quarter/year for a non-complete week/month/quarter/year 
#'     is the fraction of calendar days in that week/month/quarter/year. 
#'     For example, the number of months between 2016-02-22 and 2016-03-31 
#'     is 8/29 + 31/31 = 1.27586
#' 
#' @param startdate start Date of the interval
#' @param enddate end Date of the interval
#' @param period character. It must be one of 'day', 'week', 'month', 'quarter' and 'year'
#' 
#' @examples 
#' identical(numPeriods(as.Date("2016-02-15"), as.Date("2016-03-31"), "month"), 15/29 + 1)
#' identical(numPeriods(as.Date("2016-02-15"), as.Date("2016-03-31"), "quarter"), (15 + 31)/(31 + 29 + 31))
#' identical(numPeriods(as.Date("2016-02-15"), as.Date("2016-03-31"), "year"), (15 + 31)/366)
#' 
#' @return exact number of periods between
#' 
numPeriods <- function(startdate, enddate, period) {

    numdays <- as.numeric(enddate - startdate) + 1
    if (grepl("day", period, ignore.case=TRUE)) {
        return(numdays)

    } else if (grepl("week", period, ignore.case=TRUE)) {
        return(numdays / 7)
    }

    #create a sequence of dates between start and end dates
    effDaysinBins <- cut(seq(startdate, enddate, by="1 day"), period)

    #use the earliest start date of the previous bins and create a breaks of periodic dates with
    #user's period interval
    intervals <- seq(from=as.Date(min(levels(effDaysinBins)), "%Y-%m-%d"), 
        by=paste("1",period), 
        length.out=length(levels(effDaysinBins))+1)

    #create a sequence of dates between the earliest interval date and last date of the interval
    #that contains the enddate
    allDays <- seq(from=intervals[1],
        to=intervals[intervals > enddate][1] - 1,
        by="1 day")

    #bin all days in the whole period using previous breaks
    allDaysInBins <- cut(allDays, intervals)

    #calculate ratio of effective days to all days in whole period
    sum( tabulate(effDaysinBins) / tabulate(allDaysInBins) )
} #numPeriods

如果您发现上述解决方案无法解决的更多边界情况,请告诉我。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.