I tried to generate a sequence of dates between two dates. By search all the old posts, I found very nice solution using seq.Date.
For example:
> seq.Date(as.Date("2016/1/15"), as.Date("2016/5/1"), by = "month")
[1] "2016-01-15" "2016-02-15" "2016-03-15" "2016-04-15"
The above function yields very nice solution. However, it doesnt work when the date is 30 or 31 in Jan.
> seq.Date(as.Date("2016/1/30"), as.Date("2016/5/1"), by = "month")
[1] "2016-01-30" "2016-03-01" "2016-03-30" "2016-04-30"
The second anniversary jumps to March instead of being capped at 29/Feb. I couldnt find a workaround for this.
Here's an approach that also works in other cases:
library(lubridate)
fun <- function(from, to, by) {
mySeq <- seq.Date(as.Date(from), as.Date(to), by = by)
as.Date(sapply(mySeq, function(d) d + 1 - which.max(day(d - 0:3))), origin = "1970-01-01")
}
fun("2016/1/30", "2016/5/1", "month")
# [1] "2016-01-30" "2016-02-29" "2016-03-30" "2016-04-30"
fun("2017/1/31", "2017/5/1", "month")
# [1] "2017-01-31" "2017-02-28" "2017-03-31" "2017-04-30"
fun("2017/1/29", "2017/5/1", "month")
# [1] "2017-01-29" "2017-02-28" "2017-03-29" "2017-04-29"
What fun does is that it subtracts 0:3 from each date and chooses the one that has the largest day.
With lubridate package
library('lubridate')
pmin(
ymd('2018-01-30') + months(0:11), # NA where month goes over
ymd('2018-01-01') + months(1:12) - days(1), # last day of month
na.rm = T
)
[1] "2018-01-30" "2018-02-28" "2018-03-30"
[4] "2018-04-30" "2018-05-30" "2018-06-30"
[7] "2018-07-30" "2018-08-30" "2018-09-30"
[10] "2018-10-30" "2018-11-30" "2018-12-30"
Related
I am trying to get a sequence week interval from 2018-09-03 to 2021-03-23 showing only Monday and Sunday. I would get as result dates_ranges.
dates_ranges=c("2018-09-03 2018-09-09", "2018-09-10 2018-09-16", "2018-09-17 2018-09-23", ... , "2021-03-22 2021-03-28"
By using the seq. function I get only the Mondays. A second approach I used was
Map(c, dates_ranges[-length(dates_ranges)], dates_ranges[-1]) but Than I have seqence of intervals as a numeric
Could u please help me?
`
startdate <- as.Date("2018-09-03")
enddate <- as.Date("2021-03-22")
#first approach
dates_ranges<- seq.Date(from= (startdate),to=enddate, by=
"week")
#second approach
dates_ranges<- as.numeric(dates_ranges)
weeklyinterval<-Map(c, dates_ranges[-length(dates_ranges)],
dates_ranges[-1])
`
You can create all the dates sequences and filter to keep only Sundays and Mondays.
start_date <- as.Date('2018-09-03')
end_date <- as.Date('2021-03-23')
all_dates <- seq(start_date, end_date, by = 'day')
selected_dates <- all_dates[weekdays(all_dates) %in% c('Monday', 'Sunday')]
selected_dates
# [1] "2018-09-03" "2018-09-09" "2018-09-10" "2018-09-16" "2018-09-17"
# [6] "2018-09-23" "2018-09-24" "2018-09-30" "2018-10-01" "2018-10-07"
#[11] "2018-10-08" "2018-10-14" "2018-10-15" "2018-10-21" "2018-10-22"
#...
#...
You can then split the dataset in group of two.
split(selected_dates, ceiling(seq_along(selected_dates)/2))
#$`1`
#[1] "2018-09-03" "2018-09-09"
#$`2`
#[1] "2018-09-10" "2018-09-16"
#$`3`
#[1] "2018-09-17" "2018-09-23"
#...
#...
Use format(., format="%w") to find which weekdays are 0 (Sunday) or 1 (Monday).
dates_ranges <- seq.Date(as.Date("2021-09-03"), as.Date("2021-10-23"), by = "day")
dates_ranges
# [1] "2021-09-03" "2021-09-04" "2021-09-05" "2021-09-06" "2021-09-07" "2021-09-08" "2021-09-09" "2021-09-10" "2021-09-11"
# [10] "2021-09-12" "2021-09-13" "2021-09-14" "2021-09-15" "2021-09-16" "2021-09-17" "2021-09-18" "2021-09-19" "2021-09-20"
# [19] "2021-09-21" "2021-09-22" "2021-09-23" "2021-09-24" "2021-09-25" "2021-09-26" "2021-09-27" "2021-09-28" "2021-09-29"
# [28] "2021-09-30" "2021-10-01" "2021-10-02" "2021-10-03" "2021-10-04" "2021-10-05" "2021-10-06" "2021-10-07" "2021-10-08"
# [37] "2021-10-09" "2021-10-10" "2021-10-11" "2021-10-12" "2021-10-13" "2021-10-14" "2021-10-15" "2021-10-16" "2021-10-17"
# [46] "2021-10-18" "2021-10-19" "2021-10-20" "2021-10-21" "2021-10-22" "2021-10-23"
dates_ranges[format(dates_ranges, format="%w") %in% 0:1]
# [1] "2021-09-05" "2021-09-06" "2021-09-12" "2021-09-13" "2021-09-19" "2021-09-20" "2021-09-26" "2021-09-27" "2021-10-03"
# [10] "2021-10-04" "2021-10-10" "2021-10-11" "2021-10-17" "2021-10-18"
An advantage of %w over %a or %A (abbreviated or full weekday name) is that those are prone to locale/language changes, whereas %w is always integer.
See ?strptime for more discussion about the %-codes for formatting.
I have a vector of dates called KeyDates containing two key dates. I would like to make a new vector of dates called KeyDatesPlus containing those two key dates and the two days after, in chronological order.
KeyDates <- structure(c(15159,15165), class = "Date")
#KeyDates Output:
[1] "2011-07-04" "2011-07-10"
#desired output for KeyDatesPlus:
[1] "2011-07-04" "2011-07-05" "2011-07-10" "2011-07-11"
How could I achieve that? Thank you very much.
sort(c(KeyDates, KeyDates + 1))
[1] "2011-07-04" "2011-07-05" "2011-07-10" "2011-07-11"
structure( sapply(KeyDates, "+", (0:1)), class = "Date")
[1] "2011-07-04" "2011-07-05" "2011-07-10" "2011-07-11"
Or:
as.Date( sapply(KeyDates, "+", (0:1)))
[1] "2011-07-04" "2011-07-05" "2011-07-10" "2011-07-11"
KeyDates <- structure(c(15159,15165), class = "Date")
KeyDates.plus <- as.Date(sapply(KeyDates, function(x) c(x, x+1)))
An answer using the package lubridate:
library("lubridate")
your.vector <- c("2011-07-04", "2011-07-10")
your.vector <- parse_date_time(x = your.vector, orders = "ymd")
your.vector
# [1] "2011-07-04 UTC" "2011-07-10 UTC"
one.day <- days(x = 1)
one.day
# [1] "1d 0H 0M 0S"
your.vector + one.day
# [1] "2011-07-05 UTC" "2011-07-11 UTC"
# your exact desired output (non-UTC time zone can be specified in parse_date_time):
new.vector <- sort(x = c(your.vector, your.vector + one.day))
# [1] "2011-07-04 UTC" "2011-07-05 UTC" "2011-07-10 UTC" "2011-07-11 UTC"
Lubridate distinguishes a "period" from a "duration."
A period is the time on the clock (ie if daylight savings time happens, it's what the clock reads). That's what's specified here using days().
A duration is the physical time (ie if daylight savings time happens, it's how long you've actually been sitting there.) That could be specified instead using ddays().
KeyDates <- structure(c(15159,15165), class = "Date")
KeyDatesPlus <- KeyDates+1
KeyDatesPlus <- sort(unique(c(KeyDates, KeyDatesPlus)))
as.Date() has a useful function in that it can give you the last n days as, e.g.:
dt <- Sys.Date()-6
> dt
[1] "2015-09-25"
Is there a way to tell it to give the last six months instead of the last six days?
I need something more precise than 6*30, as it should be the last day of the month.
You cannot use just Sys.Date to do this but there are ways. The following will give you just the correct months but not the correct days (i.e. the last day of the month):
#Sys.Date will return 2015-10-01 today
dates <- seq( Sys.Date() - months(6), Sys.Date(), '1 month')
dates
[1] "2015-04-01" "2015-05-01" "2015-06-01" "2015-07-01" "2015-08-01" "2015-09-01" "2015-10-01"
However, I found this very nice blog on R-bloggers which defines this function ( I slightly modified it to work with Dates) that returns the last day of the month:
eom <- function(date) {
# date character string containing POSIXct date
date.lt <- as.POSIXlt(dates) # add a month, then subtract a day:
mon <- date.lt$mon + 2
year <- date.lt$year
year <- year + as.integer(mon==13) # if month was December add a year
mon[mon==13] <- 1
iso = ISOdate(1900+year, mon, 1, hour=0, tz='')
result = as.POSIXct(iso) - 86400 # subtract one day
result + (as.POSIXlt(iso)$isdst - as.POSIXlt(result)$isdst)*3600
}
Now running:
> eom(dates)
[1] "2015-04-30 BST" "2015-05-31 BST" "2015-06-30 BST" "2015-07-31 BST" "2015-08-31 BST" "2015-09-30 BST" "2015-10-31 GMT"
Returns the correct results.
Is that what you are looking for?
today = Sys.Date()
lastSixMonths = seq(today, length.out=6, by="-1 month")
print(lastSixMonths)
# [1] "2015-10-01" "2015-09-01" "2015-08-01" "2015-07-01" "2015-06-01"
# [6] "2015-05-01"
Using lubridate, how to calculate the last day of the previous quarter for a given date?
The below formula doesn't seem to work for Nov 3rd, 2014 (other dates work)
library(lubridate)
date = as.POSIXct("2014-11-03")
date - days(day(date)) - months(month(date) %% 3 - 1)
# NA
Interesting enough, changing order works:
date - months(month(date) %% 3 - 1) - days(day(date))
# "2014-09-30 UTC"
Here are some possibilities with functions from packages zoo and timeDate, and base R. The zoo code was improved by #G.Grothendieck, and he also suggested the base alternative (thanks a lot!). I leave the lubridate solution(s) to someone else.
First, use class yearqtr in package zoo to represent the quarterly data. You may then use as.Date.yearqtr and the frac argument "which is a number between 0 and 1 inclusive that indicates the fraction of the way through the period that the result represents. The default is 0 which means the beginning of the period" (see ?yearqtr, and ?yearmon for frac).
Step by step:
library(zoo)
date <- as.Date("2014-11-03")
# current quarter
current_q <- as.yearqtr(date)
current_q
# [1] "2014 Q4"
# first date in current quarter
first_date_current_q <- as.Date(current_q, frac = 0)
first_date_current_q
# [1] "2014-10-01"
# last date in previous quarter
last_date_prev_q <- first_date_current_q - 1
last_date_prev_q
# [1] "2014-09-30"
And a short version by #G.Grothendieck (thanks!)
as.Date(as.yearqtr(date)) - 1
# [1] "2014-09-30"
A nice base R solution by #G.Grothendieck
as.Date(cut(date, "quarter")) - 1
# [1] "2014-09-30"
Another possibility is to use timeFirstDayInQuarter and timeLastDayInQuarter functions in package timeDate:
library(timeDate)
timeLastDayInQuarter(timeFirstDayInQuarter(date) - 1)
# GMT
# [1] [2014-09-30]
I know this is old, but since this question specifically asks for a lubridate solution and I couldn't find working code for it elsewhere, I figured I'd post, keeping in mind that the base R solution is likely the most succinct:
> sampleDate <- ymd('2015-11-11')
> yearsToSample <- years(year(sampleDate) - year(origin))
> yearsToSample
[1] "45y 0m 0d 0H 0M 0S"
> additionalMonths <- months((quarter(sampleDate) - 1) * 3)
> additionalMonths
[1] "9m 0d 0H 0M 0S"
> startOfQuarter <- ymd(origin) + yearsToSample + additionalMonths - days(1)
> startOfQuarter
[1] "2015-09-30 UTC"
Confirmed that this works for dates previous to the origin ('1970-01-01') as well.
Here is a pure lubridate solution for 2021:
> date <- ymd("2014-11-03")
> yq(quarter(date, with_year = TRUE)) - days(1)
[1] "2014-09-30"
Breaking it down:
> # The year and quarter
> quarter(date, with_year = TRUE)
[1] 2014.4
> # The first day of the quarter as a date
> yq(quarter(date, with_year = TRUE))
[1] "2014-10-01"
# The day before the first day of the quarter as a date
> yq(quarter(date, with_year = TRUE)) - days(1)
[1] "2014-09-30"
How do I get a sequence of monthly dates that ends on a given month and has a given length? seq(as.Date(*), length, by="month") assumes the start date is given, not the end date, and AFAIK it's impossible to specify a negative value for by in this case.
ETA: that is, I want a sequence that spans a given period, but one whose end point is specified rather than the start point. So, something like seq(to="2000-03-01", len=3, by="month") --> 2000-01-01, 2000-02-01, 2000-03-01.
Try this:
rev(seq(as.Date("2000-03-01"), length = 3, by = "-1 month"))
## [1] "2000-01-01" "2000-02-01" "2000-03-01"
library(lubridate)
ymd('2011-03-03') - months(0:5)
Maybe you could just compute it forward, using by=month as the +1 increment, and then reverse:
R> rev(seq(as.Date("2011-01-01"), length=6, by="month"))
[1] "2011-06-01" "2011-05-01" "2011-04-01" "2011-03-01" "2011-02-01" "2011-01-01"
Here you go. Base functions only:
last.days.of.month <- function(dt) {ldt<- as.POSIXlt(dt)
ldt$mon <- ldt$mon+1
ldt$mday <- 1
return(format( ldt -1, "%Y-%m-%d"))}
last.days.of.month(as.Date(c("2010-01-06","2010-03-06", "2010-02-06")) )
# [1] "2010-01-31" "2010-03-31" "2010-02-28"
seq.ldom <- function(dt, nmonths) {ldt<- rep(as.POSIXlt(dt)[1], nmonths)
ldt$mon <- ldt$mon+seq(1:nmonths)
ldt$mday <- 1
return(format( ldt -1, "%Y-%m-%d"))}
seq.ldom(as.Date("2010-01-06"), 5)
#[1] "2010-01-31" "2010-02-28" "2010-03-31" "2010-04-30"
#[5] "2010-05-31"
Oh, for some reason I thought you wanted the last days of the month. Sorry about the useless code. The first days of the month is not hard.
seq.fdom <- function(dt, nmonths) {ldt<- rep(as.POSIXlt(dt)[1], nmonths)
ldt$mon <- ldt$mon+seq(0:(nmonths-1))
ldt$mday <- 1
return(format( ldt , "%Y-%m-%d"))}
seq.fdom(as.Date("2010-01-06"), 5)
#[1] "2010-02-01" "2010-03-01" "2010-04-01" "2010-05-01"
#[5] "2010-06-01"
And getting the prior months either:
seq.prior.fdom <- function(dt, nmonths) {ldt<- rep(as.POSIXlt(dt)[1], nmonths)
ldt$mon <- ldt$mon-rev(0:(nmonths-1))
ldt$mday <- 1
return(format( ldt , "%Y-%m-%d"))}
seq.prior.fdom(as.Date("2010-01-06"), 5)
#[1] "2009-09-01" "2009-10-01" "2009-11-01" "2009-12-01"
#[5] "2010-01-01"
I think the basic principle is clear (if not beaten to death with a canoe paddle.)