@zwoninstitute/il-gaemi
Version:
Temporal 기반 Typescript 날짜/시간 유틸리티 패키지
514 lines (505 loc) • 18.5 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
DEFAULT_TIMEZONE: () => DEFAULT_TIMEZONE,
DateError: () => DateError,
IncompatibleOperationError: () => IncompatibleOperationError,
InvalidDateError: () => InvalidDateError,
InvalidDateFormatError: () => InvalidDateFormatError,
MissingParameterError: () => MissingParameterError,
Temporal: () => import_polyfill5.Temporal,
UnsupportedFormatTypeError: () => UnsupportedFormatTypeError,
convertToZonedDateTime: () => convertToZonedDateTime,
format: () => format,
formatKorean: () => formatKorean,
formatRelative: () => formatRelative,
fromUTC: () => fromUTC,
getDate: () => getDate,
getDateTime: () => getDateTime,
getDateTimeUTC: () => getDateTimeUTC,
getDateUTC: () => getDateUTC,
getNextWorkday: () => getNextWorkday,
getNow: () => getNow,
getNowUTC: () => getNowUTC,
getPreviousWorkday: () => getPreviousWorkday,
getTime: () => getTime,
getTimeZoneOffset: () => getTimeZoneOffset,
getWeekDay: () => getWeekDay,
getWeekNum: () => getWeekNum,
isWorkday: () => isWorkday,
toUTC: () => toUTC
});
module.exports = __toCommonJS(index_exports);
// src/date/index.ts
var import_polyfill = require("@js-temporal/polyfill");
var DAY_OFF_ENV_KEY = "IL_GAEMI_DAY_OFFS";
var KOREAN_WEEKDAY_MAP = {
\uC6D4: 1,
\uD654: 2,
\uC218: 3,
\uBAA9: 4,
\uAE08: 5,
\uD1A0: 6,
\uC77C: 7
};
var FALLBACK_DAY_OFFS = [KOREAN_WEEKDAY_MAP["\uD1A0"]];
var getEnv = () => {
if (typeof process === "undefined" || typeof process.env === "undefined") {
return null;
}
return process.env;
};
function parseDayTokens(tokens) {
const parsed = tokens.map((token) => token.replace(/요일$/u, "").trim()).map((token) => token ? KOREAN_WEEKDAY_MAP[token] : void 0).filter((value) => typeof value === "number");
if (!parsed.length) {
return [...FALLBACK_DAY_OFFS];
}
return Array.from(new Set(parsed));
}
function getConfiguredDayOffWeekdays() {
const env = getEnv();
if (!env) {
return [...FALLBACK_DAY_OFFS];
}
const raw = env[DAY_OFF_ENV_KEY];
if (!raw) {
return [...FALLBACK_DAY_OFFS];
}
const tokens = raw.split(",").map((token) => token.trim()).filter((token) => token.length > 0);
if (!tokens.length) {
return [...FALLBACK_DAY_OFFS];
}
return parseDayTokens(tokens);
}
var DEFAULT_DAY_OFF_WEEKDAYS = getConfiguredDayOffWeekdays();
function isWorkday(date, holidayList = [], dayOffWeekdays = DEFAULT_DAY_OFF_WEEKDAYS) {
const plainDate = typeof date === "string" ? import_polyfill.Temporal.PlainDate.from(date) : date;
const dayOfWeek = plainDate.dayOfWeek;
if (dayOffWeekdays.includes(dayOfWeek)) {
return false;
}
const dateString = plainDate.toString();
const isHoliday = holidayList.some((holiday) => {
if (holiday.recurring) {
const holidayDate = import_polyfill.Temporal.PlainDate.from(holiday.date);
return holidayDate.month === plainDate.month && holidayDate.day === plainDate.day;
}
return holiday.date === dateString;
});
return !isHoliday;
}
function getWeekDay(date) {
const plainDate = typeof date === "string" ? import_polyfill.Temporal.PlainDate.from(date) : date;
return plainDate.dayOfWeek;
}
function getWeekNum(date) {
const plainDate = typeof date === "string" ? import_polyfill.Temporal.PlainDate.from(date) : date;
const dayOfWeek = plainDate.dayOfWeek;
const daysFromMonday = dayOfWeek - 1;
const mondayOfWeek = plainDate.subtract({ days: daysFromMonday });
const targetMonth = mondayOfWeek.month;
const targetYear = mondayOfWeek.year;
const firstDayOfMonth = mondayOfWeek.with({ day: 1 });
const firstDayWeekday = firstDayOfMonth.dayOfWeek;
const daysToFirstMonday = firstDayWeekday === 1 ? 0 : 8 - firstDayWeekday;
const firstMondayOfMonth = firstDayOfMonth.add({ days: daysToFirstMonday });
const daysDiff = mondayOfWeek.since(firstMondayOfMonth).days;
const weekNum = Math.floor(daysDiff / 7) + 1;
return { month: targetMonth, year: targetYear, weekNum };
}
function getNextWorkday(date, holidayList = [], dayOffWeekdays = DEFAULT_DAY_OFF_WEEKDAYS) {
let currentDate = typeof date === "string" ? import_polyfill.Temporal.PlainDate.from(date) : date;
do {
currentDate = currentDate.add({ days: 1 });
} while (!isWorkday(currentDate, holidayList, dayOffWeekdays));
return currentDate;
}
function getPreviousWorkday(date, holidayList = [], dayOffWeekdays = DEFAULT_DAY_OFF_WEEKDAYS) {
let currentDate = typeof date === "string" ? import_polyfill.Temporal.PlainDate.from(date) : date;
do {
currentDate = currentDate.subtract({ days: 1 });
} while (!isWorkday(currentDate, holidayList, dayOffWeekdays));
return currentDate;
}
// src/timezone/index.ts
var import_polyfill3 = require("@js-temporal/polyfill");
// src/utils/dateParser.ts
var import_polyfill2 = require("@js-temporal/polyfill");
// src/errors.ts
var DateError = class extends Error {
constructor(message) {
super(message);
this.name = "DateError";
}
};
var InvalidDateFormatError = class extends DateError {
constructor(dateString, supportedFormats) {
const message = supportedFormats ? `Unsupported date format: ${dateString}. Supported formats: ${supportedFormats.join(", ")}` : `Unsupported date format: ${dateString}`;
super(message);
this.name = "InvalidDateFormatError";
}
};
var InvalidDateError = class extends DateError {
constructor(dateString) {
super(`Invalid date: ${dateString}`);
this.name = "InvalidDateError";
}
};
var UnsupportedFormatTypeError = class extends DateError {
constructor(formatType, supportedTypes) {
const message = supportedTypes ? `Unsupported format type: ${formatType}. Supported types: ${supportedTypes.join(", ")}` : `Unsupported format type: ${formatType}`;
super(message);
this.name = "UnsupportedFormatTypeError";
}
};
var MissingParameterError = class extends DateError {
constructor(parameterName) {
super(`Missing required parameter: ${parameterName}`);
this.name = "MissingParameterError";
}
};
var IncompatibleOperationError = class extends DateError {
constructor(operation, reason) {
super(`Incompatible operation: ${operation}. Reason: ${reason}`);
this.name = "IncompatibleOperationError";
}
};
// src/utils/dateParser.ts
function parseFlexibleDateString(dateString) {
const trimmed = dateString.trim();
if (trimmed.includes("T") && (trimmed.includes("+") || trimmed.includes("Z") || trimmed.includes("["))) {
try {
return import_polyfill2.Temporal.ZonedDateTime.from(trimmed);
} catch {
throw new InvalidDateError(trimmed);
}
}
if (trimmed.includes("T")) {
try {
return import_polyfill2.Temporal.PlainDateTime.from(trimmed);
} catch {
throw new InvalidDateError(trimmed);
}
}
if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(trimmed)) {
try {
return import_polyfill2.Temporal.PlainDate.from(trimmed);
} catch {
throw new InvalidDateError(trimmed);
}
}
const koreanMatch = trimmed.match(/(\d{4})년\s*(\d{1,2})월\s*(\d{1,2})일/);
if (koreanMatch) {
const [, year, month, day] = koreanMatch;
try {
return import_polyfill2.Temporal.PlainDate.from({
year: parseInt(year),
month: parseInt(month),
day: parseInt(day)
});
} catch {
throw new InvalidDateError(trimmed);
}
}
const dotMatch = trimmed.match(/^(\d{2,4})\.(\d{1,2})\.(\d{1,2})$/);
if (dotMatch) {
let [, year, month, day] = dotMatch;
if (year.length === 2) {
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
const century = Math.floor(currentYear / 100) * 100;
const twoDigitYear = parseInt(year);
year = (century + twoDigitYear).toString();
}
try {
return import_polyfill2.Temporal.PlainDate.from({
year: parseInt(year),
month: parseInt(month),
day: parseInt(day)
});
} catch {
throw new InvalidDateError(trimmed);
}
}
const slashMatch = trimmed.match(/^(\d{1,4})\/(\d{1,2})\/(\d{1,4})$/);
if (slashMatch) {
let [, first, second, third] = slashMatch;
let year, month, day;
if (first.length === 4) {
year = first;
month = second;
day = third;
} else if (third.length <= 2) {
month = first;
day = second;
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
const century = Math.floor(currentYear / 100) * 100;
year = (century + parseInt(third)).toString();
} else {
month = first;
day = second;
year = third;
}
try {
return import_polyfill2.Temporal.PlainDate.from({
year: parseInt(year),
month: parseInt(month),
day: parseInt(day)
});
} catch {
throw new InvalidDateError(trimmed);
}
}
const compactMatch = trimmed.match(/^(\d{4})(\d{2})(\d{2})$/);
if (compactMatch) {
const [, year, month, day] = compactMatch;
try {
return import_polyfill2.Temporal.PlainDate.from({
year: parseInt(year),
month: parseInt(month),
day: parseInt(day)
});
} catch {
throw new InvalidDateError(trimmed);
}
}
const dashMatch = trimmed.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/);
if (dashMatch) {
const [, year, month, day] = dashMatch;
try {
return import_polyfill2.Temporal.PlainDate.from({
year: parseInt(year),
month: parseInt(month),
day: parseInt(day)
});
} catch {
throw new InvalidDateError(trimmed);
}
}
try {
return import_polyfill2.Temporal.PlainDateTime.from(trimmed);
} catch {
try {
return import_polyfill2.Temporal.PlainDate.from(trimmed);
} catch {
try {
return import_polyfill2.Temporal.ZonedDateTime.from(trimmed);
} catch {
throw new InvalidDateFormatError(trimmed, [
"ISO 8601 (YYYY-MM-DD)",
"ISO DateTime (YYYY-MM-DDTHH:mm:ss)",
"ISO with timezone (YYYY-MM-DDTHH:mm:ss+09:00[Asia/Seoul])",
"Korean format (2024\uB144 1\uC6D4 15\uC77C)",
"Dot separated (2024.01.15)",
"Slash separated (2024/01/15)",
"Compact format (20240115)"
]);
}
}
}
}
// src/timezone/index.ts
var DEFAULT_TIMEZONE = "Asia/Seoul";
function getNow() {
return import_polyfill3.Temporal.Now.zonedDateTimeISO(DEFAULT_TIMEZONE);
}
function getDate(year, month, day) {
return import_polyfill3.Temporal.PlainDate.from({ year, month, day }).toZonedDateTime(DEFAULT_TIMEZONE);
}
function getDateTime(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond) {
return import_polyfill3.Temporal.PlainDateTime.from({
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond
}).toZonedDateTime(DEFAULT_TIMEZONE);
}
function getDateTimeUTC(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond) {
return import_polyfill3.Temporal.PlainDateTime.from({
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond
}).toZonedDateTime("UTC");
}
function getDateUTC(year, month, day) {
return import_polyfill3.Temporal.PlainDate.from({ year, month, day }).toZonedDateTime("UTC");
}
function getTime(hour, minute, second, millisecond, microsecond, nanosecond) {
return import_polyfill3.Temporal.PlainTime.from({ hour, minute, second, millisecond, microsecond, nanosecond });
}
function getNowUTC() {
return import_polyfill3.Temporal.Now.zonedDateTimeISO("UTC");
}
function convertToZonedDateTime(date, timeZone = DEFAULT_TIMEZONE) {
const resolved = typeof date === "string" ? parseFlexibleDateString(date) : date;
if (resolved instanceof import_polyfill3.Temporal.ZonedDateTime) {
return resolved.withTimeZone(timeZone);
}
if (resolved instanceof import_polyfill3.Temporal.PlainDateTime) {
return resolved.toZonedDateTime(timeZone);
}
const plainDateTime = resolved.toPlainDateTime(import_polyfill3.Temporal.PlainTime.from("00:00:00"));
return plainDateTime.toZonedDateTime(timeZone);
}
function toUTC(zonedDateTime) {
return zonedDateTime.withTimeZone("UTC");
}
function fromUTC(utcDateTime, timeZone = DEFAULT_TIMEZONE) {
return utcDateTime.withTimeZone(timeZone);
}
function getTimeZoneOffset(fromTimeZone, toTimeZone, referenceDate) {
const baseDate = referenceDate || getNow();
const fromZoned = baseDate.withTimeZone(fromTimeZone);
const toZoned = baseDate.withTimeZone(toTimeZone);
const fromOffset = fromZoned.offsetNanoseconds;
const toOffset = toZoned.offsetNanoseconds;
return (toOffset - fromOffset) / (1e3 * 1e3 * 1e3 * 60 * 60);
}
// src/format/index.ts
var import_polyfill4 = require("@js-temporal/polyfill");
function format(date, type = "datetime", formatString) {
let temporalDate;
if (typeof date === "string") {
temporalDate = parseFlexibleDateString(date);
} else {
temporalDate = date;
}
switch (type) {
case "date":
if (temporalDate instanceof import_polyfill4.Temporal.PlainDate) {
return temporalDate.toString();
}
return temporalDate.toPlainDate().toString();
case "time":
if (temporalDate instanceof import_polyfill4.Temporal.PlainDate) {
throw new IncompatibleOperationError("formatting as time", "PlainDate does not contain time information");
}
return temporalDate.toPlainTime().toString();
case "datetime":
if (temporalDate instanceof import_polyfill4.Temporal.PlainDate) {
return `${temporalDate.toString()} 00:00:00`;
}
const plainDateTime = temporalDate instanceof import_polyfill4.Temporal.ZonedDateTime ? temporalDate.toPlainDateTime() : temporalDate;
return plainDateTime.toString().replace("T", " ");
case "iso":
if (temporalDate instanceof import_polyfill4.Temporal.ZonedDateTime) {
return temporalDate.toString();
}
if (temporalDate instanceof import_polyfill4.Temporal.PlainDateTime) {
return temporalDate.toString();
}
return temporalDate.toString();
case "custom":
if (!formatString) {
throw new MissingParameterError("formatString");
}
return formatCustom(temporalDate, formatString);
default:
throw new UnsupportedFormatTypeError(type, ["date", "time", "datetime", "iso", "custom"]);
}
}
function formatCustom(date, formatString) {
let result = formatString;
const plainDate = date instanceof import_polyfill4.Temporal.PlainDate ? date : date.toPlainDate();
const plainTime = date instanceof import_polyfill4.Temporal.PlainDate ? null : date.toPlainTime();
result = result.replace(/YYYY/g, plainDate.year.toString().padStart(4, "0"));
result = result.replace(/YY/g, (plainDate.year % 100).toString().padStart(2, "0"));
result = result.replace(/MM/g, plainDate.month.toString().padStart(2, "0"));
result = result.replace(/M/g, plainDate.month.toString());
result = result.replace(/DD/g, plainDate.day.toString().padStart(2, "0"));
result = result.replace(/D/g, plainDate.day.toString());
if (plainTime) {
result = result.replace(/HH/g, plainTime.hour.toString().padStart(2, "0"));
result = result.replace(/H/g, plainTime.hour.toString());
result = result.replace(/mm/g, plainTime.minute.toString().padStart(2, "0"));
result = result.replace(/m/g, plainTime.minute.toString());
result = result.replace(/ss/g, plainTime.second.toString().padStart(2, "0"));
result = result.replace(/s/g, plainTime.second.toString());
}
return result;
}
function formatKorean(date) {
return format(date, "custom", "YYYY\uB144 M\uC6D4 D\uC77C");
}
function formatRelative(date, baseDate) {
const targetDate = typeof date === "string" ? (() => {
const parsed = parseFlexibleDateString(date);
return parsed instanceof import_polyfill4.Temporal.ZonedDateTime ? parsed : parsed instanceof import_polyfill4.Temporal.PlainDateTime ? parsed.toZonedDateTime("Asia/Seoul") : parsed.toPlainDateTime(import_polyfill4.Temporal.PlainTime.from("00:00:00")).toZonedDateTime("Asia/Seoul");
})() : date instanceof import_polyfill4.Temporal.ZonedDateTime ? date : date instanceof import_polyfill4.Temporal.PlainDateTime ? date.toZonedDateTime("Asia/Seoul") : date.toPlainDateTime(import_polyfill4.Temporal.PlainTime.from("00:00:00")).toZonedDateTime("Asia/Seoul");
const base = baseDate || import_polyfill4.Temporal.Now.zonedDateTimeISO("Asia/Seoul");
const duration = targetDate.since(base, { largestUnit: "day" });
const days = duration.days;
const hours = duration.hours;
const minutes = duration.minutes;
if (Math.abs(days) >= 1) {
return days > 0 ? `${days}\uC77C \uD6C4` : `${Math.abs(days)}\uC77C \uC804`;
}
if (Math.abs(hours) >= 1) {
return hours > 0 ? `${hours}\uC2DC\uAC04 \uD6C4` : `${Math.abs(hours)}\uC2DC\uAC04 \uC804`;
}
if (Math.abs(minutes) >= 1) {
return minutes > 0 ? `${minutes}\uBD84 \uD6C4` : `${Math.abs(minutes)}\uBD84 \uC804`;
}
return "\uBC29\uAE08 \uC804";
}
// src/index.ts
var import_polyfill5 = require("@js-temporal/polyfill");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
DEFAULT_TIMEZONE,
DateError,
IncompatibleOperationError,
InvalidDateError,
InvalidDateFormatError,
MissingParameterError,
Temporal,
UnsupportedFormatTypeError,
convertToZonedDateTime,
format,
formatKorean,
formatRelative,
fromUTC,
getDate,
getDateTime,
getDateTimeUTC,
getDateUTC,
getNextWorkday,
getNow,
getNowUTC,
getPreviousWorkday,
getTime,
getTimeZoneOffset,
getWeekDay,
getWeekNum,
isWorkday,
toUTC
});
//# sourceMappingURL=index.js.map