【问题标题】:Add a month to a Date [duplicate]将一个月添加到日期[重复]
【发布时间】:2012-12-19 15:04:30
【问题描述】:

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

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' 参数无效

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

【问题讨论】:

    标签: r date date-arithmetic


    【解决方案1】:

    来自 lubridate 的函数 %m+% 在不超过新月份的最后一天的情况下增加一个月。

    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"
    

    【讨论】:

    • 这应该是公认的答案,因为它正确地避免了超过新月的最后一天。 +1
    • 当您将 2 月作为输入时,这不起作用,然后它会在几个月后更改为 2 月的最后一个日期
    • @BruceWayne 你有代理吗? ymd("2011-02-20") %m+% months(1) 似乎对我来说很好用。
    • @BruceWayne 只有在您选择最后一个月不存在的日子时才会发生这种情况。例如,ymd("2019-01-31") %m+% months(1) 给出2019-02-28。如果这一年是闰年,那么这一天就是 29 日。这是正确的行为。如果您想要 + 30 天,只需使用普通的 + 运算符添加 30 天。
    【解决方案2】:

    当您说“将月份添加到日期”时会模棱两可。

    你是说

    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"
    

    希望你喜欢))

    【讨论】:

    • IMO 值得使用该软件包,如果您必须考虑这么多事情只是为了增加一个月并让专家们深思熟虑后高枕无忧
    • @AshishSinghal。添加 3 个月至今 d 与 lubridate:ymd(d) %m+% months(3);只需使用上面的 oneliner:add.months(d, 3)。所以,首先,你必须学习三个函数ymd%m+%months(),如何粘合它们的语法,而后者早于标准R months()。当然,对于复杂的日期操作,lubridate 的复杂性是有道理的。
    【解决方案3】:

    Vanilla R 有一个幼稚的 difftime 类,但 Lubridate CRAN 包可以让你做你想做的事:

    require(lubridate)
    d <- ymd(as.Date('2004-01-01')) %m+% months(1)
    d
    [1] "2004-02-01"
    

    希望对您有所帮助。

    【讨论】:

    • 但这并不总是有效:d &lt;- as.Date("2004-01-31") 返回NAThis answer below 给出了这种情况的预期答案。
    • > d d # 嗯? [1] "2004-01-31"
    • 对不起,我不清楚。如果您将第二行中的日期替换为2004-01-31,然后运行其余代码,您将获得NA。在这种情况下,当您增加月份时,它会尝试将其设置为2004-02-31,这将返回NA,因为它不是有效日期。当您在下一行代码中设置day(d) 时,d 已经是NA
    • (-1) 正如@MattParker 所指出的,此解决方案不适用于 2004-01-31 等情况,而 Wojciech Sobala 提供的解决方案确实可以正常工作,并且是 lubridate 中推荐的方法.
    【解决方案4】:

    最简单的方法是将 Date 转换为 POSIXlt 格式。 然后进行如下算术运算:

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

    此外,如果您想处理 data.table 中的 Date 列,不幸的是,不支持 POSIXlt 格式。

    您仍然可以使用如下基本 R 代码执行添加月份:

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

    希望对你有帮助。

    【讨论】:

    • 谢谢!这也适用于 2 月 29 日的日期。这不是 lubridate 的情况。
    • 是的,它默认基于格鲁吉亚历,闰年是为你计算的......(但与中文相比,它不是一个复杂的算法)
    【解决方案5】:

    "mondate""Date" 有点相似,只是添加n 添加n 月而不是n 天:

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

    【讨论】:

      【解决方案6】:

      这是一个不需要安装任何软件包的功能。你给它一个Date 对象(或一个character,它可以转换成一个Date),它会在那个日期不改变月份除非您登陆的月份没有足够的天数,在这种情况下,它默认为返回月份的最后一天)。以防万一阅读没有意义,下面有一些示例。

      函数定义

      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"
      

      【讨论】:

        【解决方案7】:
        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]
        

        【讨论】:

        • Using seq 已经在另一个答案中提到过。请考虑在此处添加上下文,以突出您的答案与其他人的不同之处。
        【解决方案8】:

        我把安东尼奥的想法变成了一个具体的功能:

        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"
        

        【讨论】:

          猜你喜欢
          • 2013-07-14
          • 1970-01-01
          • 2012-11-15
          • 1970-01-01
          • 2012-09-29
          • 1970-01-01
          • 2014-09-08
          • 2017-01-08
          • 2012-05-06
          相关资源
          最近更新 更多