2

I would like to find all of the Tuesdays between two dates. But if the Tuesday falls on a user-defined list of holidays, then I would like Wednesday instead.

This code works in my tests, but it is pretty janky and I am afraid it will fail silently.

low.date <- "1996-01-01"
high.date <- "1997-01-01"
holidays = c("01-01", "07-04", "12-25")
tues <- seq(as.Date(low.date), as.Date(high.date), by = 1) 
tues <- subset(tues, format(tues, "%a") == "Tue")
tues <- ifelse(format(tues, "%m-%d") %in% holidays, tues + 1, tues)
tues <- as.Date(tues, origin = "1970-01-01")

Thanks! I see answers pointing to the timeDate package, but I only see methods for finding business days or holidays. Is there a cleaner/safer logic than what I'm using?

Richard Herron
  • 9,760
  • 12
  • 69
  • 116
  • Only a minor comment but re-using the variable `tues` like that will make it harder to test. If there were an error in the second assignment to `tues`, say, it will be overwritten twice more by the end so it will be hard to trace back. – G. Grothendieck Jan 22 '11 at 23:43
  • @G -- Good call. Thanks. Sometimes I think I am conserving RAM, but that's ridiculous in this case. – Richard Herron Jan 23 '11 at 14:42

2 Answers2

4

It's difficult to modify the logic of your solution. But here is a different form using wday function from lubridate package.

hol_tue <-  wday(tues) == 3L & format(tues, "%m-%d") %in% holidays
wday(tues)[hol_tue] <- 4

Slightly inconveniently in lubridate package day count starts from Sunday with Sunday being day 1 as opposed to POSIXlt where it's 0.

VitoshKa
  • 8,387
  • 3
  • 35
  • 59
  • Why is starting on Sunday inconvienant? You have to start the week somewhere and most businesses I have worked for used timesheets that started the week with Sunday. The standard strftime function also counts Sunday as day 0. – Sharpie Jan 23 '11 at 00:34
  • @Sharpie, 0 is not 1 :) This was my point (updated). I don't mind starting with Sunday but call it 0. POSIXlt refers to Tuesdays as the second day. I don't know how it is in US but in Europe when you say second day of the week you mean Tuesday. – VitoshKa Jan 23 '11 at 09:20
  • @VistoshKa -- Thanks! This is the second time I've heard of `ludridate`, so I'll have to check it out. – Richard Herron Jan 23 '11 at 14:43
  • 1
    We made Sunday 1 to be consistent with what most people expect: the first day of the month is 1, the first day of the year is 1, January is month one, the first year is 1, ... I don't know who thinks this is a good idea: `as.POSIXlt(ymd(20080101))$year` – hadley Jan 23 '11 at 15:45
  • 1
    Another approach is `wday(tues, T) == "Tues"` which is easier to understand when you come back to the code – hadley Jan 23 '11 at 15:47
  • @hadley, sounds reasonable. Would be also nice if `wday(tues)<-"Wed"` and `...<-"Wednesday" worked. In my answer above the wday<- function and readability of the code are the main reasons to use lubridate. – VitoshKa Jan 23 '11 at 18:28
  • Good idea - added to the to do list at https://github.com/hadley/lubridate/issues/issue/82. – hadley Jan 23 '11 at 22:49
  • 2
    Another way to write the code would be `wday(tues) <- wday(tues) + days(hol_tue)` based on conversion of FALSE to 0 and TRUE to 1. – hadley Jan 23 '11 at 22:50
2

POSIXlt in the base package gives you access to wday as a number, which is a little safer since names of days change from system to system.

low.date <- "1996-01-01"
high.date <- "1997-01-01"
holidays <- c("01-01", "07-04", "12-25")

all.days <- seq(as.Date(low.date), as.Date(high.date), by = "day")

# Tuesday is Day 2 of the week
all.tues <- all.days[as.POSIXlt(all.days)$wday == 2]
tues.holidays <- format(all.tues, "%m-%d") %in% holidays
all.tues[tues.holidays] <- all.tues[tues.holidays] + 1
J. Win.
  • 6,662
  • 7
  • 34
  • 52
  • This seems worse than the poster's solution. – G. Grothendieck Jan 22 '11 at 22:59
  • Cleaned it up a bit, but I don't think it can get much clearer. The code in the original question sequences out all the days, then reformats and string compares every day to see if it's a Tuesday, then reformats every one to see if it's a holiday, and reassigns every day with an ifelse whether it needed to change or not. Sorry, but this is better. – J. Win. Jan 23 '11 at 00:34
  • It needs to stick to using `"Date"` class as in the poster's solution. The introduction of calculations based on seconds is particularly undesirable. – G. Grothendieck Jan 23 '11 at 00:44
  • @Jon -- The `+1` is the skill I needed! I knew there had to be a better way than an `ifelse` and a re-class. I am still learning to think the R way. – Richard Herron Jan 23 '11 at 14:45
  • And I think I can avoid the `POSIX` class operation by using the `weekdays()` operator. – Richard Herron Jan 23 '11 at 14:46