Add a month to a Date [duplicate]
Asked Answered
S

8

74

I am trying to add a month to a date i have. But then its not possible in a straight manner so far. Following is what i tried.

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

Adding wont help as the month wont be overlapped.

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

Above might work , but again its not straight forward. Also its also adding 30 days or something to the date which has issues like the below

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"

In the above , for the first 2 dates , month haven’t changed.

Also the following approach also failed for month but was success for year

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

Error in format.POSIXlt(x, usetz = TRUE) : invalid 'x' argument

What is the right method to do this ?

Southeastwardly answered 5/1, 2013 at 7:20 Comment(0)
C
48

Vanilla R has a naive difftime class, but the Lubridate CRAN package lets you do what you ask:

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

Hope that helps.

Conveyor answered 5/1, 2013 at 7:38 Comment(4)
But this doesn't always work: d <- as.Date("2004-01-31") returns NA. This answer below gives the expected answer for that situation.Ephemerality
> d <- as.Date("2004-01-31") > d # Huh? [1] "2004-01-31"Conveyor
Sorry, I wasn't clear. If you replace the date in your second line with 2004-01-31 and then run the rest of your code, you'll get the NA. In that case, when you increment the month it tries to set it to 2004-02-31, which returns an NA since it's not a valid date. By the time you set day(d) in the next line of code, d is already NA.Ephemerality
(-1) As noted by @MattParker, this solution does not work for cases such as 2004-01-31, while the one provided by Wojciech Sobala does work properly and is the recommended approach in lubridate.Sped
R
166

Function %m+% from lubridate adds one month without exceeding last day of the new month.

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"
Razzia answered 6/1, 2013 at 12:53 Comment(4)
This should be the accepted answer as it correctly avoids exceeding last day of the new month. +1Vitrescence
This doesn't work when you put february as an input, then it changes following months to last date as in februarySharolynsharon
@BruceWayne do you have a reprex? ymd("2011-02-20") %m+% months(1) seems to work fine for me.Cutwater
@BruceWayne that only happens if you choose a day that doesn't exist in the final month. For instance, ymd("2019-01-31") %m+% months(1) gives 2019-02-28. If the year were a leap year, then the day would be the 29th. This is the correct behavior. If you want + 30 days, just add 30 days using the normal + operator.Bibliographer
S
64

It is ambiguous when you say "add a month to a date".

Do you mean

  1. add 30 days?
  2. increase the month part of the date by 1?

In both cases a whole package for a simple addition seems a bit exaggerated.

For the first point, of course, the simple + operator will do:

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

As for the second I would just create a one line function as simple as that (and with a more general scope):

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

You can use it with arbitrary months, including negative:

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

Of course, if you want to add only and often a single month:

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

If you add one month to 31 of January, since 31th February is meaningless, the best to get the job done is to add the missing 3 days to the following month, March. So correctly:

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

In case, for some very special reason, you need to put a ceiling to the last available day of the month, it's a bit longer:

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

As usual you could add a single month version:

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

So:

  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"

And with decrements:

  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"

Besides you didn't tell if you were interested to a scalar or vector solution. As for the latter:

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

Note: *apply family destroys the class data, that's why it has to be rebuilt. The vector version brings:

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

Hope you liked it))

Scrub answered 9/3, 2013 at 23:29 Comment(2)
IMO its worth to use the package if you have to think through so many things just to add a month and have peace of mind that experts have thought through itBracci
@AshishSinghal. To add 3 months to date d with lubridate: ymd(d) %m+% months(3); with the oneliner above simply: add.months(d, 3). So, in the first instance, you have to learn three functions ymd, %m+%, months(), how to glue their syntax, and the latter predates the standard R months(). Of course, for complex date manipulations, the complexity of lubridate makes sense.Scrub
C
48

Vanilla R has a naive difftime class, but the Lubridate CRAN package lets you do what you ask:

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

Hope that helps.

Conveyor answered 5/1, 2013 at 7:38 Comment(4)
But this doesn't always work: d <- as.Date("2004-01-31") returns NA. This answer below gives the expected answer for that situation.Ephemerality
> d <- as.Date("2004-01-31") > d # Huh? [1] "2004-01-31"Conveyor
Sorry, I wasn't clear. If you replace the date in your second line with 2004-01-31 and then run the rest of your code, you'll get the NA. In that case, when you increment the month it tries to set it to 2004-02-31, which returns an NA since it's not a valid date. By the time you set day(d) in the next line of code, d is already NA.Ephemerality
(-1) As noted by @MattParker, this solution does not work for cases such as 2004-01-31, while the one provided by Wojciech Sobala does work properly and is the recommended approach in lubridate.Sped
G
11

The simplest way is to convert Date to POSIXlt format. Then perform the arithmetic operation as follows:

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

Moreover, incase you want to deal with Date columns in data.table, unfortunately, POSIXlt format is not supported.

Still you can perform the add month using basic R codes as follows:

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

Hope it helps.

Golda answered 29/8, 2017 at 3:36 Comment(2)
Thanks! This works also for dates with 29 of February. This is not the case for lubridate.Lynnette
Yes it's based on Georgian calendar by default and the leap year is calculated for you...(but its not a complex algorithm anyway when compare to Chinese)Golda
X
8

"mondate" is somewhat similar to "Date" except that adding n adds n months rather than n days. (as.Date.mondate can be used to convert mondate class objects to Date objects.)

library(mondate)

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

as.Date(as.mondate(d) + 1)
## [1] "2004-02-29"
Xenos answered 10/3, 2013 at 1:37 Comment(0)
B
5

Here's a function that doesn't require any packages to be installed. You give it a Date object (or a character that it can convert into a Date), and it adds n months to that date without changing the day of the month (unless the month you land on doesn't have enough days in it, in which case it defaults to the last day of the returned month). Just in case it doesn't make sense reading it, there are some examples below.

Function definition

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

Some examples

Adding months

> 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

Subtracting months

> 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"
Blindstory answered 29/7, 2014 at 22:6 Comment(0)
J
5
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]
Jeminah answered 22/6, 2017 at 4:34 Comment(1)
Using seq was already mentioned in another answer. Please consider adding context here to highlight what sets your answer apart from others'.Foppery
H
1

I turned antonio's thoughts into a specific function:

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"
Helainehelali answered 29/4, 2015 at 22:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.