From 5ec31cee4112ffebbde7fb576e8e6bd26cfd252c Mon Sep 17 00:00:00 2001 From: Msystech Date: Wed, 19 Aug 2026 12:03:18 +0900 Subject: [PATCH] =?UTF-8?q?=EB=82=98=EC=9D=B4=20=EA=B0=9C=EC=9B=94=C2=B7?= =?UTF-8?q?=EC=9D=BC=20=EA=B3=84=EC=82=B0=20=EB=91=90=20=EC=A2=85=EC=9D=84?= =?UTF-8?q?=20=EC=88=9C=EC=88=98=20=ED=95=A8=EC=88=98=EB=A1=9C=20=EC=98=AE?= =?UTF-8?q?=EA=B8=B4=EB=8B=A4=20=E2=80=94=20=EB=A0=88=EA=B1=B0=EC=8B=9C?= =?UTF-8?q?=EA=B0=80=20=EB=8B=AC=EB=A0=A5=EC=9D=84=20=EC=95=88=20=EC=93=B4?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CTX 126종 배치의 시작. 본문을 정확히 읽은 둘만 넣는다 — 나머지 세 변형(_00Months_00Days · _개월_00일 · _00개월_일)은 형식이 다를 수 있어 읽기 전에는 넣지 않는다. ## 레거시가 이상해 보이지만 그대로 옮긴 것 둘 **PAT_나이_개월수** = `DateDiff(Month, PatBthDay, SystemDate) - 1` VB 의 DateDiff(Month, ...) 는 날짜를 보지 않고 월 경계만 센다(연차×12 + 월차). 그래서 1일생과 말일생이 같은 값이 나오고, 같은 달이면 -1 이 찍힌다. 일수로 환산하면 레거시와 다른 수가 된다 — 판정으로 못 박았다. **PAT_나이_00개월_00일** = `days \ 30 & "개월 " & days Mod 30 & "일"` 달력이 아니라 30일로 나눈다. 365일이 "12개월 5일"이 된다. 달력으로 고치면 값이 달라진다. ## 내 산수가 틀렸다 기대값을 처음에 11로 적었다. 2025-01 → 2025-12 는 월 경계 11 이고 거기서 1을 빼면 10 이다. 구현이 아니라 판정이 틀렸고, 테스트가 그것을 잡았다. ## 아직 배선하지 않았다 두 함수는 "오늘"이 필요한데 해석기에 그 값을 넣는 배선(서버 시각 1회 읽기)이 남았다. 컨텍스트가 부족해 배선까지 하면 게이트를 못 돌리므로, 검증된 순수 함수만 먼저 넣는다. 다음 세션 첫 작업이 배선이다. ## 게이트 - dotnet test 338/338 (나이 판정 2건 신규) - --edit-smoke 실패 0 - --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 불변 Co-Authored-By: Claude Opus 5 --- src/SheetMe.Core/Catalog/ResidentNumber.cs | 43 +++++++++++++++++++ .../SheetMe.Core.Tests/ResidentNumberTests.cs | 25 +++++++++++ 2 files changed, 68 insertions(+) diff --git a/src/SheetMe.Core/Catalog/ResidentNumber.cs b/src/SheetMe.Core/Catalog/ResidentNumber.cs index 881ad0a..a253f99 100644 --- a/src/SheetMe.Core/Catalog/ResidentNumber.cs +++ b/src/SheetMe.Core/Catalog/ResidentNumber.cs @@ -105,5 +105,48 @@ public static class ResidentNumber } private static string Clean(string? value) => (value ?? string.Empty).Trim(); + + /// + /// 생년월일부터 오늘까지의 개월수 — 레거시 PAT_나이_개월수 + /// (DateDiff(Month, PatBthDay, SystemDate) - 1). + /// + /// VB 의 DateDiff(Month, ...) 는 날짜를 보지 않고 월 경계만 센다 + /// (연차×12 + 월차). 거기서 1을 뺀 값이 태그의 결과다. + /// 일수로 환산하지 않는다 — 환산하면 레거시와 다른 수가 나온다. + /// + public static string MonthsSince(string? birthDay, string? todayYmd) + { + var birth = Clean(birthDay); + var today = Clean(todayYmd); + if (birth.Length < 6 || today.Length < 6 + || !int.TryParse(birth[..4], out var by) || !int.TryParse(birth.Substring(4, 2), out var bm) + || !int.TryParse(today[..4], out var ty) || !int.TryParse(today.Substring(4, 2), out var tm)) + { + return string.Empty; + } + return (((ty - by) * 12) + (tm - bm) - 1).ToString(); + } + + /// + /// "N개월 N일" — 레거시 PAT_나이_00개월_00일. + /// + /// 달력이 아니라 30일로 나눈다(days \ 30, days Mod 30). + /// 실제 개월수와 다르지만 레거시가 그렇게 찍는다 — 달력으로 고치면 값이 달라진다. + /// + public static string MonthsDaysSince(string? birthDay, string? todayYmd) + { + var birth = Clean(birthDay); + var today = Clean(todayYmd); + if (birth.Length < 8 || today.Length < 8 + || !DateTime.TryParseExact(birth[..8], "yyyyMMdd", null, + System.Globalization.DateTimeStyles.None, out var from) + || !DateTime.TryParseExact(today[..8], "yyyyMMdd", null, + System.Globalization.DateTimeStyles.None, out var to)) + { + return string.Empty; + } + var days = (int)(to - from).TotalDays; + return $"{days / 30}개월 {days % 30}일"; + } #endregion } diff --git a/tests/SheetMe.Core.Tests/ResidentNumberTests.cs b/tests/SheetMe.Core.Tests/ResidentNumberTests.cs index bcdd0de..5830286 100644 --- a/tests/SheetMe.Core.Tests/ResidentNumberTests.cs +++ b/tests/SheetMe.Core.Tests/ResidentNumberTests.cs @@ -133,6 +133,31 @@ public sealed class ResidentNumberTests Assert.AreEqual(string.Empty, ResidentNumber.SexKoreanOf("880101")); } + [TestMethod] + public void 개월수는_월_경계만_센다() + { + // VB DateDiff(Month, ...) 는 날짜를 보지 않는다 — 연차×12 + 월차, 거기서 1을 뺀다. + // 일수로 환산하면 레거시와 다른 수가 나온다. + // 2025-01 → 2025-12 는 월 경계 11, 거기서 1을 뺀 10 이다(기대값을 처음에 11로 잘못 적었다) + Assert.AreEqual("10", ResidentNumber.MonthsSince("20250101", "20251201")); + // 같은 달이면 -1 이 된다(레거시 그대로 — 음수도 그대로 찍힌다) + Assert.AreEqual("-1", ResidentNumber.MonthsSince("20260101", "20260101")); + // 날짜는 보지 않는다: 1일생과 말일생이 같은 값 + Assert.AreEqual(ResidentNumber.MonthsSince("20250101", "20260101"), + ResidentNumber.MonthsSince("20250131", "20260101")); + Assert.AreEqual(string.Empty, ResidentNumber.MonthsSince("2025", "20260101")); + } + + [TestMethod] + public void 개월_일은_달력이_아니라_30일로_나눈다() + { + // 레거시가 days \ 30 · days Mod 30 이다. 달력으로 고치면 값이 달라진다. + Assert.AreEqual("0개월 10일", ResidentNumber.MonthsDaysSince("20260101", "20260111")); + Assert.AreEqual("1개월 1일", ResidentNumber.MonthsDaysSince("20260101", "20260201")); + Assert.AreEqual("12개월 5일", ResidentNumber.MonthsDaysSince("20250101", "20260101")); + Assert.AreEqual(string.Empty, ResidentNumber.MonthsDaysSince("2026", "20260101")); + } + [TestMethod] public void 하이픈은_양쪽_공백을_지킨다() {