import z from "zod"; const dateRe = /^\d{4}-\d{2}-\d{2}$/ // YYYY-MM-DD const timeRe = /^(?:[01]\d|2[0-3]):[0-5]\d$/ // HH:mm (24h)\ export function parseLocalDateTime(dateStr: string, timeStr: string) { // Construct a Date in the user's local timezone const [y, m, d] = dateStr.split("-").map(Number) const [hh, mm] = timeStr.split(":").map(Number) return new Date(y, m - 1, d, hh, mm, 0, 0) } export const calendarEventSchema = z.object({ name: z.string().min(2, "Please enter at least 2 characters").max(100), startDate: z.string().regex(dateRe, "Use YYYY-MM-DD"), startTime: z.string().regex(timeRe, "Use HH:mm (24h)"), endDate: z.string().regex(dateRe, "Use YYYY-MM-DD"), endTime: z.string().regex(timeRe, "Use HH:mm (24h)"), location: z.string().max(200).default(""), color: z.string().regex(/^#([0-9A-Fa-f]{6})$/, "Use a hex color like #AABBCC"), description: z.string().max(2000).default(""), id: z.number().int().nonnegative().nullable().default(null), }).superRefine((vals, ctx) => { const start = parseLocalDateTime(vals.startDate, vals.startTime) const end = parseLocalDateTime(vals.endDate, vals.endTime) if (!(end > start)) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "End must be after start", path: ["endTime"], // attach to a visible field }) } })