kor-lunar
Version:
한국 음력 변환 유틸 / Korean lunar calendar converter
474 lines (464 loc) • 16.8 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var toInt = function (num) {
if (typeof num === "string" || typeof num === "number")
return ~~num;
return num;
};
/**
* 음력 테이블
*
* 9b (17-25): 해당 해의 총 음력 일 수 (예: 384)
*
* 1b ( 16): 윤달의 크기 (0: 29일, 1: 30일)
*
* 4b (12-15): 윤달 위치 (1-12, 윤달이 없으면 0)
*
* 12b ( 0-11): 각 월의 크기 (0: 29일, 1: 30일)
*/
var LUN_TABLE = [
/* 0 1 2 3 4 5 6 7 8 9 */
/*1890*/ 0x3002ab6, 0x2c60daa, 0x3006ee4, 0x2c40ea4, 0x2c40d4a, 0x2fe5555, 0x2c60a97, 0x2c40556, 0x300355d, 0x2c60ad5,
/*1900*/ 0x3008bd2, 0x2c40752, 0x2c60ea5, 0x2fe5b2a, 0x2c4064b, 0x2c60a9b, 0x3014aa6, 0x2c4056a, 0x2c60b59, 0x3002baa,
/*1910*/ 0x2c40752, 0x3006da5, 0x2c40b25, 0x2c40a4b, 0x300595b, 0x2c60aad, 0x2c4056a, 0x30025b5, 0x2c60ba9, 0x3007dd2,
/*1920*/ 0x2c40d92, 0x2c40d25, 0x3005d2d, 0x2c40956, 0x2c402b5, 0x3024add, 0x2c406d4, 0x2c60da9, 0x3002eca, 0x2c40e92,
/*1930*/ 0x2fe66a6, 0x2c40527, 0x2c60a57, 0x3015956, 0x2c60ada, 0x2c406d4, 0x3013751, 0x2c40749, 0x3017b13, 0x2c40a93,
/*1940*/ 0x2c4052b, 0x301651b, 0x2c6096d, 0x2c60b6a, 0x3014da4, 0x2c40ba4, 0x2c40b49, 0x3002d4b, 0x2c40a95, 0x3007aab,
/*1950*/ 0x2c4052d, 0x2c60aad, 0x3015aaa, 0x2c60db2, 0x2c40da4, 0x3013ea1, 0x2c40d4a, 0x3008d95, 0x2c40a96, 0x2c40556,
/*1960*/ 0x3006575, 0x2c60ad5, 0x2c406d2, 0x3004755, 0x2c60ea5, 0x2c40e4a, 0x2fe364e, 0x2c60a9b, 0x3007ad6, 0x2c4056a,
/*1970*/ 0x2c60b59, 0x3005bb2, 0x2c40752, 0x2c40725, 0x3004b2b, 0x2c40a4b, 0x30089ab, 0x2c402ad, 0x2c6056b, 0x30165a9,
/*1980*/ 0x2c60da9, 0x2c40d92, 0x3004d95, 0x2c40d25, 0x300ae4d, 0x2c40a56, 0x2c402b6, 0x3026aed, 0x2c406d4, 0x2c60da9,
/*1990*/ 0x3005ed2, 0x2c40e92, 0x2c40d26, 0x2fe352e, 0x2c60a57, 0x30089b6, 0x2c60b5a, 0x2c406d4, 0x3005769, 0x2c40749,
/*2000*/ 0x2c40693, 0x3004a97, 0x2c4052b, 0x2c60a5b, 0x3002aae, 0x2c4036a, 0x3027dd5, 0x2c40ba4, 0x2c40b49, 0x3005d53,
/*2010*/ 0x2c40a95, 0x2c4052d, 0x301352d, 0x2c60aad, 0x3009baa, 0x2c405d2, 0x2c60da5, 0x3005eaa, 0x2c40d4a, 0x2c40a95,
/*2020*/ 0x3004a9d, 0x2c40556, 0x2c60ab5, 0x3002ad6, 0x2c406d2, 0x3006765, 0x2c60ea5, 0x2c40e4a, 0x2fe5656, 0x2c60c9b,
/*2030*/ 0x2c4055a, 0x300356d, 0x2c60b69, 0x300bf52, 0x2c40752, 0x2c40b25, 0x3016b0b, 0x2c40a4b, 0x2c404ab, 0x30052bb,
/*2040*/ 0x2c6056d, 0x2c60b69, 0x3002daa, 0x2c40d92, 0x3007ea5, 0x2c40d25, 0x2c40a4d, 0x3015a4d, 0x2c402b6, 0x2c605b5,
/*2050*/ 0x00136d1 /* 11월 18일 까지라 데이터 부족 */,
];
var gan = ["갑", "을", "병", "정", "무", "기", "경", "신", "임", "계"];
var ji = ["자", "축", "인", "묘", "진", "사", "오", "미", "신", "유", "술", "해"];
var BASE_YEAR$1 = 1890;
var BASE_MONTH$1 = 1;
var BASE_DAY$1 = 1;
var BASE_VALUE$1 = BASE_YEAR$1 * 10000 + BASE_MONTH$1 * 100 + BASE_DAY$1;
var MAX_YEAR$1 = 2050;
var MAX_MONTH$1 = 11;
var MAX_DAY$1 = 18;
var MAX_VALUE$1 = MAX_YEAR$1 * 10000 + MAX_MONTH$1 * 100 + MAX_DAY$1;
var SMALL_MONTH_DAY = 29;
var BIG_MONTH_DAY = 30;
var totalDaysBeforeYear$1 = {};
var getYearData = function (year) {
year = toInt(year);
return LUN_TABLE[year - BASE_YEAR$1];
};
/**
* 해당 월 (평달)의 일 수를 반환합니다.
* @param year 1890년 ~ 2050년
* @param month 1월 ~ 12월
* @returns 월의 일 수 (29 또는 30)
*/
var getMonthDays$1 = function (year, month) {
month = toInt(month);
var monthType = (getYearData(year) >> (month - 1)) & 0x1;
return monthType === 0 ? SMALL_MONTH_DAY : BIG_MONTH_DAY;
};
/**
* 해당 연도의 윤달을 반환합니다.
* @param year 1890년 ~ 2050년
* @returns 윤달 월 (1월 ~ 12월), 없으면 0
*/
var getLeapMonth = function (year) {
return (getYearData(year) >> 12) & 0xf;
};
/**
* 해당 연도에 윤달이 있는지를 반환합니다.
* @param year 1890년 ~ 2050년
* @return 윤달이 있으면 true
*/
var hasLeapMonth = function (year) {
return getLeapMonth(year) !== 0;
};
/**
* 해당 월이 윤달인지를 반환합니다.
* @param year 1890년 ~ 2050년
* @param month 1월 ~ 12월
* @returns 윤달이면 true
*/
var isLeapMonth = function (year, month) {
month = toInt(month);
return month === getLeapMonth(year);
};
/**
* 해당 월 (윤달)의 일 수를 반환합니다.
* @param year 1890년 ~ 2050년
* @param month 1월 ~ 12월
* @returns 윤달의 일 수 (29 또는 30), 윤달이 아니면 0
*/
var getLeapMonthDays = function (year, month) {
if (!isLeapMonth(year, month))
return 0;
var monthType = (getYearData(year) >> 16) & 0x1;
return monthType === 0 ? SMALL_MONTH_DAY : BIG_MONTH_DAY;
};
/**
* 해당 연도의 총 일 수를 반환합니다.
* @param year 1890년 ~ 2050년
* @return 해당 연도의 총 일 수
*/
var getYearDays$1 = function (year) {
return (getYearData(year) >> 17) & 0x1ff;
};
/**
* 연도별 누적 일 수를 초기에 룩업 테이블로 생성하여
* O(1)으로 누적 일 수를 가져오게 변환함
*/
totalDaysBeforeYear$1[BASE_YEAR$1] = 0;
for (var y$1 = BASE_YEAR$1 + 1; y$1 <= MAX_YEAR$1; y$1++) {
totalDaysBeforeYear$1[y$1] = totalDaysBeforeYear$1[y$1 - 1] + getYearDays$1(y$1 - 1);
}
/**
* 1890년부터 해당 연도 전까지의 누적 일 수를 반환합니다.
* @param year 1890년 ~ 2050년
* @return 해당 연도 전까지의 누적 일 수
*/
var getTotalDaysBeforeYear$1 = function (year) {
year = toInt(year);
var days = totalDaysBeforeYear$1[year];
return days;
};
/**
* 해당 연도 내에서 해당 월 (및 윤달 포함) 전까지의 누적 일 수를 반환합니다.
* @param year 1890년 ~ 2050년
* @param month 1월 ~ 12월
* @param isLeapMonth 대상이 윤달이면 true
* @returns 해당 연도 내, 해당 월 전까지의 누적 일 수
*/
var getTotalDaysBeforeMonth$1 = function (year, month, isLeapMonth) {
month = toInt(month);
var days = 0;
// 해당 월 전까지 윤달을 포함하여 누적
for (var m = 1; m < month; m++) {
days += getMonthDays$1(year, m);
if (m === getLeapMonth(year)) {
days += getLeapMonthDays(year, m);
}
}
// 대상이 윤달이면, 앞에 있는 평달을 누적
var leapMonth = getLeapMonth(year);
if (isLeapMonth && leapMonth === month) {
days += getMonthDays$1(year, month);
}
return days;
};
/**
* 1890년부터 해당 연도, 월, 일 (및 윤달 포함) 까지의 누적 일 수를 반환합니다.
* @param year 1890년 ~ 2050년
* @param month 1월 ~ 12월
* @param day 일자
* @param isLeapMonth 대상이 윤달이면 true
* @returns 총 누적 일 수
*/
var getTotalDays$1 = function (year, month, day, isLeapMonth) {
day = toInt(day);
var days = getTotalDaysBeforeYear$1(year) + getTotalDaysBeforeMonth$1(year, month, isLeapMonth) + day;
return days;
};
var getSecha = function (year) {
year = toInt(year);
var g = gan[(year + 6) % gan.length];
var j = ji[(year + 8) % ji.length];
return g + j;
};
var getWolgeon = function (year, month) {
year = toInt(year);
month = toInt(month);
var g = gan[(year * 2 + month + 3) % gan.length];
var j = ji[(month + 1) % ji.length];
return g + j;
};
var getIljinByJulianDay = function (julianDay) {
julianDay = toInt(julianDay);
var g = gan[(julianDay - 1) % gan.length];
var j = ji[(julianDay + 1) % ji.length];
return g + j;
};
var getIljin = function (year, month, day, isLeapMonth) {
var days = getTotalDays$1(year, month, day, isLeapMonth);
return getIljinByJulianDay(days - 1);
};
/**
* 날짜가 지원하는 범위 내에 있는지를 반환합니다.
* 날짜의 유효성 (존재 여부)은 검사하지 않습니다.
* @returns 날짜가 범위 내에 있으면 true
*/
var isDateInRange$1 = function (year, month, day) {
year = toInt(year);
month = toInt(month);
day = toInt(day);
var value = year * 10000 + month * 100 + day;
return value >= BASE_VALUE$1 && value <= MAX_VALUE$1;
};
/**
* 실제로 존재하는 유효한 날짜인지를 반환합니다.
* @returns 유효한 날짜이면 true
*/
var isValidDate$1 = function (year, month, day, isLeapMonth) {
year = toInt(year);
month = toInt(month);
day = toInt(day);
if (year < BASE_YEAR$1 || year > MAX_YEAR$1)
return false;
if (year === BASE_YEAR$1) {
if (month < BASE_MONTH$1)
return false;
if (month === BASE_MONTH$1 && day < BASE_DAY$1)
return false;
}
if (year === MAX_YEAR$1) {
if (month > MAX_MONTH$1)
return false;
if (month === MAX_MONTH$1 && day > MAX_DAY$1)
return false;
}
if (month < 1 || month > 12)
return false;
if (day < 1)
return false;
var endDay = isLeapMonth ? getLeapMonthDays(year, month) : getMonthDays$1(year, month);
return day <= endDay;
};
var LunarData = /*#__PURE__*/Object.freeze({
__proto__: null,
BASE_YEAR: BASE_YEAR$1,
BASE_MONTH: BASE_MONTH$1,
BASE_DAY: BASE_DAY$1,
BASE_VALUE: BASE_VALUE$1,
MAX_YEAR: MAX_YEAR$1,
MAX_MONTH: MAX_MONTH$1,
MAX_DAY: MAX_DAY$1,
MAX_VALUE: MAX_VALUE$1,
getMonthDays: getMonthDays$1,
getLeapMonth: getLeapMonth,
hasLeapMonth: hasLeapMonth,
isLeapMonth: isLeapMonth,
getLeapMonthDays: getLeapMonthDays,
getYearDays: getYearDays$1,
getTotalDaysBeforeYear: getTotalDaysBeforeYear$1,
getTotalDaysBeforeMonth: getTotalDaysBeforeMonth$1,
getTotalDays: getTotalDays$1,
getSecha: getSecha,
getWolgeon: getWolgeon,
getIljinByJulianDay: getIljinByJulianDay,
getIljin: getIljin,
isDateInRange: isDateInRange$1,
isValidDate: isValidDate$1
});
var MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
var LEAP_FEBRUARY_DAY = 29;
var YEAR_DAY = 365;
var LEAP_YEAR_DAY = 366;
var BASE_YEAR = 1890;
var BASE_MONTH = 1;
var BASE_DAY = 21;
var BASE_VALUE = BASE_YEAR * 10000 + BASE_MONTH * 100 + BASE_DAY;
var MAX_YEAR = 2050;
var MAX_MONTH = 12;
var MAX_DAY = 31;
var MAX_VALUE = MAX_YEAR * 10000 + MAX_MONTH * 100 + MAX_DAY;
var totalDaysBeforeYear = {};
var isLeapYear = function (year) {
year = toInt(year);
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
};
var getMonthDays = function (year, month) {
month = toInt(month);
var day = month === 2 && isLeapYear(year) ? LEAP_FEBRUARY_DAY : MONTH_DAYS[month - 1];
return day;
};
var getYearDays = function (year) {
var day = isLeapYear(year) ? LEAP_YEAR_DAY : YEAR_DAY;
return day;
};
/**
* 연도별 누적 일 수를 초기에 룩업 테이블로 생성하여
* O(1)으로 누적 일 수를 가져오게 변환함
*/
totalDaysBeforeYear[BASE_YEAR] = 0;
for (var y = BASE_YEAR + 1; y <= MAX_YEAR; y++) {
totalDaysBeforeYear[y] = totalDaysBeforeYear[y - 1] + getYearDays(y - 1);
}
var getTotalDaysBeforeYear = function (year) {
year = toInt(year);
var day = totalDaysBeforeYear[year];
return day;
};
var getTotalDaysBeforeMonth = function (year, month) {
var day = 0;
for (var m = 1; m < month; m++) {
day += getMonthDays(year, m);
}
return day;
};
var getTotalDays = function (year, month, day) {
var days = getTotalDaysBeforeYear(year) + getTotalDaysBeforeMonth(year, month) + day;
return days;
};
/**
* 날짜가 지원하는 범위 내에 있는지를 반환합니다.
* 날짜의 유효성 (존재 여부)은 검사하지 않습니다.
* @returns 날짜가 범위 내에 있으면 true
*/
var isDateInRange = function (year, month, day) {
year = toInt(year);
month = toInt(month);
day = toInt(day);
var value = year * 10000 + month * 100 + day;
return value >= BASE_VALUE && value <= MAX_VALUE;
};
/**
* 실제로 존재하는 유효한 날짜인지를 반환합니다.
* @returns 유효한 날짜이면 true
*/
var isValidDate = function (year, month, day) {
year = toInt(year);
month = toInt(month);
day = toInt(day);
if (year < BASE_YEAR || year > MAX_YEAR)
return false;
if (year === BASE_YEAR) {
if (month < BASE_MONTH)
return false;
if (month === BASE_MONTH && day < BASE_DAY)
return false;
}
if (year === MAX_YEAR) {
if (month > MAX_MONTH)
return false;
if (month === MAX_MONTH && day > MAX_DAY)
return false;
}
if (month < 1 || month > 12)
return false;
if (day < 1)
return false;
var endDay = getMonthDays(year, month);
return day <= endDay;
};
var SolarData = /*#__PURE__*/Object.freeze({
__proto__: null,
BASE_YEAR: BASE_YEAR,
BASE_MONTH: BASE_MONTH,
BASE_DAY: BASE_DAY,
BASE_VALUE: BASE_VALUE,
MAX_YEAR: MAX_YEAR,
MAX_MONTH: MAX_MONTH,
MAX_DAY: MAX_DAY,
MAX_VALUE: MAX_VALUE,
isLeapYear: isLeapYear,
getMonthDays: getMonthDays,
getYearDays: getYearDays,
getTotalDaysBeforeYear: getTotalDaysBeforeYear,
getTotalDaysBeforeMonth: getTotalDaysBeforeMonth,
getTotalDays: getTotalDays,
isDateInRange: isDateInRange,
isValidDate: isValidDate
});
var SOLAR_LUNAR_DAY_DIFF = 20;
var JULIAN_DAY_DIFF = 2411389;
/**
* 양력을 음력으로 변환합니다.
* 양력 지원 날짜 범위: 1890년 1월 21일 ~ 2050년 12월 31일
* @param solYear 양력 연도
* @param solMonth 양력 월
* @param solDay 양력 일
* @returns 음력 날짜
*/
var toLunar = function (solYear, solMonth, solDay) {
solYear = toInt(solYear);
solMonth = toInt(solMonth);
solDay = toInt(solDay);
if (!isDateInRange(solYear, solMonth, solDay)) {
throw new RangeError("\uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 \uB0A0\uC9DC\uC785\uB2C8\uB2E4. \uC785\uB825\uD55C \uB0A0\uC9DC: ".concat(solYear, "-").concat(solMonth, "-").concat(solDay));
}
var year = Math.min(solYear, MAX_YEAR$1);
var month = solYear > MAX_YEAR$1 ? MAX_MONTH$1 : solMonth;
var day = 1;
var lunTotalDays = getTotalDays$1(year, month, day, true);
var solTotalDays = getTotalDays(solYear, solMonth, solDay);
var diffDays = solTotalDays - SOLAR_LUNAR_DAY_DIFF - lunTotalDays;
day += diffDays;
var day2 = solTotalDays - SOLAR_LUNAR_DAY_DIFF;
var julianDay = JULIAN_DAY_DIFF + day2 - 1;
var dayOfWeek = (day2 + 1) % 7;
var isLeapMonth = month === getLeapMonth(year);
var monthDays;
while (day < 1) {
if (isLeapMonth) {
isLeapMonth = false;
}
else {
month--;
if (month === 0) {
month = 12;
year--;
}
isLeapMonth = month === getLeapMonth(year);
}
monthDays = isLeapMonth ? getLeapMonthDays(year, month) : getMonthDays$1(year, month);
day += monthDays;
}
return {
year: year,
month: month,
day: day,
isLeapMonth: isLeapMonth,
secha: getSecha(year),
wolgeon: isLeapMonth ? "" : getWolgeon(year, month),
iljin: getIljinByJulianDay(julianDay),
julianDay: julianDay,
dayOfWeek: dayOfWeek,
};
};
/**
* 음력을 양력으로 변환합니다.
* 음력 지원 날짜 범위: 1890년 1월 1일 ~ 2050년 11월 18일
* @param lunYear 음력 연도
* @param lunMonth 음력 월
* @param lunDay 음력 일
* @param isLeapMonth 음력 윤달 여부, 윤달이면 true
* @returns 양력 날짜
*/
var toSolar = function (lunYear, lunMonth, lunDay, isLeapMonth) {
lunYear = toInt(lunYear);
lunMonth = toInt(lunMonth);
lunDay = toInt(lunDay);
if (!isDateInRange$1(lunYear, lunMonth, lunDay)) {
throw new RangeError("\uC9C0\uC6D0\uB418\uC9C0 \uC54A\uB294 \uB0A0\uC9DC\uC785\uB2C8\uB2E4. \uC785\uB825\uD55C \uB0A0\uC9DC: ".concat(lunYear, "-").concat(lunMonth, "-").concat(lunDay));
}
var lunTotalDays = getTotalDays$1(lunYear, lunMonth, lunDay, isLeapMonth);
var solTotalDays = getTotalDays(lunYear, lunMonth, lunDay);
var diffDays = lunTotalDays - (solTotalDays - SOLAR_LUNAR_DAY_DIFF);
var year = lunYear;
var month = lunMonth;
var day = lunDay + diffDays;
var monthDays = getMonthDays(year, month);
while (day > monthDays) {
day -= monthDays;
month++;
if (month > 12) {
month = 1;
year++;
}
monthDays = getMonthDays(year, month);
}
return { year: year, month: month, day: day };
};
var korLunar = { toLunar: toLunar, toSolar: toSolar, LunarData: LunarData, SolarData: SolarData };
exports.LunarData = LunarData;
exports.SolarData = SolarData;
exports["default"] = korLunar;
exports.toLunar = toLunar;
exports.toSolar = toSolar;
//# sourceMappingURL=kor-lunar.js.map