2013-01-05 121 views
44

我想添加一个月到我有一个日期。但到目前为止它不可能以一种直接的方式进行。以下是我的尝试。添加一个月到一个日期

d <- as.Date("2004-01-31") 
d + 60 
# [1] "2004-03-31" 

添加不会帮助,因为该月不会重叠。

seq(as.Date("2004-01-31"), by = "month", length = 2) 
# [1] "2004-01-31" "2004-03-02" 

上面可能有效,但它又不是直截了当。 此外其还增加了30天,东西其中有像下面

seq(as.Date("2004-01-31"), by = "month", length = 10) 
# [1] "2004-01-31" "2004-03-02" "2004-03-31" "2004-05-01" "2004-05-31" "2004-07-01" "2004-07-31" "2004-08-31" "2004-10-01" "2004-10-31" 

在上面,第2个日期问题之日起,一个月都没有改变。

还有以下方法也未能为一个月,但是为今年成功

d <- as.POSIXlt(as.Date("2010-01-01")) 
d$year <- d$year +1 
d 
# [1] "2011-01-01 UTC" 
d <- as.POSIXlt(as.Date("2010-01-01")) 
d$month <- d$month +1 
d 

错误format.POSIXlt(x, usetz = TRUE):无效的“X”的说法

什么是做这种正确的方法是什么?

回答

22

香草R的天真difftime类,但LubridateCRAN包让你做什么你问:

require(lubridate) 
d <- as.Date('2004-01-01') 
month(d) <- month(d) + 1 
day(d) <- days_in_month(d) 
d 
[1] "2004-02-29" 

希望有所帮助。

+3

但是这并不总是奏效:'d < - as.Date(“2004-01-31”)'返回'NA'。 [下面的答案](http://stackoverflow.com/a/14182326/143319)给出了该情况的预期答案。 –

+0

> d < - as.Date(“2004-01-31”) > d#Huh? [1]“2004-01-31” – hd1

+3

对不起,我不清楚。如果你用'2004-01-31'替换第二行中的日期,然后运行代码的其余部分,就会得到'NA'。在这种情况下,当你增加月份时,它会尝试将其设置为“2004-02-31”,由于该日期不是有效日期,所以会返回“NA”。当你在下一行代码中设置“day(d)”时,'d'已经是'NA'。 –

81

功能%m+%来自lubridate增加一个月不超过新月的最后一天。

library(lubridate) 
(d <- ymd("2012-01-31")) 
1 parsed with %Y-%m-%d 
[1] "2012-01-31 UTC" 
d %m+% months(1) 
[1] "2012-02-29 UTC" 
42

当你说“给一个日期添加一个月”时,这是不明确的。

你的意思

  1. 加30天呢?
  2. 将日期的月份部分增加1?

在这两种情况下,一个简单的加法整个包似乎有点夸张。

对于第一点,当然,简单的+操作就可以了:

d=as.Date('2010-01-01') 
d + 30 
#[1] "2010-01-31" 

至于第二个,我只想创造一个线功能这么简单(与更广泛的范围内) :

add.months= function(date,n) seq(date, by = paste (n, "months"), length = 2)[2] 

您可以任意月,包括负面使用它:

add.months(d, 3) 
#[1] "2010-04-01" 
add.months(d, -3) 
#[1] "2009-10-01" 

当然,如果你想只添加和经常单月:

add.month=function(date) add.months(date,1) 
add.month(d) 
#[1] "2010-02-01" 

如果你一月份加入到1月31日,自2月31日是毫无意义的,把工作做得最好的是添加3月份到下个月失踪3天。所以正确:

add.month(as.Date("2010-01-31")) 
#[1] "2010-03-03" 

在情况下,对于一些非常特殊的原因,你需要把一个上限,以该月的最后一个可用的一天,这是一个有点长:

add.months.ceil=function (date, n){ 

    #no ceiling 
    nC=add.months(date, n) 

    #ceiling 
    day(date)=01 
    C=add.months(date, n+1)-1 

    #use ceiling in case of overlapping 
    if(nC>C) return(C) 
    return(nC) 
} 

像往常一样,你可以添加一个单月的版本:

add.month.ceil=function(date) add.months.ceil(date,1)  

所以:

d=as.Date('2010-01-31') 
    add.month.ceil(d) 
    #[1] "2010-02-28" 
    d=as.Date('2010-01-21') 
    add.month.ceil(d) 
    #[1] "2010-02-21" 

并与递减:

d=as.Date('2010-03-31') 
    add.months.ceil(d, -1) 
    #[1] "2010-02-28" 
    d=as.Date('2010-03-21') 
    add.months.ceil(d, -1) 
    #[1] "2010-02-21" 

除此之外,您还没有告诉我们,如果你有兴趣到一个标量或矢量解决方案。至于后者:

add.months.v= function(date,n) as.Date(sapply(date, add.months, n), origin="1970-01-01") 

注:*apply家庭破坏类的数据,这就是为什么它被重建。 矢量版本带来:

d=c(as.Date('2010/01/01'), as.Date('2010/01/31')) 
add.months.v(d,1) 
[1] "2010-02-01" "2010-03-03" 

希望你喜欢它))

7

"mondate"有点类似于"Date"除了加入n增加n个月而不是n天:

> library(mondate) 
> d <- as.Date("2004-01-31") 
> as.mondate(d) + 1 
mondate: timeunits="months" 
[1] 2004-02-29 
4

这里有一个功能,不需要安装任何软件包。你给它一个Date对象(或character,它可以转换成Date),并把它添加n个月该日期不改变月(当天除非你的土地上一个月没有足够天,在这种情况下,它默认为返回月份的最后一天)。以防万一它读取它没有意义,下面有一些例子。

函数定义

addMonth <- function(date, n = 1){ 
    if (n == 0){return(date)} 
    if (n %% 1 != 0){stop("Input Error: argument 'n' must be an integer.")} 

    # Check to make sure we have a standard Date format 
    if (class(date) == "character"){date = as.Date(date)} 

    # Turn the year, month, and day into numbers so we can play with them 
    y = as.numeric(substr(as.character(date),1,4)) 
    m = as.numeric(substr(as.character(date),6,7)) 
    d = as.numeric(substr(as.character(date),9,10)) 

    # Run through the computation 
    i = 0 
    # Adding months 
    if (n > 0){ 
    while (i < n){ 
     m = m + 1 
     if (m == 13){ 
     m = 1 
     y = y + 1 
     } 
     i = i + 1 
    } 
    } 
    # Subtracting months 
    else if (n < 0){ 
    while (i > n){ 
     m = m - 1 
     if (m == 0){ 
     m = 12 
     y = y - 1 
     } 
     i = i - 1 
    } 
    } 

    # If past 28th day in base month, make adjustments for February 
    if (d > 28 & m == 2){ 
     # If it's a leap year, return the 29th day 
     if ((y %% 4 == 0 & y %% 100 != 0) | y %% 400 == 0){d = 29} 
     # Otherwise, return the 28th day 
     else{d = 28} 
    } 
    # If 31st day in base month but only 30 days in end month, return 30th day 
    else if (d == 31){if (m %in% c(1, 3, 5, 7, 8, 10, 12) == FALSE){d = 30}} 

    # Turn year, month, and day into strings and put them together to make a Date 
    y = as.character(y) 

    # If month is single digit, add a leading 0, otherwise leave it alone 
    if (m < 10){m = paste('0', as.character(m), sep = '')} 
    else{m = as.character(m)} 

    # If day is single digit, add a leading 0, otherwise leave it alone 
    if (d < 10){d = paste('0', as.character(d), sep = '')} 
    else{d = as.character(d)} 

    # Put them together and convert return the result as a Date 
    return(as.Date(paste(y,'-',m,'-',d, sep = ''))) 
} 

一些例子

添加个月

> addMonth('2014-01-31', n = 1) 
[1] "2014-02-28" # February, non-leap year 
> addMonth('2014-01-31', n = 5) 
[1] "2014-06-30" # June only has 30 days, so day of month dropped to 30 
> addMonth('2014-01-31', n = 24) 
[1] "2016-01-31" # Increments years when n is a multiple of 12 
> addMonth('2014-01-31', n = 25) 
[1] "2016-02-29" # February, leap year 

减去个月

> addMonth('2014-01-31', n = -1) 
[1] "2013-12-31" 
> addMonth('2014-01-31', n = -7) 
[1] "2013-06-30" 
> addMonth('2014-01-31', n = -12) 
[1] "2013-01-31" 
> addMonth('2014-01-31', n = -23) 
[1] "2012-02-29" 
0

我转身安东尼的想法变成具体的乐趣ction:

library(DescTools) 

> AddMonths(as.Date('2004-01-01'), 1) 
[1] "2004-02-01" 

> AddMonths(as.Date('2004-01-31'), 1) 
[1] "2004-02-29" 

> AddMonths(as.Date('2004-03-30'), -1) 
[1] "2004-02-29" 
2
addedMonth <- seq(as.Date('2004-01-01'), length=2, by='1 month')[2] 
addedQuarter <- seq(as.Date('2004-01-01'), length=2, by='1 quarter')[2] 
+1

在另一个答案中已经提到使用'seq'。请考虑在此添加上下文以突出显示您的答案与其他人分开的内容。 – BenBarnes

2

最简单的方法是将日期转换为POSIXlt格式。 然后执行运算如下:

date_1m_fwd  <- as.POSIXlt("2010-01-01") 
date_1m_fwd$mon <- date_1m_fwd$mon +1 

此外,柜面要应对data.table日期列,遗憾的是不支持POSIXlt格式。

你仍然可以使用基础研发代码如下执行添加月:

library(data.table) 
dt <- as.data.table(seq(as.Date("2010-01-01"), length.out=5, by="month")) 
dt[,shifted_month:=tail(seq(V1[1], length.out=length(V1)+3, by="month"),length(V1))] 

希望它能帮助。

+0

谢谢!这也适用于2月29日的日期。这种情况并非如此。 – giordano

+0

是的,它默认是基于格鲁吉亚日历,闰年是为你计算的......(但与中国人相比,它不是一个复杂的算法) –