Резервный таймер обратного отсчета, который будет установлен на тот же день / время каждую неделю

#javascript #datetime

#javascript #дата и время

Вопрос:

Если пользователь устанавливает дату на серверной части (с помощью средства выбора даты и времени jQuery), следующая acf_vars.timer переменная будет выглядеть следующим образом во внешнем интерфейсе:

 2021 2 9 13 08 00
 

У меня есть следующая конструкция в качестве таймера обратного отсчета (CODEPEN):

 const [y, month, d, h, minute, s] = acf_vars.timer.split(' ');

// monthIndex in Date Object begins with 0, so we subtract 1
const countDownDate = new Date(y, month - 1, d, h, minute, s).getTime();

const updateCountdown = () => {
    const now = new Date().getTime(); // Get today's date and time
    const distance = countDownDate - now; // Find distance between now and the countdown date
    const expiredTimer = distance <= 0;

    let days = Math.floor(distance / (1000 * 60 * 60 * 24));
    let hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
    let minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
    let seconds = Math.floor((distance % (1000 * 60)) / 1000);

    if (expiredTimer) {
        days = hours = minutes = seconds = 0;
        clearInterval(timerInterval);
    }

    document.querySelectorAll('.ticker').forEach((container) => {
        container.children[0].classList.contains('days') amp;amp;
            (container.children[0].textContent = days);
        container.children[2].classList.contains('hours') amp;amp;
            (container.children[2].textContent = hours);
        container.children[4].classList.contains('minutes') amp;amp;
            (container.children[4].textContent = minutes);
        container.children[6].classList.contains('seconds') amp;amp;
            (container.children[6].textContent = seconds);
    });
};

const timerInterval = setInterval(updateCountdown, 1000);

updateCountdown();
 

Если пользователь не указывает будущую дату в серверной части, я бы хотел использовать резервный вариант, который автоматически устанавливает таймер обратного отсчета на предстоящее воскресенье в 9 утра. Чтобы решить эту проблему, я попытался установить стандартную countDownDate переменную, но у меня возникли проблемы с поиском способа автоматически установить день и время на предстоящее воскресенье в 9 утра.

Ответ №1:

Кажется, вам просто нужен способ установить дату на следующее воскресенье в 09:00. В обычном JS это может быть:

 /* Get a date for next occurence of Sunday at 09:00
 *
 * @param {Date} d - start date, default is current date and time
 * @returns {Date} for next Sunday at 09:00 after d
 */
function getNextSunday(d = new Date()) {
  // Create date for next Sun at 9
  let sun = new Date(d.getFullYear(), d.getMonth(), d.getDate()   (7 - (d.getDay() || 7)), 9, 0, 0, 0);
  // If date is same day but later, move to next Sun
  sun <= d? sun.setDate(sun.getDate()   7) : null;
  return sun;
}

// Examples
// Next Sunday at 9:00
console.log(getNextSunday().toString());
// Next Sunday after Sun 7 Feb at 8:59:59
console.log(getNextSunday(new Date(2021, 1, 7, 8, 59, 59)).toString());
// Next Sunday after Sun 7 Feb at 9:00
console.log(getNextSunday(new Date(2021, 1, 7, 9)).toString()); 

Примечания:

  1. d.getDay() || 7 используется так, что если getDay возвращает 0 (воскресенье), он заменяется числом 7. Это означает, что выражение устанавливает sun на текущий день, если это воскресенье, или на следующее воскресенье, если это не так. В противном случае по воскресеньям он будет создавать дату для предыдущего воскресенья.
  2. sun <= d? sun.setDate(sun.getDate() 7) : null используется так, что если текущая дата — воскресенье, но время после указанного времени (в данном случае 9:00), дата переносится на следующее воскресенье в 9:00. Использование составного ? : оператора таким образом — это просто еще один способ записи if (sun <= d) sun.setDate(sun.getDate() 7) в одной строке. Я предпочитаю, чтобы за операторами if следовал блок, и предпочел бы использовать ? : для простых выражений в отдельных строках.

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

1. Это отличное ванильное решение. Два вопроса: не могли бы вы объяснить, когда и почему оператор OR в (d.getDay() || 7)) будет важен? Поскольку d.getDay() всегда будет что-то возвращать, почему мы включаем это? И не могли бы вы объяснить, что вы имеете в виду If date is same day but later, move to next Sun ? Я думаю, что ваши комментарии уже дают отличное представление, я просто хочу убедиться, что я полностью понимаю концепцию при использовании. Спасибо!

Ответ №2:

Moment.js возможно, это то, что вы ищете.

 moment().endOf('week').add(1, 'second').add(9, 'hours').toString()
 

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

Редактировать

Кроме того, если вы не хотите использовать внешние библиотеки, вы можете проверить текущий день недели и час с помощью объекта даты JavaScript.

 const day = countDownDate.getDay() // weekday sun = 0, sat = 6
const hour = countDownDate.getHours() // 0 - 23
 

По вашей логике вы хотели бы получить значение distance между днем и 0 (также с учетом countDownDate начала воскресенья) и часом и 9. Однако другой способ реализовать это — проверять текущую дату при каждом обновлении, что может уменьшить ошибку в случае прерывания интервала.

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

1. Спасибо за указатель. Как вы думаете, стоило бы потратить усилия на разборку используемых методов, если бы я не хотел использовать moment библиотеку?

2. Конечно, я обновил свой ответ. Я думал, вас заинтересует moment.js поскольку вы уже использовали jquery.

3. «В конце недели используется день начала недели с учетом локали «, что означает, что вы понятия не имеете, что это будет. Если предполагается установить его на воскресенье григорианского календаря, то сделайте именно это.