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
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 };
|
|
});
|
|
};
|