Как я мог получить N-й понедельник, вторник и т.д. Каждого месяца между двумя датами, используя JavaScript

#javascript #json #date

#язык JavaScript #json #Дата

Вопрос:

У меня есть программа, в которой пользователь может установить событие с датой начала, датой окончания и периодом повторения, которое событие должно повторяться еженедельно, ежемесячно по дате, ежемесячно по дням недели и ежегодно. После того, как пользователь создаст событие, оно будет сохранено в базе данных, и событие отобразится в календаре на главной странице моей программы.

До сих пор мне удавалось разрабатывать алгоритмы для повторения дат еженедельно, ежемесячно по датам и ежегодно, но не ежемесячно по дням недели. Под «ежемесячно по дням недели» я имею в виду событие, которое повторяется каждый один и тот же день недели раз в месяц в период, занимаемый датой начала и датой окончания.

Например, событие, которое повторяется в первый понедельник каждого месяца с 1 марта по 1 ноября, 1 марта является первым понедельником марта, поэтому я хотел бы сгенерировать дату, которая является первым понедельником апреля, то есть 5 апреля, и так далее каждый месяц с марта по ноябрь.

Приведенный ниже фрагмент-это моя функция для ежемесячного повторения дат по датам, это для событий, которые повторяются каждые 15 числа любого месяца между датой начала и датой окончания.

 function repeatEventMonthly(jsonArray, num){  //First I get the start date and end date as strings on a JSON, I separate //them by the "/" and then use the individual parts to construct two Date Objects  var split = jsonArray[num].Fecha.split("/"); var split2 = jsonArray[num].fechaFin.split("/");  var dd = split[1]; var mm = split[0]; var yy = split[2];  var dd2 = split2[1]; var mm2 = split2[0]; var yy2 = split2[2];  var starDate = new Date(); var endDate = new Date();  starDate.setFullYear(yy); starDate.setMonth(mm-1); starDate.setDate(dd); endDate.setFullYear(yy2); endDate.setMonth(mm2-1); endDate.setDate(dd2);  //the variable top means how many days are between the startDate and endDate. //I use a function that calculates this number  var top = getDaysInDates(starDate, endDate);  if (jsonArray[num].tipoRepeticion == "2") {   //the attribute "tipoRepeticion" in my JSON object lets me know what type of  //repetition the event must be set, 2 means Monthly by Weekday and 3 is Monthly by Date  }else if(jsonArray[num].tipoRepeticion == "3"){   //If the event has repetition type 3, then inside this for loop I generate a new date  //which I create with the value of the startDate and then I add the index number in the for  //loop cycle, meaning that if the startDate is March 3, the in index 1 is March 4, Index 2  //is March 5 and so on, then with an If statement I check that if the date on the  //generated date object is the same as the date of the startDate, I push the date to   //another JSON array that I use to display the dates on the calendar.   for (let index = 0; index lt; top; index  ) {  let sd = new Date(starDate);  sd.setDate(sd.getDate() index);  if (sd.getDate() == starDate.getDate()) {  let str = ((sd.getMonth() 1)   "/"   sd.getDate()   "/"   sd.getFullYear()).toString();  eventMen.push({   // the function "construcDates" helps me create a valid String for my program  // to display at the user.   date: constructDates(str, 0, jsonArray[num].tipoRepeticion),  title: jsonArray[num].titulo,  descripcion: jsonArray[num].descripcion,  tipo: jsonArray[num].tipo,  tipoRepeticion : jsonArray[num].tipoRepeticion  });  }  } }  

Таким образом, эта функция эффективно генерирует события с разницей в один месяц, но в одну и ту же дату, а это означает, что если мое мероприятие начинается 3 марта и заканчивается 3 декабря, то в моем календаре оно отображает одно и то же событие 3 апреля, 3 мая, 3 июня, 3 июля… вплоть до 3 ноября. Этот метод может быть более сложным, чем мне нужно, но я все еще изучаю JavaScript, так что это то, что я придумал.

Однако, несмотря на то, что я решил логику для других типов повторяющихся дат (еженедельно, ежемесячно по датам и ежегодно), повторяющиеся даты ежемесячно по дням недели оказываются довольно сложными, поскольку в каждом месяце не одинаковое количество дней, я не могу просто суммировать 30 или 31 день в каждом месяце и надеяться, что дата будет, например, в один и тот же «первый понедельник месяца». Я хотел бы спросить, есть ли у кого-нибудь какие-либо предложения или решения относительно того, как я мог бы рассчитать эту головоломку, также стоит отметить, что пример «первый понедельник любого месяца» не является единственным возможным типом повторяющейся даты по месяцам, также это может быть второй вторник каждого месяца или последняя пятница.

Ответ №1:

Чтобы получить n-й экземпляр дня в месяце, создайте дату для первого числа месяца, перейдите к первому экземпляру требуемого дня, затем добавьте (n-1) дней на неделю, например

 /* Get nth instance of a particular weekday in a month  *  * @param {number|string} nth - instance of day, 1 to 4  * @param {number|string} day - day of week, Sun 0, Mon 1, etc.  * @param {Date} month - any date in month to get day of  * @returns {Date} that is nth instance of day in month */ function getNthDayInMonth(nth, day, month) {  // Create new date for 1st of month  let d = new Date(month.getFullYear(), month.getMonth());  // Move to first instance of day in month and   // add (n - 1) weeks  d.setDate(1   (7 - d.getDay()   day)%7   (nth - 1)*7);  return d; }  // Formater let f = (d) =gt; new Intl.DateTimeFormat('en-GB', {  weekday:'short',  day:'2-digit',  month:'short',  year: 'numeric' }).format(d);  // Examples [[1,1,new Date(2021,11),'1st Mon in Dec 2021'],  [3,3,new Date(2022, 0),'3rd Wed in Jan 2022'],  [4,1,new Date(2022, 1),'4th Mon in Feb 2022'],  [4,2,new Date(2022, 1),'4th Tue in Feb 2022'],  [4,3,new Date(2022, 1),'4th Wed in Feb 2022'],  [1,3,new Date(2022, 5),'1st Wed in Jun 2022'],  [5,5,new Date(2022,11),'5th Fri in Dec 2022'] ].forEach(a =gt; {  let [n,d,m,note] = a;  console.log(`${note}: ${f(getNthDayInMonth(n,d,m))}`); }); 

Всегда есть по крайней мере 4 случая каждого дня в месяце и до 5 случаев некоторых дней (например, в январе 2022 года 5 суббот, воскресений и понедельников). n-й параметр может быть ограничен 4, или, если разрешены 5-е экземпляры, ограничьте его 5, а затем проверьте, совпадает ли конечный месяц с начальным месяцем. Если нет, выдайте ошибку или просто верните значение undefined.

Чтобы получить все n-е число месяцев между двумя датами, выполните цикл по месяцам, чтобы получить n-е число экземпляров, и удалите все даты, которые находятся до начала или после окончания.

Ответ №2:

Не могли бы вы просто получить день недели первого числа месяца, а затем из этого выяснить, сколько дней осталось до желаемого дня недели. Затем добавьте умножение на 7, пока не получите желаемую дату.

Например:

 const startDate = new Date("2021-03-01"); // Mon Mar 01 2021 const friday = 5; // Friday is the 5th day of the week (Monday is 1) const weekOfMonth = 2; // We want the 3rd week of the month (1st week is 0)  // Start calculations const dayOfWeek = startDate.getUTCDay(); // 1 const diff = friday - dayOfWeek; // 4 const dateOf3rdFriday = new Date(  startDate.getUTCFullYear(),  startDate.getUTCMonth(),  startDate.getUTCDay()   diff   7 * weekOfMonth ); // Fri Mar 19 2021  

В последнюю пятницу месяца вам просто нужно будет сделать то же самое, но в обратном порядке.

Комментарии:

1. Единственная причина использовать UTC заключается в том, что вы начинаете с new Date("2021-03-01") . Если вы начинаете с локальных значений, таких как new Date(2021,2,1) , то вы можете использовать все локальные методы.