Системы напоминаний

Системы напоминаний опираются на точные операции над датами и временем, где ключевую роль играет корректное вычисление интервалов, проверка наступления событий и формирование расписаний. В JavaScript подобная логика часто строится поверх библиотеки date-fns, предоставляющей набор чистых функций без мутаций и скрытых состояний.


Любая система напоминаний сводится к описанию сущности задачи во времени:

  • момент срабатывания
  • периодичность (опционально)
  • окно допустимого выполнения
  • текст или действие
  • статус выполнения

Базовая структура:

const reminder = {
  id: 1,
  title: "Проверить отчёт",
  runAt: new Date("2026-05-22T14:30:00"),
  repeat: null
};

Работа с такими структурами требует строгого контроля над датами, что и обеспечивает date-fns через функции неизменяемого характера.


Построение времени срабатывания

Формирование расписания часто базируется на функциях добавления временных единиц:

import { addMinutes, addHours, addDays } from "date-fns";

const now = new Date();

const inFiveMinutes = addMinutes(now, 5);
const inTwoHours = addHours(now, 2);
const tomorrow = addDays(now, 1);

Такие операции позволяют формировать динамические напоминания относительно текущего момента без ручного вычисления timestamp.

Для сложных сценариев комбинируются несколько шагов:

import { addDays, addHours } from "date-fns";

const base = new Date();
const scheduled = addHours(addDays(base, 3), 2);

Округление и нормализация времени

Системы напоминаний часто требуют приведения времени к «границам» единиц измерения:

import { startOfMinute, startOfHour, startOfDay } from "date-fns";

const now = new Date();

const minuteAligned = startOfMinute(now);
const hourAligned = startOfHour(now);
const dayAligned = startOfDay(now);

Эта нормализация используется при:

  • группировке задач по дням
  • планировании очередей
  • устранении дрейфа секунд и миллисекунд

Проверка наступления события

Основной цикл системы напоминаний строится вокруг сравнения дат.

import { isBefore, isAfter, isEqual } from "date-fns";

const now = new Date();

if (isBefore(reminder.runAt, now)) {
  // событие должно быть выполнено
}

Комбинация условий позволяет точно определять состояние задачи:

const isDue =
  isBefore(reminder.runAt, now) || isEqual(reminder.runAt, now);

Окна выполнения и допустимые интервалы

Реальные системы редко работают с точечным временем; чаще используется интервал допустимости:

import { isWithinInterval, addMinutes } from "date-fns";

const now = new Date();

const window = {
  start: addMinutes(reminder.runAt, -2),
  end: addMinutes(reminder.runAt, 2)
};

const active = isWithinInterval(now, window);

Такой подход снижает влияние задержек планировщика и сетевых факторов.


Вычисление разницы времени

Для отображения статуса задач и приоритизации используется вычисление интервалов:

import { differenceInMinutes, differenceInHours } from "date-fns";

const minutesLeft = differenceInMinutes(reminder.runAt, new Date());
const hoursLeft = differenceInHours(reminder.runAt, new Date());

Эти значения позволяют:

  • сортировать очередь
  • отображать время до события
  • выделять просроченные задачи

Представление длительности события

Для сложных систем полезно декомпозировать интервал:

import { intervalToDuration } from "date-fns";

const duration = intervalToDuration({
  start: new Date(),
  end: reminder.runAt
});

Результат содержит структуру:

  • years
  • months
  • days
  • hours
  • minutes
  • seconds

Такая модель используется в интерфейсах планировщиков и логах.


Форматирование времени уведомлений

Отображение времени срабатывания требует стабильного форматирования:

import { format } from "date-fns";

const text = format(reminder.runAt, "yyyy-MM-dd HH:mm");

Шаблоны позволяют унифицировать вывод:

  • HH:mm — краткое время
  • dd.MM.yyyy — дата
  • yyyy-MM-dd HH:mm:ss — технический формат

Архитектура планировщика напоминаний

Типовая реализация включает:

Очередь задач в памяти

const queue = [];

Периодический polling

setInterval(() => {
  const now = new Date();

  queue.forEach(task => {
    if (isBefore(task.runAt, now)) {
      execute(task);
    }
  });
}, 1000);

Сортировка по ближайшему событию

import { compareAsc } from "date-fns";

queue.sort((a, b) => compareAsc(a.runAt, b.runAt));

Такой порядок снижает количество проверок и повышает предсказуемость.


Повторяющиеся напоминания

Повторяющиеся события требуют пересчёта даты после выполнения:

import { addDays } from "date-fns";

function reschedule(task) {
  return {
    ...task,
    runAt: addDays(task.runAt, 1)
  };
}

Для более сложных паттернов используются:

  • ежечасные события (addHours)
  • еженедельные (addWeeks — из расширенного набора)
  • рабочие дни (через пользовательскую логику поверх date-fns)

Временные зоны и погрешности

Системы напоминаний часто сталкиваются с проблемами:

Переходы на летнее время

Сдвиг локального времени приводит к:

  • дублированию часов
  • пропуску интервалов

Разные локали исполнения

Сервер и клиент могут интерпретировать даты по-разному.

Базовая стратегия — хранение времени в UTC:

const utcTime = new Date(Date.UTC(2026, 4, 22, 10, 0, 0));

Границы точности планирования

JavaScript-таймеры не гарантируют точное срабатывание в миллисекунду. Поэтому логика напоминаний строится вокруг:

  • допустимых окон выполнения
  • регулярной сверки времени
  • идемпотентного выполнения задач

Использование date-fns позволяет уменьшить логическую сложность, оставляя обработку времени детерминированной и функциональной.


Композиция операций над датами

Сложные сценарии формируются через композицию функций:

import { addDays, setHours, startOfDay } from "date-fns";

const base = startOfDay(new Date());
const scheduled = setHours(addDays(base, 1), 9);

Такой подход упрощает чтение логики расписаний и уменьшает вероятность ошибок при ручных вычислениях timestamp.