| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- const ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
- export function isValidIsoDateYmd(value) {
- if (typeof value !== "string") return false;
- const s = value.trim();
- if (!ISO_DATE_RE.test(s)) return false;
- const [y, m, d] = s.split("-").map((x) => Number(x));
- if (!Number.isInteger(y) || !Number.isInteger(m) || !Number.isInteger(d)) {
- return false;
- }
- // Predictable policy (same as backend):
- // - month: 1..12
- // - day: 1..31
- if (m < 1 || m > 12) return false;
- if (d < 1 || d > 31) return false;
- return true;
- }
- export function normalizeIsoDateYmdOrNull(value) {
- if (typeof value !== "string") return null;
- const s = value.trim();
- if (!s) return null;
- return isValidIsoDateYmd(s) ? s : null;
- }
- /**
- * Compare ISO dates (YYYY-MM-DD).
- * Lexicographic compare is correct for this format.
- */
- export function compareIsoDatesYmd(a, b) {
- const aa = String(a || "");
- const bb = String(b || "");
- if (aa === bb) return 0;
- return aa < bb ? -1 : 1;
- }
- /**
- * Returns true when both dates exist and the range is invalid (from > to).
- * IMPORTANT: from === to is valid and represents a single day.
- */
- export function isInvalidIsoDateRange(from, to) {
- const f = normalizeIsoDateYmdOrNull(from);
- const t = normalizeIsoDateYmdOrNull(to);
- if (!f || !t) return false;
- return f > t;
- }
- /**
- * Format ISO date (YYYY-MM-DD) as German UI date: DD.MM.YYYY
- */
- export function formatIsoDateDe(ymd) {
- const s = normalizeIsoDateYmdOrNull(ymd);
- if (!s) return null;
- const [y, m, d] = s.split("-");
- return `${d}.${m}.${y}`;
- }
- /**
- * Build a compact German label for the active date filter.
- */
- export function formatIsoDateRangeLabelDe({ from = null, to = null } = {}) {
- const f = formatIsoDateDe(from);
- const t = formatIsoDateDe(to);
- if (f && t) {
- if (normalizeIsoDateYmdOrNull(from) === normalizeIsoDateYmdOrNull(to)) {
- return f;
- }
- return `${f} – ${t}`;
- }
- if (f) return `ab ${f}`;
- if (t) return `bis ${t}`;
- return null;
- }
- /**
- * Convert a Date to ISO YYYY-MM-DD using LOCAL calendar values
- * (avoids timezone drift from toISOString()).
- */
- export function toIsoDateYmdFromDate(date) {
- if (!(date instanceof Date) || Number.isNaN(date.getTime())) return null;
- const y = date.getFullYear();
- const m = String(date.getMonth() + 1).padStart(2, "0");
- const d = String(date.getDate()).padStart(2, "0");
- return `${y}-${m}-${d}`;
- }
- /**
- * Convert ISO YYYY-MM-DD to a Date (local time).
- */
- export function toDateFromIsoDateYmd(ymd) {
- const s = normalizeIsoDateYmdOrNull(ymd);
- if (!s) return null;
- const [y, m, d] = s.split("-").map((x) => Number(x));
- return new Date(y, m - 1, d);
- }
|