0

From date 10/31/2018, I want to obtain 09/30/2018.

I tried:

PREV.PROD.DATE<-seq.Date(from=as.Date(chron("10/31/2018")),length=2,by="-1 months")

but it returns:

"2018-10-31" "2018-10-01"

How can I obtain 09/30 instead of 10/01?

Notes: I would like to avoid to use an external package, and I would like the solution to work for any end of month date.

3 Answers3

3

The integer units of Date are days, so you can do:

seq.Date(from=as.Date("2018-10-31"), length=2, by="-1 months") - c(0,1)
# [1] "2018-10-31" "2018-09-30"

If you want arbitrary prior-last-date:

(d <- as.POSIXlt(Sys.Date()))
# [1] "2018-11-08 UTC"
d$mday <- 1L
as.Date(d) - 1
# [1] "2018-10-31"

Replace Sys.Date() with whatever single date you have. If you want to vectorize this:

(ds <- Sys.Date() + c(5, 20, 50))
# [1] "2018-11-13" "2018-11-28" "2018-12-28"
lapply(as.POSIXlt(ds), function(a) as.Date(`$<-`(a, "mday", 1L)) - 1L)
# [[1]]
# [1] "2018-10-31"
# [[2]]
# [1] "2018-10-31"
# [[3]]
# [1] "2018-11-30"
r2evans
  • 141,215
  • 6
  • 77
  • 149
  • Thanks. The problem is that it has to work for any end of month date... – BladeVsResiduals Nov 08 '18 at 17:32
  • 1
    Wow, that was a quick downvote. BladeVsResiduals, I don't know precisely what you mean. My code does precisely what you posed in your question, can you fix your question to better reflect what you mean here? – r2evans Nov 08 '18 at 17:33
  • If you're referring to `lubridate::mday`, OP stated their preference against external packages. – r2evans Nov 08 '18 at 18:13
  • 1
    @r2evans This is a cool answer with `function(a) as.Date(`$<-`(a, "mday", 1L)) - 1L` without any external packages, Learn it! – Jiaxiang Nov 09 '18 at 16:44
1

I like to floor the date - making it the first day of its month, and then subtract 1 to make it the last day of the previous month:

x = as.Date("2018-10-31")
library(lubridate)
floor_date(x, unit = "months") - 1
# [1] "2018-09-30"

Here's a version without using other packages:

as.Date(format(x, "%Y-%m-01")) - 1
# [1] "2018-09-30"
Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
  • Thanks. I would like to avoid to load a new package, because this code line is to be integrated in a big project. – BladeVsResiduals Nov 08 '18 at 17:35
  • 1
    Perfectly understandable. Don't take new dependencies lightly! I'd recommend editing your question to be more specific about your requirements. – Gregor Thomas Nov 08 '18 at 17:52
0

I don't use function chron. But I think the function duration from lubridate helps you. You don't need use floor_date to confuse you.

library(lubridate)
#> 
#> 载入程辑包:'lubridate'
#> The following object is masked from 'package:base':
#> 
#>     date
library(tidyverse)
better_date <- 
    str_split("10/31/2018",'/') %>% 
    .[[1]] %>% 
    .[c(3,1,2)] %>% 
    str_flatten(collapse = '-') %>% 
    as.Date()
(better_date - duration('1 months')) %>% 
    as.Date()
#> [1] "2018-09-30"

Created on 2018-11-09 by the reprex package (v0.2.1)

Jiaxiang
  • 865
  • 12
  • 23
  • That assignment of `better_date` can much more easily be handled with `as.Date(..., format="%m/%d/%Y")`, why go through all of that trouble? It's a bit obfuscated and inefficient (IMO). The gem of this is in the last 2 lines of code ... unfortunately the OP commented in another answer that they don't want non-base packages ... – r2evans Nov 08 '18 at 17:43
  • Yeah ... Just a quick answer, because I am more familiar with tidyverse than the base packages, I think your answer is better a lot! – Jiaxiang Nov 09 '18 at 16:46