Найти ближайшую дату, учитывая TargetDate и день недели

12

Найти ближайшую дату TargetDate для данного дня недели.

Например, учитывая дату 20161219и день недели Friday (6), ответ - 20161216.

Другой пример, учитывая дату 20161219и день недели Wednesday (4), ответ 20161221.

Последний пример, учитывая дату 20161219и день недели Monday (2), ответ 20161219.

Правила:

  • Формат даты для ввода и вывода должен совпадать. В примерах я использовал yyyymmdd, но вы можете использовать любой формат, если год (не менее двух цифр), месяц и день месяца «читабельны человеком».
  • День недели вводится как целое число. В моем примере воскресенье - это первый день недели, поэтому это номер дня недели 1. Вы можете иметь любую нумерацию Дня недели, если вы заметите ее, если она отличается от примера.
  • Годы 1970-2030 должны быть учтены.
  • Допускаются общедоступные инструменты для датирования языков и библиотеки, но уличная репутация предоставляется тем, кто предпочитает их не использовать.
  • Наименьшее количество байтов побеждает.
Бретт ВандерВин
источник
2
Могут ли ответы принять дату в другой форме, если это удобно? Кроме того, дни недели также считаются неудобными для языков, которые считают понедельник первым днем ​​недели
Blue
4
Да, строгие требования к форматированию делают эту задачу больше о разборе / преобразовании / форматировании, чем о фактической дате поиска. Было бы лучше, если бы IMO указывал это следующим образом: « Вы можете принять дату и день недели в любом приемлемом формате, если вы используете один и тот же формат для ввода и вывода ». Но так как множество людей уже представили ответы, я думаю, что это должно быть так, как сейчас.
smls
@ Смлс, я согласен. Урок усвоен в следующий раз.
Бретт ВандерВин
1
@BrettVanderVeen Я не думаю, что уже слишком поздно, чтобы ослабить требования. Вы можете просто добавить комментарий к каждому из (в настоящее время 8) ответов об обновленной спецификации. Я сделал такое по случаю.
Адам

Ответы:

1

Perl 6 , 46 байт

{~first *.day-of-week==$^b,Date.new($^a)-3..*}

Лямбда, которая принимает два аргумента: строку даты в yyyy-mm-ddформате и номер дня недели между 1= понедельник и 7= воскресенье.

Объяснение:

                           Date.new($^a)       # Construct Date object from first argument.
                                        -3     # Subtract 3 days.
                                          ..*  # Construct a Range from that, to infinity.
  first                   ,                    # Iterate the range, and return first date...
        *.day-of-week==$^b                     # whose weekday number equals the second arg.

Используя формат даты и дня недели, использованный в примерах, это будет 88 байтов:

{S:g/\-//with ~first *.day-of-week==($^b-2)%7+1,Date.new(|($^a~~/(....)(..)(..)/))-3..*}
SMLS
источник
К вашему сведению, я упростила правила форматирования даты и дня недели. Не стесняйтесь повторно.
Бретт ВандерВин
@BrettVanderVeen: Обновлено.
smls
2

Perl 6 , 83 байта

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

Попытайся

расширенный

->     # 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 「-」
  }
}
Брэд Гилберт b2gills
источник
К вашему сведению, я упростила правила форматирования даты и дня недели. Не стесняйтесь повторно.
Бретт ВандерВин
2

JavaScript (ES6), 93 байта

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

Использует %Y-%m-%dформат даты. Если %a %b %d %Yдопустимый формат даты, то для 82 байтов:

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

Я мог бы сэкономить еще 2 байта, потребовав от воскресенья = 10 до субботы = 16, но это глупое отображение дня недели. Предыдущая версия 142 141 байт (спасибо @Guedes), в которой использовался определенный формат даты ГГГГММДД, с пояснениями:

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

Это слишком долго. Объяснение: Сначала вставляет -s в дату, чтобы перевести ее в формат ISO, который new Dateзатем можно проанализировать. Затем сравнивает требуемый день с фактическим днем, чтобы получить значение между -3и 3для фактического смещения. Магия 9происходит от 7(потому что %процессор по модулю, а не от истинного по модулю) плюс 3(для -3смещения) минус 1(потому что nиндексируется 1 и индексируется getDay0). Затем дата преобразуется обратно в формат ISO, а ненужные символы удаляются.

Нил
источник
Вы можете сохранить один байт, заменив первую замену на.replace(/(?=..(..)?$)/g,'-')
Вашингтон Гуэдс
К вашему сведению, я упростила правила форматирования даты и дня недели. Не стесняйтесь повторно.
Бретт ВандерВин
1

Python, 150 149 байт

from datetime import*
lambda s,n,t='%Y%m%d':[d for d in[datetime.strptime(s,t)+timedelta(o-3)for o in range(7)]if(n-2)%7==d.weekday()][0].strftime(t)

Оууф, 149. repl.it

Без имени функция , которая принимает строку s, дату , как указано ( с использованием формата строки t='%Y%m%d'для ввода и вывода) и число в [1,7], n.
Осматривает семь дней из трех дней до трех дней после того, что указано для дня недели, о котором идет запрос.
Неделя datetimeначинается с понедельника и начинается с нуля, поэтому тест проводится (n-2)%7==d.weekday().

Джонатан Аллан
источник
К вашему сведению, я упростила правила форматирования даты и дня недели. Не стесняйтесь повторно.
Бретт ВандерВин
1

Баш + кореутилс, 50

date -d$1+$[($2-`date -d$1 +%u`+9)%7-3]day +%Y%m%d

Попробуйте онлайн .

  • date -d$1 +%uдает день недели (1..7); 1 понедельник
  • Это вычитается из входного дня недели, чтобы получить разницу дневных индексов. Мы хотим добавить коэффициент в диапазоне [-3, 3] к дате ввода, чтобы получить ближайшую дату, поэтому мы добавляем магический фактор 9, берем модуль 7, затем вычитаем 3. Магический фактор 9 равен 3 + 7 - 1. 3 регулирует входной диапазон для диапазона [-3, 3] и снова вычитается по модулю. 7 требуется, потому что bash modulo аналогичен CPU modulo и может давать -ve результаты - добавление 7 корректирует в нужный диапазон. -1 - потому что на входе 1 - воскресенье, но с выходом% u, 1 - понедельник.
  • Затем внешний dateанализирует <input date> + <magic factor>daysи представляет его в формате ГГГГММДД.
Цифровая травма
источник
К вашему сведению, я упростила правила форматирования даты и дня недели. Не стесняйтесь повторно.
Бретт ВандерВин
1

Язык макросов SAS, 110 байт

%macro s(d,a);%put %sysfunc(putn(%sysfunc(intnx(week.&a.,%eval(%sysfunc(putn(&d,8.))-4),1)),yymmddn8.));%mend;

Страшный язык игры в гольф, но вот объяснение (изнутри):

Создайте макрос, sкоторый принимает ввод даты dи числовой день недели a. Затем %eval(%sysfunc(putn(&d,8.))-4)преобразует дату в числовую дату SAS и уменьшает ее на 4 дня. Затем intnxфункция возвращает следующее вхождение указанного дня недели. putnзатем форматирует дату обратно в ггггммдд и %putпечатает вывод в журнал.

J_Lard
источник
К вашему сведению, я упростила правила форматирования даты и дня недели. Не стесняйтесь повторно.
Бретт ВандерВин
1

Mathematica, 85 49 байтов

#~DatePlus~Mod[#2-#~DateValue~"ISOWeekDay",7,-3]&

Формат ввода для дат является либо списком, {2016, 12, 16}либо фактическим DateObject(я думаю, что несколько других также работают, но я проверил их). В любом случае формат вывода будет совпадать автоматически. Нумерация дня начинается с 1понедельника.

Идея состоит в том, чтобы вычислить разницу между входным днем ​​недели и целевым днем ​​недели, #2-#~DateValue~"ISOWeekDay"а затем отобразить ее, [-3, 3]используя модуль со смещением (то есть Mod[...,7,-3]). Результат просто добавляется к дате ввода (единицей по умолчанию для добавления объекта даты является дни).

Мартин Эндер
источник
К вашему сведению, я упростила правила форматирования даты и дня недели. Не стесняйтесь повторно.
Бретт ВандерВин
@BrettVanderVeen спасибо, отредактировал.
Мартин Эндер
1

R 119 77 56 байт

Безымянная функция, которая принимает два ввода: дату D(строка) и день d(числовой).

function(D,d,s=-3:3+as.Date(D))s[lubridate::wday(s)==d]

Использует функцию wday()из lubridateпакета. Он может легко конвертировать Dateобъекты в дни недели 1, если по умолчанию он считается воскресным.

Редактировать: I / O теперь является as.Date()функцией по умолчанию :"YYYY-mm-dd"

Неуправляемый и объясненный

function(D,d){
    D=as.Date(D);            # Format input date
    s=-3:3+D;                # Generate sequence of +- 3 days
    s[lubridate::wday(s)==d] # Match the weekday that equals to target day                                    
}

Изменить: теперь создает последовательность +-3дней, для которых гарантированно будет найден любой данный день недели. Впоследствии мы должны соответствовать только одному дню, что значительно облегчило проблему.

Billywob
источник
К вашему сведению, я упростила правила форматирования даты и дня недели. Не стесняйтесь повторно.
Бретт ВандерВин
@BrettVanderVeen Спасибо, обновлено.
Billywob