Skip to content

Compare versions

From:To:
~3
**Cal.com, раздел «когда я доступен», ноябрь 2022 — по сей день.**

Cal.com, раздел «когда я доступен», ноябрь 2022 — по сей день.

Cal.com — открытая замена Calendly: человек выставляет часы, когда он свободен, а гости сами занимают в них слоты. Часы задаются по дням недели: с такого-то до такого-то. Выпадающий список, шаг пятнадцать минут.

Пока вы работаете днём, всё честно.

### Кадр первый: 18 ноября 2022. Четырнадцать минут
Кадр первый: 18 ноября 2022. Четырнадцать минут

Приходит Ахмед Садман (ahmedsadman) и рассказывает, что работает по ночам — с 22:30 до 01:30 — и что в списке кончается время (#5587):

In the availability section, 11:46 PM to 11:59 PM gets totally wasted as there is no way to include that 15 min slot in my availability.

В разделе доступности промежуток с 23:46 до 23:59 пропадает впустую: его нечем включить в мои часы.

Последний пункт списка — 23:45. Дальше суток нет.

### Кадр второй: 8 декабря 2022. Две строки вместо одной
Кадр второй: 8 декабря 2022. Две строки вместо одной

Через три недели Удит Таккар (Udit-takkar) заводит ту же болезнь с другой стороны (#5929). Человек хочет быть доступным с 20:00 до 03:00. Приходится писать две строки:

8PM to 11:45PM

12:00 AM to 3:AM

Семь часов работы, записанные как два куска с дырой в четверть часа посредине.

### Кадр третий: 21 декабря 2022. Починка за одиннадцать часов
Кадр третий: 21 декабря 2022. Починка за одиннадцать часов

Эмрис Эл (emrysal) присылает PR #6137 с названием, которое описывает ровно то, что делает:

Add 23:59 as a valid availability select option

Добавить 23:59 как допустимый пункт списка доступности.

Открыт в 05:05, влит в 16:13 того же дня. Четырнадцать потерянных минут возвращены. Задача #5929 закрыта.

### Кадр четвёртый: 7 января 2023. Слота всё равно нет
Кадр четвёртый: 7 января 2023. Слота всё равно нет

Через две с половиной недели возвращается Ахмед Садман — тот же человек, что жаловался в ноябре, — и заводит #6323:

My selected availability is 10:30PM - 2:00AM. Given that, it should have a slot at 11:30PM so the meeting would be on 11:30PM - 12:30AM. But, the application doesn't allow booking at 11:30PM. I should mention that I have selected my availability up to 11:59PM for a day.

Мои часы — с 22:30 до 02:00. Значит, должен быть слот в 23:30, встреча шла бы с 23:30 до 00:30. Но приложение не даёт записаться на 23:30. Отмечу, что часы я выставил до 23:59, как позволил тот PR.

Вот он, поворот. Починка дала последние четырнадцать минут суток — и ничего не изменила. Встреча длится час; начавшись в 23:30, она кончится завтра, а завтра — это другая строка расписания.

Не хватало не пункта в списке. Не хватало того, чтобы день умел кончаться после полуночи.

### Кадр пятый: 26 января 2023. Хотфикс
Кадр пятый: 26 января 2023. Хотфикс

Питер Рихелен (PeerRich), сооснователь Cal.com, пишет в задачу две строки:

can you try again? we made a hotfix

попробуйте ещё раз, мы выкатили хотфикс

В тот же день приходит ответ со скриншотом:

Unfortunately, it still doesn't work for me. Not seeing any 11:30 PM option

К сожалению, у меня по-прежнему не работает. Пункта 23:30 не вижу.

Задача закрыта и в тот же день открыта заново.

### Кадр шестой: 22 мая 2023. Честная строка
Кадр шестой: 22 мая 2023. Честная строка

Ещё через четыре месяца тот же PeerRich пишет фразу, ради которой стоит читать чужие задачи:

this is a really low hanging plus hard to fix issue. We're reworking the entire booking page right now and will revisit this later

это одновременно и мелочь, и трудно чинится. Мы сейчас переделываем всю страницу записи и вернёмся к этому позже.

«Мелочь, которую трудно починить» — вот диагноз, и он поставлен верно. Снаружи это один пропущенный пункт в списке. Внутри — предположение, на котором стоит вся выборка слотов: доступность лежит внутри суток.

### Кадр седьмой: 23 ноября 2023. Not planned
Кадр седьмой: 23 ноября 2023. Not planned

Через десять месяцев после открытия и через год после первой жалобы задача закрывается — со статусом not_planned.

Не «починено», не «дубликат». Не будем.

### Кадр восьмой: сегодняChanged
Кадр восьмой: сегодня

В коде Cal.com в сентябре 2026-го список времён по-прежнему собирается так — ScheduleComponent.tsx, строки 574–590:

js
1const end = dayjs().utc().endOf("day");
2for (
3 let t = dayjs().utc().startOf("day");
4 t.isBefore(end);
5 t = t.add(INCREMENT + (!t.add(INCREMENT).isSame(t, "day") ? -1 : 0), "minutes")
6) { … }
7// allow 23:59
8options.push({ value: end.toDate().valueOf(), … });

Тот самый -1: если очередные пятнадцать минут перепрыгнут в следующий день, отнять минуту, чтобы попасть в 23:59. И отдельный push под комментарием // allow 23:59 на строке 588 — пункт, который циклом не получается.

Ниже, у кнопки «добавить промежуток», ещё два шрама — строки 630–643:

js
1const nextRangeEnd =
2 nextRangeStart.hour() === 23
3 ? dayjs(nextRangeStart).add(59, "minutes").add(59, "seconds").add(999, "milliseconds")
4 : dayjs(nextRangeStart).add(1, "hour");
5
6end: nextRangeEnd.isAfter(endOfDay) ? endOfDay.toDate() : nextRangeEnd.toDate(),

Двадцать третий час — особый случай; всё, что вылезает за конец суток, подрезается по конец суток. Три года спустя граница на месте, и видно её именно по заплаткам вокруг.

Ссылки закреплены по коммиту 176037d0 — чтобы через месяц они показывали тот же код, а не чужой на том же номере. Репозиторий переименован в calcom/cal.diy; старые ссылки на calcom/cal.com перенаправляются туда же.

### Цена решения
Цена решения

«Сутки — отрезок от 00:00 до 23:59» — это не строчка, это решение о модели. Заметно оно становится в тот день, когда приходит первый человек, работающий по ночам, и к этому времени на предположении уже стоят выборка слотов, часовые пояса и календарная синхронизация.

Дальше каждая заплатка честно возвращает по кусочку — четырнадцать минут, особый двадцать третий час, подрезка по полуночи — и ни одна не двигает границу.

Отсюда и «мелочь, которую трудно починить»: мелочь — снаружи, трудно — внутри.

Найти, где сутки считаются закрытым отрезкомChanged

Ищите сравнение вида now >= open && now < close над временем в минутах от полуночи — и всё, что берёт endOf('day') как границу диапазона.

refs changed
Проверить, что говорит интерфейс, а не только что считает функция

Смотрите не на возвращаемое значение, а на строку, которую увидит человек. Поиск ближайшего открытия может пропустить сегодняшний день как прошедший и уверенно сказать «Закрыто · откроется в 11:00», пока заведение работает.

Решить: поддерживаем ночную смену или отказываем честноChanged

Третьего не дано. Либо день перестаёт быть закрытым отрезком в расчёте, либо такие часы не сохраняются и человек читает почему. Заплатка на границе суток — не решение: она возвращает минуты, а не сутки.

refs changed
Если отказываем — проверить, что запрет стоит не только на записи

Значение могло попасть в базу мимо формы: сидом, миграцией, прямым SQL или до появления запрета. Такой день живёт дальше и молчит — витрина показывает «закрыто», владелец узнаёт от гостя.

Не делать схему чтения строгой заодно с записью

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