用几周、几个月、几个季度和几年来区分日期。

时间:2022-08-03 21:31:53

I have two dates let´s say 14.01.2013 and 26.03.2014.

我有两个日期让´s 14.01.2013和26.03.2014说。

I would like to get the difference between those two dates in terms of weeks(?), months(in the example 14), quarters(4) and years(1).

我想从周数(?)、月数(在示例14中)、季度数(4)和年数(1)中得到这两个日期之间的区别。

Do you know the best way to get this?

你知道最好的办法吗?

7 个解决方案

#1


60  

what about this:

这个:

# 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() and as.yearqtr() are in package zoo. year() is in package lubridate. What do you think?

yearmon()和as.yearqtr()在package zoo中。年()在包装润滑剂中。你怎么认为?

#2


29  

All the existing answers are imperfect (IMO) and either make assumptions about the desired output or don't provide flexibility for the desired output.

所有现有的答案都是不完美的(IMO),要么对期望的输出做出假设,要么不为期望的输出提供灵活性。

Based on the examples from the OP, and the OP's stated expected answers, I think these are the answers you are looking for (plus some additional examples that make it easy to extrapolate).

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

(This only requires base R and doesn't require zoo or lubridate)

(这只需要基数R,不需要动物园或润滑剂)

Convert to Datetime Objects

将Datetime对象

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

Difference in Days

不同的天

You can use the diff in days to get some of our later answers

你可以在几天内用diff得到我们后面的一些答案

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

Difference in Weeks

不同的周

Difference in weeks is a special case of units = "weeks" in difftime()

周差是困难时间单位=“周”的特殊情况

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

Note that this is the same as dividing our diff_in_days by 7 (7 days in a week)

请注意,这与将diff_in_days除以7(一周7天)是一样的

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

Difference in Years

不同年

With similar logic, we can derive years from diff_in_days

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

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

You seem to be expecting the diff in years to be "1", so I assume you just want to count absolute calendar years or something, which you can easily do by using floor()

您似乎期望年差值为“1”,因此我假设您只想计算绝对日历年或其他内容,您可以通过使用floor()轻松实现这一点

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

Difference in Quarters

不同季度

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

Difference in Months

差几个月

Can calculate this as a conversion from diff_years

可以把它计算为从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

I know this question is old, but given that I still had to solve this problem just now, I thought I would add my answers. Hope it helps.

我知道这个问题已经过时了,但考虑到我现在还得解决这个问题,我想我应该加上我的答案。希望它可以帮助。

#3


12  

For weeks, you can use 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

But difftime doesn't work with duration over weeks.
The following is a very suboptimal solution using cut.POSIXt for those durations but you can work around it:

但是扩散时间不会持续几个星期。下面是一个使用cut的次优解。积极的态度,但你可以围绕它:

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

This is however the number of months, quarters or years spanned by your time interval and not the duration of your time interval expressed in months, quarters, years (since those do not have a constant duration). Considering the comment you made on @SvenHohenstein answer I would think you can use nlevels(cut(seq1,"months")) - 1 for what you're trying to achieve.

然而,这是由时间间隔跨度的月数、季度数或年数,而不是以月、季度、年为单位表示的时间间隔的持续时间(因为这些时间间隔没有固定的持续时间)。考虑到您对@SvenHohenstein的回答所作的评论,我认为您可以使用nlevels(cut(seq1,“months”))- 1来实现您的目标。

#4


11  

I just wrote this for another question, then stumbled here.

我只是写了一个问题,然后在这里被绊倒了。

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)
}

Usage examples:

使用例子:

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

#5


3  

Here's a solution:

这里有一个解决方案:

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

#6


0  

try this for a months solution

尝试一个月的解决方案

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

#7


0  

A more "precise" calculation. That is, 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

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

explanation inline with code

解释内联代码

#' 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

Please let me know if you find more boundary cases where the above solution does not work.

如果你发现更多的边界情况,上面的解不能工作,请告诉我。

#1


60  

what about this:

这个:

# 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() and as.yearqtr() are in package zoo. year() is in package lubridate. What do you think?

yearmon()和as.yearqtr()在package zoo中。年()在包装润滑剂中。你怎么认为?

#2


29  

All the existing answers are imperfect (IMO) and either make assumptions about the desired output or don't provide flexibility for the desired output.

所有现有的答案都是不完美的(IMO),要么对期望的输出做出假设,要么不为期望的输出提供灵活性。

Based on the examples from the OP, and the OP's stated expected answers, I think these are the answers you are looking for (plus some additional examples that make it easy to extrapolate).

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

(This only requires base R and doesn't require zoo or lubridate)

(这只需要基数R,不需要动物园或润滑剂)

Convert to Datetime Objects

将Datetime对象

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

Difference in Days

不同的天

You can use the diff in days to get some of our later answers

你可以在几天内用diff得到我们后面的一些答案

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

Difference in Weeks

不同的周

Difference in weeks is a special case of units = "weeks" in difftime()

周差是困难时间单位=“周”的特殊情况

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

Note that this is the same as dividing our diff_in_days by 7 (7 days in a week)

请注意,这与将diff_in_days除以7(一周7天)是一样的

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

Difference in Years

不同年

With similar logic, we can derive years from diff_in_days

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

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

You seem to be expecting the diff in years to be "1", so I assume you just want to count absolute calendar years or something, which you can easily do by using floor()

您似乎期望年差值为“1”,因此我假设您只想计算绝对日历年或其他内容,您可以通过使用floor()轻松实现这一点

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

Difference in Quarters

不同季度

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

Difference in Months

差几个月

Can calculate this as a conversion from diff_years

可以把它计算为从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

I know this question is old, but given that I still had to solve this problem just now, I thought I would add my answers. Hope it helps.

我知道这个问题已经过时了,但考虑到我现在还得解决这个问题,我想我应该加上我的答案。希望它可以帮助。

#3


12  

For weeks, you can use 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

But difftime doesn't work with duration over weeks.
The following is a very suboptimal solution using cut.POSIXt for those durations but you can work around it:

但是扩散时间不会持续几个星期。下面是一个使用cut的次优解。积极的态度,但你可以围绕它:

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

This is however the number of months, quarters or years spanned by your time interval and not the duration of your time interval expressed in months, quarters, years (since those do not have a constant duration). Considering the comment you made on @SvenHohenstein answer I would think you can use nlevels(cut(seq1,"months")) - 1 for what you're trying to achieve.

然而,这是由时间间隔跨度的月数、季度数或年数,而不是以月、季度、年为单位表示的时间间隔的持续时间(因为这些时间间隔没有固定的持续时间)。考虑到您对@SvenHohenstein的回答所作的评论,我认为您可以使用nlevels(cut(seq1,“months”))- 1来实现您的目标。

#4


11  

I just wrote this for another question, then stumbled here.

我只是写了一个问题,然后在这里被绊倒了。

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)
}

Usage examples:

使用例子:

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

#5


3  

Here's a solution:

这里有一个解决方案:

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

#6


0  

try this for a months solution

尝试一个月的解决方案

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

#7


0  

A more "precise" calculation. That is, 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

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

explanation inline with code

解释内联代码

#' 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

Please let me know if you find more boundary cases where the above solution does not work.

如果你发现更多的边界情况,上面的解不能工作,请告诉我。