// krw.mjs
import { test } from 'node:test';
import assert from 'node:assert/strict';
/**
* 숫자를 "1,234,567원" 형태의 문자열로 변환합니다.
* 소수점 이하는 버리며, 숫자가 아니거나 NaN·Infinity 이면 null 을 반환합니다.
*/
export function formatKRW(n) {
// 숫자 타입이 아니거나 유한한 값이 아니면 null
if (typeof n !== 'number' || !Number.isFinite(n)) {
return null;
}
// 소수점 이하 버림 (반올림하지 않음)
const truncated = Math.trunc(n);
// 버림 결과가 0 이면 부호 없이 처리 (-0.5 → "0원")
if (truncated === 0) {
return '0원';
}
const isNegative = truncated < 0;
// 큰 정수도 지수 표기 없이 정확히 문자열로 만들기 위해 BigInt 사용
const digits = BigInt(Math.abs(truncated)).toString();
// 세 자리마다 쉼표 삽입
const withCommas = digits.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return `${isNegative ? '-' : ''}${withCommas}원`;
}
/**
* "2026년 9월 26일" 형태의 문자열을 "2026-09-26" 형식으로 변환합니다.
* 형식이 다르거나 존재하지 않는 날짜이면 null 을 반환합니다.
*/
export function parseKoreanDate(s) {
// 문자열이 아니면 null
if (typeof s !== 'string') {
return null;
}
// 앞뒤 공백 제거 후 형식 검사
const match = s.trim().match(/^(\d{4})년\s+(\d{1,2})월\s+(\d{1,2})일$/);
if (!match) {
return null;
}
const year = Number(match[1]);
const month = Number(match[2]);
const day = Number(match[3]);
// 월 범위 검사
if (month < 1 || month > 12) {
return null;
}
// 윤년 판정
const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
// 월별 일수
const daysInMonth = [31, isLeapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
// 일 범위 검사
if (day < 1 || day > daysInMonth[month - 1]) {
return null;
}
// 두 자리 0 채움
const mm = String(month).padStart(2, '0');
const dd = String(day).padStart(2, '0');
return `${match[1]}-${mm}-${dd}`;
}
// ===== 테스트: formatKRW =====
test('formatKRW: 일반 정수에 쉼표와 "원"을 붙인다', () => {
assert.equal(formatKRW(1234567), '1,234,567원');
assert.equal(formatKRW(0), '0원');
assert.equal(formatKRW(999), '999원');
});
test('formatKRW: 소수는 반올림하지 않고 버린다', () => {
assert.equal(formatKRW(1234.9), '1,234원');
assert.equal(formatKRW(-0.5), '0원');
});
test('formatKRW: 음수는 앞에 "-"를 붙인다', () => {
assert.equal(formatKRW(-1234), '-1,234원');
assert.equal(formatKRW(-1234.9), '-1,234원');
});
test('formatKRW: 숫자가 아니면 null 을 반환한다', () => {
assert.equal(formatKRW('1234'), null);
assert.equal(formatKRW(null), null);
assert.equal(formatKRW(undefined), null);
});
test('formatKRW: NaN·Infinity 이면 null 을 반환한다', () => {
assert.equal(formatKRW(NaN), null);
assert.equal(formatKRW(Infinity), null);
assert.equal(formatKRW(-Infinity), null);
});
// ===== 테스트: parseKoreanDate =====
test('parseKoreanDate: 한 자리 월·일을 두 자리로 채운다', () => {
assert.equal(parseKoreanDate('2026년 9월 26일'), '2026-09-26');
assert.equal(parseKoreanDate('2026년 1월 5일'), '2026-01-05');
});
test('parseKoreanDate: 이미 두 자리인 월·일도 처리한다', () => {
assert.equal(parseKoreanDate('2026년 09월 06일'), '2026-09-06');
});
test('parseKoreanDate: 앞뒤 공백은 무시한다', () => {
assert.equal(parseKoreanDate(' 2026년 9월 26일 '), '2026-09-26');
});
test('parseKoreanDate: 존재하지 않는 날짜는 null 을 반환한다', () => {
assert.equal(parseKoreanDate('2026년 2월 30일'), null);
assert.equal(parseKoreanDate('2026년 13월 1일'), null);
assert.equal(parseKoreanDate('2026년 4월 31일'), null);
assert.equal(parseKoreanDate('2026년 0월 10일'), null);
});
test('parseKoreanDate: 윤년 2월 29일을 구분한다', () => {
assert.equal(parseKoreanDate('2024년 2월 29일'), '2024-02-29');
assert.equal(parseKoreanDate('2025년 2월 29일'), null);
});
test('parseKoreanDate: 형식이 다르면 null 을 반환한다', () => {
assert.equal(parseKoreanDate('2026-09-26'), null);
assert.equal(parseKoreanDate('26년 9월 26일'), null);
assert.equal(parseKoreanDate('2026년 9월'), null);
assert.equal(parseKoreanDate(20260926), null);
});
터미널에서 node --test krw.mjs 명령을 실행하시면 테스트 결과를 확인하실 수 있습니다.