You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

89 lines
3.5 KiB
TypeScript

// frontend/lib/calendar/time.ts — cal.jsx 의 toMin/fmt/topOf 이식
export const HSTART = 7;
export const PXH = 52;
export const HOURS = Array.from({ length: 16 }, (_, i) => 7 + i); // 7..22
export const toMin = (t: string): number => {
const [h, m] = t.split(":").map(Number);
return h * 60 + m;
};
// 분 → "HH:MM"(자정~23:59 캡). 드래그 이동/리사이즈 결과 계산용.
export const fromMin = (total: number): string => {
const t = Math.max(0, Math.min(total, 23 * 60 + 59));
const h = Math.floor(t / 60);
const m = t % 60;
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
};
export const GRID_END_MIN = (HSTART + HOURS.length) * 60; // 23:00 (그리드 하단)
export const fmt = (t: string): string => {
const [h, m] = t.split(":").map(Number);
const ap = h < 12 ? "오전" : "오후";
const hh = h % 12 === 0 ? 12 : h % 12;
return `${ap} ${hh}:${String(m).padStart(2, "0")}`;
};
export const topOf = (t: string): number => ((toMin(t) - HSTART * 60) / 60) * PXH;
// 그리드 내부 Y 픽셀 → "HH:MM"(30분 단위 반올림). 슬롯 클릭 생성용.
export const timeOfPx = (y: number): string => {
const totalMin = HSTART * 60 + (y / PXH) * 60;
const lastMin = (HSTART + HOURS.length) * 60; // 22:00
const snapped = Math.round(totalMin / 30) * 30;
const clamped = Math.max(HSTART * 60, Math.min(snapped, lastMin - 60));
const h = Math.floor(clamped / 60);
const m = clamped % 60;
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
};
// "HH:MM" + n시간 → "HH:MM"(24시 캡). 생성 시 종료시각 계산용.
export const addHour = (t: string, n: number): string => {
const total = Math.min(toMin(t) + n * 60, 23 * 60 + 59);
const h = Math.floor(total / 60);
const m = total % 60;
return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`;
};
// ── 실제 날짜(ISO "YYYY-MM-DD") 유틸 ──────────────────────────────
export const parseISO = (iso: string): Date => {
const [y, m, d] = iso.split("-").map(Number);
return new Date(y, (m || 1) - 1, d || 1);
};
export const isoOf = (d: Date): string =>
`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
export const addDays = (iso: string, n: number): string => {
const d = parseISO(iso);
d.setDate(d.getDate() + n);
return isoOf(d);
};
export const addMonths = (iso: string, n: number): string => {
const d = parseISO(iso);
d.setDate(1);
d.setMonth(d.getMonth() + n);
return isoOf(d);
};
export const startOfWeek = (iso: string): string => {
const d = parseISO(iso);
d.setDate(d.getDate() - d.getDay()); // 일요일 시작
return isoOf(d);
};
export const weekDatesOf = (iso: string): string[] => {
const s = startOfWeek(iso);
return Array.from({ length: 7 }, (_, i) => addDays(s, i));
};
export const dayNum = (iso: string): number => parseISO(iso).getDate();
export const monthNum = (iso: string): number => parseISO(iso).getMonth() + 1;
export const yearNum = (iso: string): number => parseISO(iso).getFullYear();
// 월 그리드(42칸): 해당 월 1일이 포함된 주(일요일)부터 6주.
export const monthCells = (iso: string): { date: string; inMonth: boolean }[] => {
const ref = parseISO(iso);
const first = new Date(ref.getFullYear(), ref.getMonth(), 1);
const startIso = startOfWeek(isoOf(first));
return Array.from({ length: 42 }, (_, i) => {
const date = addDays(startIso, i);
return { date, inMonth: monthNum(date) === ref.getMonth() + 1 };
});
};