Arithmetic with months(9) resulting in NA

76 views Asked by At

I have come across some unusual behavior calculating dates in R.

I am trying to estimate date of conception by back calculating 9 months from date of birth, where date of birth is in the format as_date("1946-03-31".

So, the calculation would look like

as_date("1946-03-31")- months(9)

However, instead of getting a date, I get NA.

Strangely, if I change the month number, I get a perfect result:

as_date("1946-03-31")- months(8)

or

as_date("1946-03-31")- months(10)

Both work.

I also get a perfect date if I use days

as_date("1946-03-31")- days(273)

Is there a totally obvious reason for this that I'm missing? If not, is this replicable across other setups?

2

There are 2 answers

4
Gregor Thomas On

Months are poorly defined. What is 1 month before March 30? It's not well-defined because February has fewer days than March.

What answer do you expect for as_date("1946-03-31") - months(9)? Is it the day after as_date("1946-03-30") - months(9), which would be 1945-07-01? Or is it the day before as_date("1946-04-01") - months(9), which would be 1945-06-30? Both would be somewhat reasonable, but they are different because June has 1 day less than March.

"9 months" as a human gestation period is also a really rough estimate. When I google "gestation period for humans" I get 280 days - I'd suggest using that instead. Days are nice and consistent. (Or look for a better estimate wikipedia has a nice page. Things will depend on whether you are trying to estimate the fertilization date or the date of last menstruation.)

0
zephryl On

As nicely described by @GregorThomas and @RitchieSacramento, arithmetic with months is often ambiguous since they have inconsistent numbers of days. One approach is provided by lubridate’s %m+% and %m-% operators. As described in the docs:

Date %m+% months(n) always returns a date in the nth month after Date. If the new date would usually spill over into the n + 1th month, %m+% will return the last day of the nth month (rollback()). Date %m-% months(n) always returns a date in the nth month before Date.

With your example:

library(lubridate)

as_date("1946-03-31") %m-% months(9)
# "1945-06-30"