How to subtract months from a date in R?

R

R Problem Overview


I'm trying to subtract n months from a date as follows:

maturity <- as.Date("2012/12/31")

m <- as.POSIXlt(maturity)

m$mon <- m$mon - 6

but the resulting date is 01-Jul-2012, and not 30-Jun-2012, as I should expect. Is there any short way to get such result?

Thanks in advance

R Solutions


Solution 1 - R

  1. seq.Date. Note that June has only 30 days so it cannot give June 31st thus instead it gives July 1st.

    seq(as.Date("2012/12/31"), length = 2, by = "-6 months")[2]

    [1] "2012-07-01"

If we knew it was at month end we could do this:

seq(as.Date(cut(as.Date("2012/12/31"), "month")), length=2, by="-5 month")[2]-1
## "2012-06-30"

2) yearmon. Also if we knew it was month end then we could use the "yearmon" class of the zoo package like this:

library(zoo)
as.Date(as.yearmon(as.Date("2012/12/31")) -.5, frac = 1)
## [1] "2012-06-30"

This converts the date to "yearmon" subtracts 6 months (.5 of a year) and then converts it back to "Date" using frac=1 which means the end of the month (frac=0 would mean the beginning of the month). This also has the advantage over the previous solution that it is vectorized automatically, i.e. as.Date(...) could have been a vector of dates.

Note that if "Date" class is only being used as a way of representing months then we can get rid of it altogether and directly use "yearmon" since that models what we want in the first place:

as.yearmon("2012-12") - .5
## [1] "Jun 2012"

3) mondate. A third solution is the mondate package which has the advantage here that it returns the end of the month 6 months ago without having to know that we are month end:

library(mondate)
mondate("2011/12/31") - 6
## mondate: timeunits="months"
## [1] 2011/06/30

This is also vectorized.

  1. lubridate. This lubridate answer has been changed in line with changes in the package:

    library(lubridate) as.Date("2012/12/31") %m-% months(6)

    [1] "2012-06-30"

lubridate is also vectorized.

  1. sqldf/SQLite

    library(sqldf) sqldf("select date('2012-12-31', '-6 months') as date")

    date

    1 2012-07-01

or if we knew we were at month end:

sqldf("select date('2012-12-31', '+1 day', '-6 months', '-1 day') as date")
##         date
## 1 2012-06-30

Solution 2 - R

you can use lubridate package for this

library(lubridate)
maturity <- maturity %m-% months(6)

there is no reason for changing the day field.

you can set your day field back to the last day in that month by

day(maturity) <- days_in_month(maturity)

Solution 3 - R

lubridate works correctly with such calculations:

library(lubridate)
as.Date("2000-01-01") - days(1)    # 1999-12-31
as.Date("2000-03-31") - months(1)  # 2000-02-29

but sometimes fails:

as.Date("2000-02-29") - years(1)   # NA, should be 1999-02-28

Solution 4 - R

Technically you cannot add/subtract 1 month to all dates (although you can add/subtract 30 days to all dates, but I suppose, that's not something you want). I think this is what you are looking for

> lubridate::ceiling_date(as.Date("2020-01-31"), unit = "month")
[1] "2020-02-01"
> lubridate::floor_date(as.Date("2020-01-31"), unit = "month")
[1] "2020-01-01"

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser648905View Question on Stackoverflow
Solution 1 - RG. GrothendieckView Answer on Stackoverflow
Solution 2 - RAneesh Cherian KView Answer on Stackoverflow
Solution 3 - RJ. DoeView Answer on Stackoverflow
Solution 4 - RqedView Answer on Stackoverflow