/** * 24절기 계산 * 사주 계산에서 월주는 절기를 기준으로 합니다. */ // 24절기 (입춘부터 시작) export const SOLAR_TERMS = [ '입춘', '우수', '경칩', '춘분', '청명', '곡우', '입하', '소만', '망종', '하지', '소서', '대서', '입추', '처서', '백로', '추분', '한로', '상강', '입동', '소설', '대설', '동지', '소한', '대한' ] as const; // 월 절기 (홀수 인덱스: 입X, 짝수 인덱스: X분/X지) export const MONTH_SOLAR_TERMS = [ '입춘', // 1월 (인월) '경칩', // 2월 (묘월) '청명', // 3월 (진월) '입하', // 4월 (사월) '망종', // 5월 (오월) '소서', // 6월 (미월) '입추', // 7월 (신월) '백로', // 8월 (유월) '한로', // 9월 (술월) '입동', // 10월 (해월) '대설', // 11월 (자월) '소한', // 12월 (축월) ] as const; interface SolarTermDate { year: number; month: number; day: number; hour: number; minute: number; } /** * 간단한 절기 계산 (근사치) * 실제로는 천문 계산이 필요하지만, 여기서는 근사값 사용 * * 절기는 매년 비슷한 시기에 오지만 정확한 시간은 천문학적 계산 필요 */ export function getSolarTermDate(year: number, termIndex: number): SolarTermDate { // 절기 기준일 (대략적인 날짜) // 입춘은 대략 2월 4일경, 각 절기는 약 15일 간격 const baseMonth = [ 2, 2, 3, 3, 4, 4, // 입춘~곡우 5, 5, 6, 6, 7, 7, // 입하~대서 8, 8, 9, 9, 10, 10, // 입추~상강 11, 11, 12, 12, 1, 1 // 입동~대한 ]; const baseDay = [ 4, 19, 5, 20, 4, 20, // 입춘~곡우 (2월 4일, 2월 19일...) 5, 21, 6, 21, 7, 23, // 입하~대서 7, 23, 8, 23, 8, 23, // 입추~상강 7, 22, 7, 22, 5, 20 // 입동~대한 ]; let month = baseMonth[termIndex]; let day = baseDay[termIndex]; // 대한과 소한은 다음 해 1월이므로 조정 if (termIndex >= 22) { // 이미 1월로 설정되어 있음 } return { year, month, day, hour: 0, minute: 0 }; } /** * 주어진 날짜가 어느 절기 이후인지 확인 * @param year 년 * @param month 월 * @param day 일 * @returns 절기 인덱스 (0~23) */ export function getCurrentSolarTerm(year: number, month: number, day: number): number { const date = new Date(year, month - 1, day); const dateValue = date.getTime(); // 각 절기 날짜 확인 for (let i = 23; i >= 0; i--) { const termDate = getSolarTermDate(year, i); let termYear = termDate.year; let termMonth = termDate.month; // 대한, 소한은 이전 해 처리 if (i >= 22 && month >= 2) { termYear = year; } else if (i >= 22) { termYear = year - 1; } const term = new Date(termYear, termMonth - 1, termDate.day); if (dateValue >= term.getTime()) { return i; } } // 입춘 이전이면 전년도 대한 이후 return 23; } /** * 절기 기준 월주 지지 인덱스 계산 * @param year 년 * @param month 월 * @param day 일 * @returns 지지 인덱스 (0: 자, 1: 축, 2: 인, ...) */ export function getSolarTermMonthBranch(year: number, month: number, day: number): number { const termIndex = getCurrentSolarTerm(year, month, day); // 절기 인덱스를 월로 변환 // 입춘(0) -> 인월(2) // 경칩(2) -> 묘월(3) // 청명(4) -> 진월(4) // ... const monthBranches = [ 2, // 입춘 -> 인월 2, // 우수 -> 인월 3, // 경칩 -> 묘월 3, // 춘분 -> 묘월 4, // 청명 -> 진월 4, // 곡우 -> 진월 5, // 입하 -> 사월 5, // 소만 -> 사월 6, // 망종 -> 오월 6, // 하지 -> 오월 7, // 소서 -> 미월 7, // 대서 -> 미월 8, // 입추 -> 신월 8, // 처서 -> 신월 9, // 백로 -> 유월 9, // 추분 -> 유월 10, // 한로 -> 술월 10, // 상강 -> 술월 11, // 입동 -> 해월 11, // 소설 -> 해월 0, // 대설 -> 자월 0, // 동지 -> 자월 1, // 소한 -> 축월 1, // 대한 -> 축월 ]; return monthBranches[termIndex]; } /** * 절기명 가져오기 */ export function getSolarTermName(termIndex: number): string { return SOLAR_TERMS[termIndex]; } /** * 다음 절기까지 남은 일수 계산 */ export function getDaysToNextSolarTerm(year: number, month: number, day: number): number { const currentDate = new Date(year, month - 1, day); const currentTerm = getCurrentSolarTerm(year, month, day); const nextTermIndex = (currentTerm + 1) % 24; let nextYear = year; if (currentTerm === 23) { nextYear = year + 1; } const nextTerm = getSolarTermDate(nextYear, nextTermIndex); const nextDate = new Date(nextTerm.year, nextTerm.month - 1, nextTerm.day); const diffTime = nextDate.getTime() - currentDate.getTime(); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); return diffDays; }