Find the nearest Date, given a TargetDate and Day of Week

Perl 6, 83 bytes

->$_,\w{~(Date.new(|m/(....)(..)(..)/)-3...*.day-of-week%7+1==w)[*-1]~~{S:g/"-"//}}

Try it

Expanded

->     # pointy block lambda
  $_,
  \w
{
  ~(  # turn into a Str

    Date.new( |m/(....)(..)(..)/ ) - 3 # three days earlier
    ...                                # generate a sequence
    *.day-of-week % 7 + 1 == w         # stop when the right day is found

  )[*-1]  # the last one

  ~~      # apply the following block

  {
    S:g/"-"//  # remove 「-」
  }
}

JavaScript (ES6), 93 bytes

(s,n,d=new Date(s))=>d.setDate(d.getDate()+(n+9-d.getDay())%7-3)&&d.toISOString().slice(0,10)

Uses %Y-%m-%d date format. If %a %b %d %Y is an acceptable date format, then for 82 bytes:

(s,n,d=new Date(s))=>d.setDate(d.getDate()+(n+9-d.getDay())%7-3)&&d.toDateString()

I could save a further 2 bytes by requiring Sunday = 10 to Saturday = 16, but that's a silly day of week mapping. Previous 142 141 bytes (thanks to @Guedes) version that used the specific YYYYMMDD date format, with its explanation:

(s,n,d=new Date(s.replace(/(?=..(..)?$)/g,'-')))=>d.setDate(d.getDate()+(n+9-d.getDay())%7-3)&&d.toISOString().replace(/-(..)-(..).*/,'$1$2')

This feels far too long. Explanation: First, inserts -s into the date to get it into ISO format which new Date can then parse. Then, compares the desired day with the actual day to get a value between -3 and 3 for the actual offset. The magic 9 comes from 7 (because % is CPU modulo, not true modulo) plus 3 (for the -3 offset) minus 1 (because n is 1-indexed and getDay is 0-indexed). The date is then converted back into ISO format and the unwanted characters removed.

Tags:

Date

Code Golf