Clarinetist
Clarinetist

Reputation: 1197

Last Day of Month Function, given Year and Month

I am unable to find what is specifically giving me this error after stepping through the code multiple times. I'm hoping that someone has seen this error before.

Here's what I think is relevant to the problem:

#### Load packages
library(lubridate)

MONTH <- 1
YEAR <- 2018

# Last day of month
last_day_of_mth <- function(YEAR, MONTH){
  MONTH_BEGIN <- as.POSIXlt(paste0(YEAR, "-", MONTH, "-1"))
  MONTH_END <- MONTH_BEGIN
  day(MONTH_END) <- day(MONTH_BEGIN)
  month(MONTH_END) <- month(MONTH_END) + 1
  day(MONTH_END) <- day(MONTH_END) - 1
  return(as.character(MONTH_END))
}

This code loads a bunch of external files via source() which do not contain any other packages or other functions. They just query data from the Google Analytics API or they load .csv files, manipulate the data frames, and spit out appropriate output as .csv files.

For some bizarre reason, after running these 10 files via source(), it spits out

> last_day_of_mth(2018, 1)
[1] "2018-03-02 06:00:00"

which is obviously not correct - it should be 2018-01-31.

Upvotes: 5

Views: 5778

Answers (3)

David Klotz
David Klotz

Reputation: 2431

Adapting your own code, and continuing to use lubridate:

last_day_of_mth <- function(YEAR, MONTH){
  ymd(paste0(YEAR, "-", MONTH, "-1")) %>% 
    ceiling_date(., "month") %>% 
    {.-days(1)}
}

Upvotes: 1

G. Grothendieck
G. Grothendieck

Reputation: 270428

Here are two solutions. Both work even if year and month are vectors instead of the scalars shown here.

1) as.Date.yearmon First convert the input to a "yearmon" object and then convert that to "Date" class using as.Date with the frac=1 argument.

library(zoo)

# input    
year <- 2018
month <- 2

ym <- as.yearmon(paste(year, month, sep = "-"))
as.Date(ym, frac = 1)
## [1] [1] "2018-02-28"

2) base A base solution using fom (first of month) from Find out the number of days of a month in R is the following. First we convert the year and month to a "Date" class object dat1 for the first of that month and add 32 which is enough days to get to the next month. Then use fom to find the first of that month and subtract 1 to get the last date of the input year/month.

fom <- function(x) as.Date(cut(x, "month"))

dat1 <- as.Date(paste(year, month, 1, sep = "-"))
fom(dat1 + 32) - 1
## [1] "2018-02-28"

Upvotes: 6

MKR
MKR

Reputation: 20095

Another option could be using Hmisc library. One can find out maximum number of days in a month after using year and month with day=1 to make a date. Then find out max number of days in that month.

Finally use year, month and max number of days in month to make last date in that month.

I have created a function as lastDayofMonth

library(Hmisc)
lastDayofMonth <- function(year, month){
  as.Date(sprintf("%4d-%2d-%2d", year, month,
           monthDays(as.Date(sprintf("%4d-%2d-%2d", year, month, 1)))
         ))
}

> lastDayofMonth(2018,2)
#[1] "2018-02-28"
> lastDayofMonth(2018,3)
#[1] "2018-03-31"
> lastDayofMonth(2017,3)
#[1] "2017-03-31"
> lastDayofMonth(2000,2)
#[1] "2000-02-29"

Upvotes: 0

Related Questions