Find out the number of days of a month in R
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 (http://www.php.net/manual/en/function.date.php):
days = format(date, "%t")
but "%t" seems to have a different meaning in R. Is there a solution for this p开发者_运维知识库roblem?
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
# 28
Leap 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"
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 days
or for a numeric result use:
as.numeric(as.Date(ym, frac = 1) - as.Date(ym) + 1)
## [1] 30
1a) A variation would be:
as.Date(ym + 1/12) - as.Date(ym)
## Time difference of 30 days
or for a numeric result:
as.numeric(as.Date(ym + 1/12) - as.Date(ym))
## [1] 30
2) 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
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] 29
Timing 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
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))
}
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.0009999999
You 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
> 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
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)
}
精彩评论