2

I would like to get the same day last year given any year. How can I best do this in R. For example, given Sunday 2010/01/03, I would like to obtain the Sunday of the same week the year before.

# "Sunday"
weekdays(as.Date("2010/01/03", format="%Y/%m/%d"))

# "Saturday"
weekdays(as.Date("2009/01/03", format="%Y/%m/%d"))
Uwe
  • 41,420
  • 11
  • 90
  • 134
user2058291
  • 107
  • 3
  • 10
  • With `lubridate` you could do `as.Date(parse_date_time(paste(year(dates) - 1, week(dates), weekdays(dates)), orders = 'YUA'))` where `dates` is an input vector of dates, but this will fail occasionally for the first or last week of the year where the week is truncated such that there is no corresponding day. – alistaire Jan 21 '17 at 02:57

4 Answers4

5

To find the same weekday one year ago, simply subtract 52 weeks or 364 days from the given date:

d <- as.Date("2010-01-03")
weekdays(d)
#[1] "Sunday"
d - 52L * 7L
#[1] "2009-01-04"
weekdays(d - 52L * 7L)
#[1] "Sunday"

Please note that the calendar year has 365 days (or 366 days in a leap year) which is one or two days more than 52 weeks. So, the calendar date of the same weekday one year ago moves on by one or two days. (Or, it explains why New Year's Eve is always on a different weekday.)

Uwe
  • 41,420
  • 11
  • 90
  • 134
  • This is very good simple practical answer! However, nitpicking the question to highlight general pains with dates, the OPs definition of "same week" is a unclear. Example: `week(as.Date("2017/01/07", format="%Y/%m/%d"))` is 1, but `week(as.Date("2017/01/07", format="%Y/%m/%d") - 364)` is 2 (because Jan 1 2017 is a Sunday). If you use the ISO8601 definition of week number implemented in lubridate `isoweek` then both are week1 – Andrew Lavers Jan 21 '17 at 13:03
  • @epi99, I agree that the phrases _the same day last year_ and _obtain the Sunday of the same week the year before_ in the Q do leave room for interpretation what is meant with _same day_ and _same week_. So, my answer is based on guessing from the example given. – Uwe Jan 21 '17 at 16:38
  • This is exactly what I needed thanks for the answer, sorry for the confusion with the actual question! – user2058291 Jan 21 '17 at 18:15
1

Using lubridate the following formula will give you the corresponding weekday in the same week in the previous year:

as.Date(dDate - 364 - ifelse(weekdays( dDate - 363) == weekdays( dDate ), 1, 0))

Where dDate is some date, i.e. dDate <- as.Date("2016-02-29"). The ifelse accounts for leap years.

Rui Barradas
  • 70,273
  • 8
  • 34
  • 66
  • when could this condition `weekdays( dDate - 363) == weekdays( dDate )` ever be true? you're subtracting 52 x 7 days, so the weekday will always be the same – maja zaloznik May 03 '22 at 12:27
0

Here's a simple algorithm. subtract 365 days from the day of interest. Adjust that day to the closest matching day of the week using the Tableau code below (easily translatable into other languages). This is equivalent to the rule in the table below (with 1 = Monday and 7 = Sunday). Basically you adjust day - 365 to be on the correct day of the week either in the same week if that moves <= 3 days otherwise you use the matching weekday from the previous/next week. It'll choose whichever leads to the least difference in terms of # of days.

[day prior year raw] = [day] - 365
[matching day prior year] = 
if abs(datepart('weekday',[day]) - datepart('weekday',[day prior year raw]))<= 3
then [day prior year raw]+datepart('weekday',[day]) - datepart('weekday',[day prior year raw])
else [day prior year raw]+(if  datepart('weekday',[day]) > datepart('weekday',[day prior year raw])
        then -7+(datepart('weekday',[day]) - datepart('weekday',[day prior year raw]))
        else 7+(datepart('weekday',[day]) - datepart('weekday',[day prior year raw])) end
      )
end)

enter image description here

k13
  • 713
  • 8
  • 17
-1

Look at ?years in package lubridate. This creates a period object which correctly spans a period, across leap years.

> library(lubridate)
> # set the reference date
> d1 = as.Date("2017/01/03", format="%Y/%m/%d")
> 
> # verify across years and leap years
> d1 - years(1)
[1] "2016-01-03"
> d1 - years(2)
[1] "2015-01-03"
> d1 - years(3)
[1] "2014-01-03"
> d1 - years(4)
[1] "2013-01-03"
> d1 - years(5)
[1] "2012-01-03"
> 
> weekdays(d1 - years(1))
[1] "Sunday"
> weekdays(d1 - years(2))
[1] "Saturday"
> 
> # feb 29 on year period in yields NA
> ymd("2016/02/29") - years(1)
[1] NA
> 
> # feb 29 in a non-leap year fails to convert
> ymd("2015/02/29") - years(1)
[1] NA
Warning message:
All formats failed to parse. No formats found. 
> 
> # feb 29, leap year with 4 year period works.
> ymd("2016/02/29") - years(4)
[1] "2012-02-29"
> 
Andrew Lavers
  • 4,328
  • 1
  • 12
  • 19
  • You are right, `lubridate` helps to find the same _date_ one year ago but this will be a different _weekday_. So, how does this answer the question of the OP _given Sunday [...] I would like to obtain the Sunday of the same week the year before_? BTW, @alistaire had already suggested a `lubridate` solution in his comment. – Uwe Jan 21 '17 at 07:46