f8ab2a1d71
Tests / frontend-test (push) Successful in 2m1s
SharePoint хранит поле «только дата» как полночь по поясу сайта (UTC+5) в UTC: «05.10» приходит из REST как 2026-10-04T19:00:00Z. Формы брали первые 10 символов и показывали 04.10, а каждое пересохранение (полное право, исполнитель, повторная отправка) записывало дату ещё на день раньше. Чтение всех дат идёт через spDateOnlyToIso (округление до ближайшей полуночи UTC), «сегодня» — через todayIsoDate вместо toISOString, который до 05:00 давал вчерашнее число. Уже сдвинутые записи в SP не исправляются. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
57 lines
2.9 KiB
TypeScript
57 lines
2.9 KiB
TypeScript
function pad2(n: number): string {
|
||
return n < 10 ? `0${n}` : `${n}`;
|
||
}
|
||
|
||
/** Сегодня + n дней в формате YYYY-MM-DD (для min/max у input type=date). */
|
||
export function isoDatePlusDays(days: number): string {
|
||
const d = new Date();
|
||
d.setDate(d.getDate() + days);
|
||
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
|
||
}
|
||
|
||
/**
|
||
* Сегодня + n КАЛЕНДАРНЫХ месяцев в формате YYYY-MM-DD (для min/max у input
|
||
* type=date) — не приближение через 30 дней: `setMonth` сам разносит месяцы
|
||
* разной длины (28–31 день) и года на границе декабря/января.
|
||
*
|
||
* 31 января + 1 месяц → JS даёт 3 марта (переносит лишние дни на следующий
|
||
* месяц, а не откатывает до последнего числа) — для месячного уведомления это
|
||
* не проблема (крайняя ранняя дата и так сдвигается на несколько дней позже,
|
||
* не раньше), но не считать это «ровно через месяц» в общем случае.
|
||
*/
|
||
export function isoDatePlusMonths(months: number): string {
|
||
const d = new Date();
|
||
d.setMonth(d.getMonth() + months);
|
||
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
|
||
}
|
||
|
||
/** Сегодня по МЕСТНОМУ времени в формате YYYY-MM-DD (toISOString дал бы дату по UTC — до 05:00 вчерашнюю). */
|
||
export function todayIsoDate(): string {
|
||
return isoDatePlusDays(0);
|
||
}
|
||
|
||
const HALF_DAY_MS = 12 * 60 * 60 * 1000;
|
||
|
||
/**
|
||
* Значение поля SharePoint «только дата» → YYYY-MM-DD для input type=date.
|
||
*
|
||
* REST отдаёт такие поля в UTC: «5 октября» на сайте с поясом UTC+5 приходит
|
||
* как «2026-10-04T19:00:00Z», и `slice(0, 10)` давал 4-е число (а каждое
|
||
* пересохранение сдвигало дату ещё на день назад). Округляем до ближайшей
|
||
* полуночи UTC (+12 ч) — так верно для любого пояса сайта в пределах ±11 ч и
|
||
* не зависит от пояса браузера.
|
||
*/
|
||
export function spDateOnlyToIso(value: unknown): string {
|
||
if (typeof value !== 'string' || value === '') return '';
|
||
if (value.indexOf('T') < 0) return value.slice(0, 10);
|
||
const ms = Date.parse(value);
|
||
if (isNaN(ms)) return value.slice(0, 10);
|
||
return new Date(ms + HALF_DAY_MS).toISOString().slice(0, 10);
|
||
}
|
||
|
||
/** YYYY-MM-DD → дд.мм.гггг, для текста ошибок валидации. Невалидную строку возвращает как есть. */
|
||
export function formatIsoDate(iso: string): string {
|
||
const [y, m, d] = iso.split('-');
|
||
return y && m && d ? `${d}.${m}.${y}` : iso;
|
||
}
|