shoc-frontend-new/src/domain/work-orders/utils/wizard-date-utils.ts

177 lines
4.8 KiB
TypeScript
Raw Normal View History

export const MONTH_NAMES = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
export function pad2(n: number): string {
return String(n).padStart(2, "0");
}
export function todayIso(): string {
const d = new Date();
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
}
export function fmtDate(iso: string): string {
if (!/^\d{4}-\d{2}-\d{2}$/.test(iso)) {
return "";
}
const [y, m, d] = iso.split("-").map(Number);
return `${MONTH_NAMES[m - 1].slice(0, 3)} ${d}, ${y}`;
}
export function addDaysIso(iso: string, days: number): string {
const d = new Date(`${iso}T12:00:00`);
d.setDate(d.getDate() + days);
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
}
export function mondayOf(iso: string): string {
const d = new Date(`${iso}T12:00:00`);
const dow = d.getDay();
d.setDate(d.getDate() + (dow === 0 ? -6 : 1 - dow));
return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
}
export function weeksOfMonth(y: number, m: number): string[] {
const firstIso = `${y}-${pad2(m)}-01`;
const lastIso = `${y}-${pad2(m)}-${pad2(new Date(y, m, 0).getDate())}`;
const out: string[] = [];
for (let mon = mondayOf(firstIso); mon <= lastIso; mon = addDaysIso(mon, 7)) {
if (addDaysIso(mon, 4) >= firstIso) {
out.push(mon);
}
}
return out;
}
export function weekRangeLabel(mondayIso: string): string {
if (!/^\d{4}-\d{2}-\d{2}$/.test(mondayIso)) {
return "";
}
const fri = addDaysIso(mondayIso, 4);
const [, sm, sd] = mondayIso.split("-").map(Number);
const [, em, ed] = fri.split("-").map(Number);
return `${MONTH_NAMES[sm - 1].slice(0, 3)} ${sd} – ${MONTH_NAMES[em - 1].slice(0, 3)} ${ed}`;
}
export function fmtWeekRange(startIso: string): string {
if (!/^\d{4}-\d{2}-\d{2}$/.test(startIso)) {
return fmtDate(startIso);
}
return weekRangeLabel(startIso);
}
/** Short date for ranges: "Jun 8". */
export function fmtShortDate(iso: string): string {
if (!iso || !/^\d{4}-\d{2}-\d{2}$/.test(iso)) {
return "—";
}
const [, m, d] = iso.split("-").map(Number);
return `${MONTH_NAMES[m - 1].slice(0, 3)} ${d}`;
}
/** "Jun 8 – Jun 12" for a Schedule On range. */
export function fmtDateRange(start: string, end: string): string {
return `${fmtShortDate(start)} – ${fmtShortDate(end)}`;
}
/** Inclusive day count between two ISO dates (order-independent). */
export function rangeDayCount(a: string, b: string): number {
const d1 = new Date(`${a}T12:00:00`).getTime();
const d2 = new Date(`${b}T12:00:00`).getTime();
return Math.abs(Math.round((d2 - d1) / 86400000)) + 1;
}
export function fmt12(t: string): string {
const [h, m] = t.split(":").map(Number);
const ampm = h < 12 ? "AM" : "PM";
const h12 = h === 0 ? 12 : h > 12 ? h - 12 : h;
return `${h12}:${String(m).padStart(2, "0")} ${ampm}`;
}
export function calcDuration(s: string, e: string): string {
const [sh, sm] = s.split(":").map(Number);
const [eh, em] = e.split(":").map(Number);
const mins = eh * 60 + em - (sh * 60 + sm);
if (mins <= 0) {
return "";
}
const h = Math.floor(mins / 60);
const m = mins % 60;
if (h && m) {
return `${h}h ${m}m`;
}
if (h) {
return `${h}h`;
}
return `${m}m`;
}
export function formatApptRange(start: string, end: string): string {
if (!start) {
return "—";
}
return end ? `${fmt12(start)} – ${fmt12(end)}` : fmt12(start);
}
export const TIME_SLOTS: string[] = (() => {
const out: string[] = [];
for (let m = 5 * 60; m <= 22 * 60; m += 30) {
const h = Math.floor(m / 60);
const mm = m % 60;
out.push(`${String(h).padStart(2, "0")}:${String(mm).padStart(2, "0")}`);
}
return out;
})();
export function parseApptTime(appt: string): { start: string; end: string } {
const m = appt.match(/(\d{1,2}):(\d{2})\s*[–-]\s*(\d{1,2}):(\d{2})/);
if (m) {
return {
start: `${m[1].padStart(2, "0")}:${m[2]}`,
end: `${m[3].padStart(2, "0")}:${m[4]}`,
};
}
const single = appt.match(/(\d{1,2}):(\d{2})\s*(AM|PM)/i);
if (single) {
let h = Number(single[1]) % 12;
if (/pm/i.test(single[3])) {
h += 12;
}
return { start: `${pad2(h)}:${single[2]}`, end: "" };
}
const startOnly = appt.trim().match(/^(\d{1,2}):(\d{2})$/);
if (startOnly) {
return {
start: `${startOnly[1].padStart(2, "0")}:${startOnly[2]}`,
end: "",
};
}
return { start: "", end: "" };
}
export function formatApptTimeValue(start: string, end: string): string {
if (!start) {
return "";
}
return end ? `${start} – ${end}` : start;
}
export function fmtWeekOf(mondayIso: string): string {
if (!mondayIso) {
return "";
}
return `Week of ${weekRangeLabel(mondayIso)}`;
}