개발 이야기3분 읽기

나이 계산에 정답이 없는 자리

2월 29일생의 생일과 1월 31일에 한 달을 더한 날. 개월 수 계산이 구현마다 갈리는 지점을 실행 결과로 정리했습니다.

두 날짜 사이가 며칠인지는 뺄셈입니다. 몇 년 몇 개월 며칠인지는 그렇지 않습니다.

흔한 구현이 음수를 낸다

년·월·일을 각각 빼고 음수가 나오면 윗자리에서 빌려 오는 방식을 씁니다.

let y = t.getFullYear() - b.getFullYear();
let m = t.getMonth() - b.getMonth();
let d = t.getDate() - b.getDate();
if (d < 0) { m--; d += 이전_달_일수; }
if (m < 0) { y--; m += 12; }

이 코드를 1월 31일부터 3월 1일까지로 돌리면 이렇게 나옵니다.

1990-01-31 → 1990-03-01 : 0년 1개월 -2일

일수가 음수입니다. d = 1 - 31 = -30이고, 빌려 온 이전 달(2월)이 28일이라 -30 + 28 = -2입니다. 한 번 빌려서는 모자랍니다.

개월을 먼저 확정하면 음수가 안 나온다

순서를 바꿉니다. 개월 수를 먼저 정하고, 그 개월만큼 이동한 날짜(앵커)에서 남은 일수를 셉니다.

let n = (t.getFullYear()-b.getFullYear())*12 + (t.getMonth()-b.getMonth());
if (addMonths(b, n) > t) n--;
const anchor = addMonths(b, n);
return [Math.floor(n/12), n%12, 일수(anchor, t)];

앵커는 항상 t 이하이므로 남은 일수는 0 이상입니다.

1990-01-29 → 1990-03-01 : 0년 1개월 1일
1990-01-30 → 1990-03-01 : 0년 1개월 1일
1990-01-31 → 1990-03-01 : 0년 1개월 1일

음수는 사라졌습니다. 대신 다른 것이 보입니다 — 시작일이 사흘에 걸쳐 다른데 결과가 같습니다.

1월 31일에 한 달을 더하면 며칠인가

addMonths가 정해야 하는 값입니다. 2월에는 31일이 없습니다.

1990-01-31 + 1개월 = 1990-02-28   (말일로 당김)
1990-02-28 - 1개월 = 1990-01-28   (출발점이 아니다)

말일로 당기는 것이 가장 흔한 선택입니다. 그러면 1월 29·30·31일이 모두 2월 28일로 모입니다. 위에서 세 날짜가 같은 결과를 낸 이유입니다.

그리고 이 연산은 왕복하지 않습니다. 한 달을 더했다 빼면 출발점으로 돌아오지 않습니다. 개월 단위 반복 일정을 매번 직전 값에서 계산하면 날짜가 점점 앞으로 밀립니다 — 1월 31일에 시작한 월간 일정이 2월 28일, 3월 28일, 4월 28일로 굳어집니다. 항상 원래 날짜에서 n개월을 더해야 31일이 유지됩니다.

2월 29일생의 생일

2000-02-29 → 2026-02-28 : 26년 0개월 0일
2000-02-29 → 2026-03-01 : 26년 0개월 1일
2000-02-29 → 2028-02-28 : 27년 11개월 30일
2000-02-29 → 2028-02-29 : 28년 0개월 0일

평년인 2026년에는 2월 28일에 만 26세가 됩니다. 말일로 당기는 규칙의 결과입니다. 윤년인 2028년에는 2월 28일에 아직 27세이고 29일에 28세가 됩니다.

3월 1일로 보는 구현도 있습니다. 어느 쪽도 틀린 것이 아니라 규칙 선택입니다. 한국 민법은 이 경우를 명시하지 않고, 실무에서는 2월 28일로 보는 쪽이 일반적입니다.

나이의 세 가지 정의

2000년 12월 25일생을 2026년 8월 28일에 기준으로 셉니다.

만 나이  25   생일이 지나야 올라간다
연 나이  26   연도만 뺀다 (2026 - 2000)

연 나이는 병역·청소년 관련 법령에서 쓰입니다. 생일과 무관하게 1월 1일에 다 같이 올라갑니다. 세는 나이는 2023년 6월 법 개정으로 공식 문서에서 빠졌습니다.

같은 사람이 세 숫자를 갖는 셈이라, 폼에서 나이를 받을 때는 어느 정의인지 밝히거나 생년월일을 받는 편이 확실합니다.

날짜 수와 평일 수

2000-12-25 부터 2026-08-28 까지  9,377일
그중 평일  6,700일 / 주말  2,678일

평일만 세는 계산은 공휴일을 빼지 않습니다. 근무일 계산이 필요하면 나라별 공휴일 목록이 따로 있어야 하고, 대체공휴일 규칙도 매년 달라집니다.

확인 순서

  1. 개월 수 계산에서 일수가 음수로 나올 수 있는지 봅니다. 말일 시작이 시험 값입니다
  2. 월간 반복 일정은 직전 실행일이 아니라 원래 시작일에서 n개월을 더해 계산합니다
  3. 2월 29일생 처리를 정하고 문서에 적습니다. 2월 28일과 3월 1일 중 하나입니다
  4. 나이를 저장하지 말고 생년월일을 저장합니다. 나이는 조회 시점에 계산합니다
  5. 근무일 계산에는 공휴일 목록이 필요합니다. 주말만 빼면 실제와 어긋납니다

tools.onuel.dev나이·날짜 차이 계산기는 만 나이를 년·월·일로 내고 두 날짜 사이를 평일만으로도 셉니다. 위의 2월 29일 사례를 그대로 넣어 확인할 수 있습니다. 기간을 초나 ISO 8601로 바꿀 때는 기간 변환기를, 타임스탬프는 Epoch 변환Epoch 일괄 변환을 씁니다.

  • #날짜
  • #계산
  • #윤년