@plasius/schema
Version:
Entity schema definition & validation helpers for Plasius ecosystem
61 lines (52 loc) • 2.29 kB
text/typescript
/**
* Validates whether a string is a properly formatted ISO 8601 datetime, date, or time.
*
* @param value - The value to validate.
* @param options - Optional settings for validation mode.
* @param options.mode - The mode of validation:
* - "datetime" (default): Checks full ISO 8601 datetime and equality with toISOString().
* - "date": Validates that the string is in YYYY-MM-DD format and represents a valid date.
* - "time": Validates that the string matches a valid HH:MM:SS(.sss)?Z? ISO 8601 time pattern.
* @returns True if the value is valid according to the specified mode; otherwise, false.
*/
export const validateDateTimeISO = (
value: unknown,
options?: { mode?: "datetime" | "date" | "time" }
): boolean => {
const mode = options?.mode ?? "datetime";
if (typeof value !== "string") return false;
if (mode === "datetime") {
// Strict ISO 8601 date-time: YYYY-MM-DDTHH:mm:ss(.fraction)?(Z|±HH:MM)
const isoDateTimeRegex =
/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
if (!isoDateTimeRegex.test(value)) return false;
const date = new Date(value);
if (Number.isNaN(date.getTime())) return false;
// We purposely do NOT require `value === date.toISOString()` because
// valid ISO8601 inputs may include offsets (e.g., "+01:00") or omit
// milliseconds, both of which produce a different canonical ISO string.
// The regex ensures strict shape; Date parsing ensures it is a real moment.
return true;
}
if (mode === "date") {
// YYYY-MM-DD format
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (!dateRegex.test(value)) return false;
const date = new Date(value);
if (isNaN(date.getTime())) return false;
// Ensure the date parts match exactly (to avoid 2023-02-30 being accepted)
const [year, month, day] = value.split("-").map(Number);
return (
date.getUTCFullYear() === year &&
date.getUTCMonth() + 1 === month &&
date.getUTCDate() === day
);
}
if (mode === "time") {
// HH:MM:SS(.sss)?Z?
// Hours: 00-23, Minutes: 00-59, Seconds: 00-59, optional fractional seconds, optional Z
const timeRegex = /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d(\.\d+)?Z?$/;
return timeRegex.test(value);
}
return false;
};