Find out the number of days of a month in R
Mia Lopez
I have a date in P
date = as.Date("2011-02-23", "%Y-%m-%d")Is it possible to find out the number of days of the month of that particular date? (With respect to leapyears). In PHP it would look similar to this ():
days = format(date, "%t")but "%t" seems to have a different meaning in R. Is there a solution for this problem?
015 Answers
lubridate package has required function "days_in_month" with respect to leapyears.
Your example:
date <- as.Date("2011-02-23", "%Y-%m-%d") lubridate::days_in_month(date) # Feb # 28Leap year (2016):
date_leap <- as.Date("2016-02-23", "%Y-%m-%d") lubridate::days_in_month(date_leap) # Feb # 29 The Hmisc library has a couple of helpful functions for doing this:
require(Hmisc)
monthDays(as.Date('2010-01-01')) You can write simple function to do that:
numberOfDays <- function(date) { m <- format(date, format="%m") while (format(date, format="%m") == m) { date <- date + 1 } return(as.integer(format(date - 1, format="%d")))
}Invoke as:
> date = as.Date("2011-02-23", "%Y-%m-%d")
> numberOfDays(date)
[1] 28
> date # date is unchanged
[1] "2011-02-23" 2 That is as easy as taking a difference between two dates -- so make it the first of the month and the following month:
R> difftime( as.Date("2011-06-01"), as.Date("2011-05-01") )
Time difference of 31 days
R> as.numeric(difftime( as.Date("2011-06-01"), as.Date("2011-05-01") ))
[1] 31
R> The as.numeric() casts this to a number you can use.
Here are a couple of approaches. Both approaches are vectorized, i.e. the input x can be a single "Date" class date or a vector of such dates.
1) This converts the input date, x, into a "yearmon" object and then converts it back to the last of the month and first of the month and subtracts the two adding 1.
x <- Sys.Date() # use today as the test date
library(zoo)
ym <- as.yearmon(x)
as.Date(ym, frac = 1) - as.Date(ym) + 1
## Time difference of 30 daysor for a numeric result use:
as.numeric(as.Date(ym, frac = 1) - as.Date(ym) + 1)
## [1] 301a) A variation would be:
as.Date(ym + 1/12) - as.Date(ym)
## Time difference of 30 daysor for a numeric result:
as.numeric(as.Date(ym + 1/12) - as.Date(ym))
## [1] 302) This is a base solution. First we define fom which inputs a "Date" vector and returns the first of the month of each component. Now we just take the difference between the first of the next month and the first of the current month.
fom <- function(x) as.Date(cut(x, "month"))
fom1 <- fom(x)
fom2 <- fom(fom1 + 32)
as.numeric(fom2 - fom1)
## [1] 30 2 Here is a simple way: for n in 28:31, find the biggest number that results in a valid date. In my tests this is at least 4 times faster than any of the time-difference-based methods:
ndays <- function(d) { last_days <- 28:31 rev(last_days[which(!is.na( as.Date( paste( substr(d, 1, 8), last_days, sep = ''), '%Y-%m-%d')))])[1]
}
> ndays('1999-03-10')
[1] 31
> ndays('1999-04-10')
[1] 30
> ndays('2000-02-10')
[1] 29Timing comparisons with some of the other methods suggested here:
> system.time( replicate( 5000, nd <- { ym <- as.yearmon('2011-06-01'); as.numeric( as.Date(ym, frac = 1) - as.Date(ym) + 1) })) user system elapsed 16.634 1.807 18.238
> system.time( replicate( 5000, nd <- as.numeric( difftime( as.Date("2011-06-01"), as.Date("2011-05-01") )))) user system elapsed 3.137 0.341 3.470
> system.time( replicate( 5000, nd <- ndays('2011-06-01'))) user system elapsed 0.729 0.044 0.771 2 Here's another possible function that doesn't require any packages to be installed. You just feed the function a date object. Since there's lots of other excellent answers here I wrote it with an eye towards being fairly straightforward and (hopefully) easy to read :)
daysInMonth <- function(d = Sys.Date()){ m = substr((as.character(d)), 6, 7) # month number as string y = as.numeric(substr((as.character(d)), 1, 4)) # year number as numeric # Quick check for leap year leap = 0 if ((y %% 4 == 0 & y %% 100 != 0) | y %% 400 == 0) leap = 1 # Return the number of days in the month return(switch(m, '01' = 31, '02' = 28 + leap, # adds 1 if leap year '03' = 31, '04' = 30, '05' = 31, '06' = 30, '07' = 31, '08' = 31, '09' = 30, '10' = 31, '11' = 30, '12' = 31))
} 1 This is basically Dirk's approach but actually placed in a function, in order to check when the next month is also in the next year; and simply subtracting the dates is the same as using difftime():
numberOfDays <- function(d){ temp <- unlist(strsplit(as.character(d),"-")) begin <- as.Date(paste(temp[1],temp[2],"01",sep="-")) if (temp[2] != "12"){ nextMonth <- as.character(as.integer(temp[2])+1) end <- as.Date(paste(temp[1],nextMonth,"01",sep="-")) return(as.integer(as.Date(end) - as.Date(begin))) } else{ nextYear <- as.character(as.integer(temp[1])+1) end <- as.Date(paste(nextYear,"01","01",sep="-")) return(as.integer(as.Date(end) - as.Date(begin))) }
} You can pass the year, or take the current year by default:
days.in.month = function(month, year = NULL){ month = as.integer(month) if (is.null(year)) year = as.numeric(format(Sys.Date(), '%Y')) dt = as.Date(paste(year, month, '01', sep = '-')) dates = seq(dt, by = 'month', length = 2) as.numeric(difftime(dates[2], dates[1], units = 'days'))
} I'm providing another one-liner no extra packages needed:
NumberOfDays <- function(date)
return(as.numeric(format(as.Date(paste0(format(date,format="%Y"),formatC(ifelse(format(date,format="%m")=="12",0,as.numeric(format(date,format="%m")))+1,width=2,format="d",flag="0"),"01"),"%Y%m%d")-1,format="%d")))
> NumberOfDays(as.Date("2015-02-14","%Y-%m-%d"))
[1] 28
> system.time(NumberOfDays(as.Date("2015-02-14","%Y-%m-%d"))) user system elapsed
0.0010000000 0.0000000000 0.0009999999You should provide a Date object to this function with the formatting of your own preference.
Hi another way to get the days could be:
# Any date
Today <- Sys.Date()
# Get Month and Year
Month <- format(Today,"%m")
Year <- format(Today,"%Y")
# get first date for the next month and then subtract 1
Days <- format( as.Date( paste0("01/",(as.numeric(Month)%%12)+1,"/",Year) ,"%d/%m/%Y")-1,"%d") Yes!
You just need to use the function days_in_month() from lubridate package.
Follow a quick example how you can use the function to provide the days in the month.
lubridate::days_in_month(as.Date("2018-02-01"))Output:
Feb 28
For more information about the function: click here
0> Ymd<-"201602"
> lastDate<-format(seq(as.POSIXct(sprintf("%s%s",substr(Ymd,1,6),"01"),format="%Y%m%d"),length=2,by="1 month")-1,"%Y%m%d")[2]
> cat(as.integer(lastDate)%%100)
29
# Other Output
> lastDate
[1] "20160229"
> cat(substr(lastDate,7,8))
29 1 This is another alternative solution using zoo's yearmon. This is a fast utility.
require(zoo)
days_in_month <- function(d) { rigor_days_in_month <- function(d) { get_day <- function(x) as.numeric(format(x,"%d")) m <- zoo::as.yearmon(d) get_day(as.Date(m, frac = 1)) } mm <- as.numeric(format(d,"%m")) # get_month ifelse(mm %in% c(1,3,5,7,8,10,12), 31, ifelse(mm != 2, 30, rigor_days_in_month(d)))
}You can unwrap get_day() to make it into a one-liner if you would like.
I'm late to the party but this function is much better and supports vectors:
month_days <- function(mon) { # input as class "Date" require(lubridate) day(mon) <- 1 given <- mon month(given) <- month(given) + 1 as.numeric(given - mon)
}