본문으로 건너뛰기
날짜

1월 31일의 한 달 뒤는 2월 28일인가 3월 3일인가

1월 31일에 한 달을 더했더니 어떤 앱은 2월 28일, 어떤 앱은 3월 3일을 내놓습니다. 둘 다 버그가 아닙니다. 달력에는 “한 달”의 길이가 정해져 있지 않아서, 없는 날짜를 어떻게 처리할지, 개월과 일을 어떤 순서로 셀지, 어느 시간대의 자정을 기준으로 삼을지를 도구마다 다르게 정해 두었기 때문입니다.

수정 · 약 12분 · 작성 Fixit Dev

1월 31일의 한 달 뒤가 2월 28일도 되고 3월 3일도 되는 이유

달력에서 “한 달”은 28일에서 31일 사이를 오가는 단위입니다. 그래서 개월 더하기는 “일(日)은 두고 월만 올린다”로 정의되는데, 1월 31일처럼 다음 달에 같은 일이 없으면 추가 규칙이 필요하고 그 규칙이 도구마다 다릅니다.

말일 클램프 방식은 목적지 달에 그 일자가 없으면 그 달의 마지막 날로 내려 맞춥니다. 2026년 1월 31일(토) + 1개월 = 2026년 2월 28일(토)입니다. 초과 이월 방식은 2월 31일이라는 없는 날짜를 만든 뒤 넘치는 3일을 3월로 넘겨 3월 3일(화)로 만듭니다. 아래에 연결된 날짜 더하기 계산기는 말일 클램프 방식입니다.

도구·언어방식2026-01-31 + 1개월
스프레드시트 EDATE (엑셀·구글 시트)말일 클램프2026-02-28
엑셀 DATE(연, 월+1, 일)초과 이월2026-03-03
자바스크립트 Date.setMonth()초과 이월2026-03-03
자바 java.time, 파이썬 dateutil말일 클램프2026-02-28
파이썬 표준 datetime.timedelta개월 단위 없음30일을 더하면 2026-03-02
2026년 9월 기준. 처음 쓰는 도구는 1월 31일 + 1개월을 넣어 어느 쪽인지 확인하는 것이 가장 빠릅니다.

스프레드시트 함수 표준인 OpenFormula는 EDATE를 “더한 뒤 일자가 그 달의 일수보다 크면 그 달의 마지막 날로 조정한다”고 정의하고, 엑셀과 구글 시트의 EDATE도 같은 결과를 냅니다. 반면 같은 엑셀에서 DATE(연, 월+1, 일)로 한 달을 더하면 DATE 함수가 초과분을 다음 달로 넘겨 3월 3일이 됩니다. 자바스크립트 setMonth도 MDN 문서대로 초과 이월이라, 보정 없이 쓴 달력 위젯은 월말에만 어긋나 버그가 오래 숨어 있습니다.

윤년 규칙과 2월 29일이 사라지는 해의 계산

윤년 규칙은 세 단계입니다. 4로 나누어떨어지면 윤년, 그중 100으로 나누어떨어지면 평년, 그중 다시 400으로 나누어떨어지면 윤년입니다. 2024·2028·2032년과 2000년은 윤년, 2100년은 평년입니다. “4년마다”라고만 외우면 2100년에 틀립니다.

2월 29일이 있는 해에서 없는 해로 넘어가면 앞 절의 문제가 그대로 나타납니다. 2028년 2월 29일(화) + 1년은 말일 클램프로 2029년 2월 28일(수), 초과 이월로 2029년 3월 1일(목)입니다. 반대 방향은 하나로 모여서 2029년 2월 28일 − 1년은 어느 방식이든 2028년 2월 28일이므로, 1년을 더했다 빼도 원래 날짜로 돌아오지 않습니다.

법은 이 경우를 정해 두었습니다. 민법 제160조 제3항은 월·연으로 정한 기간에서 최종 월에 해당일이 없으면 그 월의 말일로 만료한다고 하고, 행정기본법 제7조의2는 나이를 출생일을 산입해 만 나이로 계산하도록 합니다. 둘을 적용하면 2028년 2월 29일생의 첫 1년은 2029년 2월 28일이 끝나는 시점에 만료합니다. 다만 “그날 24시 만료”와 “그날이 생일”은 같은 말이 아니어서, 2월 28일과 3월 1일 중 무엇을 표시할지는 기관마다 갈립니다.

“몇 개월 며칠”은 계산 순서에 따라 답이 갈린다

두 날짜의 차이를 일수로 구하면 답은 하나입니다. 2026년 1월 31일(토)에서 3월 1일(일)까지는 29일이고 어떤 도구도 다르게 낼 수 없습니다. 문제는 그 29일을 “몇 개월 며칠”로 바꿀 때로, 달마다 길이가 달라 개월을 어디서 잘라내느냐에 따라 남는 일수가 달라집니다.

가능한 계산 순서는 크게 셋입니다. ① 시작일에서 년·개월을 더하되 없는 날짜는 말일로 맞춘 뒤 남은 일수를 세기, ② 종료일에서 개월을 거꾸로 빼서 시작일을 넘지 않는 날짜를 찾은 뒤 그 사이 일수를 세기, ③ 초과 이월로 개월을 더해 종료일을 넘지 않는 최대 개월 수를 잡기입니다. 아래에 연결된 날짜 차이 계산기는 ①을 씁니다.

시작일 → 종료일총 일수① 앞에서 년→월→일② 뒤에서 개월 빼기③ 초과 이월
2026-01-31 → 2026-03-0129일1개월 1일1개월 1일0개월 29일
2026-01-31 → 2026-02-2828일1개월 0일0개월 28일0개월 28일
2026-02-28 → 2026-03-3131일1개월 3일1개월 0일1개월 3일
2028-02-29 → 2029-02-28365일1년 0개월 0일11개월 28일11개월 30일
총 일수는 같아도 분해 결과는 세 방식이 모두 다를 수 있다.

첫 줄에서 ①은 1월 31일 + 1개월 = 2월 28일이고 3월 1일까지 1일이 남아 “1개월 1일”입니다. ③은 1월 31일 + 1개월을 3월 3일로 보므로 3월 1일을 넘어 개월을 세지 못하고 “29일”이 됩니다. 둘째 줄은 반대로, ②에서 2월 28일 − 1개월 = 1월 28일이 시작일보다 앞서 개월을 세지 못해 “28일”이 됩니다. 어느 것이 정답이라 할 수 없으니, 기관 간 비교는 개월·일이 아니라 총 일수로 하는 편이 안전합니다.

양끝 포함과 초일 불산입 — 1년 계약의 종료일 구하기

“3월 1일부터 3월 3일까지 며칠인가”에는 2일(날짜 차이)과 3일(양끝 포함) 두 답이 있습니다. 근속·숙박·대여처럼 “그날을 채웠다”는 계산은 양끝 포함, 나이나 D-day처럼 기준일에서 떨어진 거리를 재는 계산은 차이 방식입니다.

법의 기본 규칙은 민법 제155조부터 제161조에 있습니다. 제157조는 초일 불산입이 원칙이되 기간이 오전 0시부터 시작하면 산입한다는 단서를 두고, 제160조는 주·월·연 단위 기간을 달력에 따라 계산하되, 주·월·연의 처음부터 세지 않는 기간은 기산일에 해당하는 날의 전날에 만료한다고 정합니다. 제159조에 따라 만료 시점은 그 말일이 끝나는 24시입니다.

“2026년 3월 1일부터 1년” 계약은 3월 1일 0시에 시작하므로 초일을 산입합니다. 1년 뒤 해당일은 2027년 3월 1일이고 그 전날인 2027년 2월 28일(일) 24시에 만료합니다. 계약서에 “2026. 3. 1. ~ 2027. 2. 28.”로 적는 근거입니다. 같은 계약이라도 “체결일부터 1년”이고 체결이 3월 1일 낮이었다면 초일을 빼고 3월 2일부터 세므로 만료일은 2027년 3월 1일(월)입니다.

이 하루 차이에 다른 규칙이 얹힙니다. 2027년 2월 28일은 일요일, 3월 1일은 삼일절입니다. 제161조는 말일이 토요일 또는 공휴일이면 익일에 만료한다고 정하므로, 신고·이의신청 기한처럼 이 조항이 적용되는 기간이라면 2월 28일 만료는 3월 1일로 넘어가고, 그날도 공휴일이라 같은 조문을 다시 적용하면 3월 2일(화)까지 밀립니다. 반면 종료일을 날짜로 못 박은 계약은 제155조에 따라 약정이 우선합니다.

시간대와 서머타임 때문에 하루가 사라지는 경우

시각이 끼어드는 순간 “같은 날”의 정의가 흔들립니다. 한국 표준시(KST)는 UTC+9이고 서머타임이 없지만, 서버는 대개 UTC이고 브라우저는 기기의 시간대를 따릅니다. 브라우저(KST)에서 2026년 3월 1일 0시를 만들어 ISO 문자열로 바꾸면 “2026-02-28T15:00:00.000Z”가 되고, 앞 열 글자만 잘라 저장하면 2월 28일이 됩니다. 반대로 UTC 서버가 “2026-03-01T00:00:00+09:00”를 받아 서버의 현지 날짜를 읽어도 2월 28일입니다.

자바스크립트에는 함정이 하나 더 있습니다. ECMAScript 규격은 “2026-03-01”처럼 날짜만 있는 문자열은 UTC 자정으로, 시각이 붙고 오프셋이 없는 문자열은 현지 시각으로 해석합니다. 한국에서는 전자가 3월 1일 오전 9시라 문제가 없지만, 미주처럼 UTC보다 늦은 시간대에서는 2월 28일 저녁이 되어 하루 어긋납니다.

해결은 날짜만 있는 값을 시각 없이 다루는 것입니다. 연·월·일 세 정수만 쓰거나, 시각형 값이 필요하면 모든 날짜를 UTC 자정으로 만들어 차이를 86,400,000밀리초로 나눕니다. UTC에는 서머타임이 없어 하루가 항상 24시간이라 정수로 떨어집니다. 뉴욕 현지 시각으로 계산하면 2026년 3월 8일(일)에 시계가 한 시간 앞당겨져 3월 7일 0시부터 3월 9일 0시까지가 47시간이고, 24로 나눠 내림하면 1일이 나옵니다.

해외 날짜와의 차이도 같은 문제입니다. 서울의 3월 2일 오전 8시가 뉴욕에서는 아직 3월 1일 오후 6시이므로, 두 날짜를 같은 시간대의 달력 날짜로 바꾼 뒤 빼야 합니다.

같은 날짜의 요일이 해마다 하루씩 밀리는 규칙

평년은 365일이고 365 = 52 × 7 + 1이므로 같은 날짜의 요일은 이듬해에 하루 뒤로 밀립니다. 윤년의 2월 29일을 사이에 두고 넘어가면 366 = 52 × 7 + 2라서 이틀 밀립니다. 2026년 9월 25일은 금요일, 2027년 9월 25일은 토요일, 2028년 9월 25일은 2월 29일을 지나므로 월요일입니다.

“윤년이면 이틀”이 아니라 “2월 29일이 두 날짜 사이에 있으면 이틀”입니다. 2027년 1월 15일(금)에서 2028년 1월 15일(토)로 갈 때는 2028년이 윤년이어도 2월 29일이 아직 오지 않아 하루만 밀리고, 2028년 1월 15일에서 2029년 1월 15일(월)로 갈 때 이틀 밀립니다.

흔한 실수 세 가지 — 365일, 30일, 월말 급여일

날짜 계산 오류는 대부분 달력 단위를 고정 일수로 바꿔 계산하는 데서 나옵니다.

  1. 11년 = 365일: 2027년 3월 1일 + 365일은 2028년 2월 29일(화)이지 3월 1일이 아닙니다. 10년 뒤를 3,650일 뒤로 계산하면 2026년 9월 25일 기준 2036년 9월 22일(월)이 나와, 윤년이 세 번 낀 실제 10주년 9월 25일(목, 3,653일 뒤)보다 사흘 이릅니다.
  2. 21개월 = 30일: 2026년 1월 31일 + 30일은 3월 2일(월)로, 말일 클램프(2월 28일)와도 초과 이월(3월 3일)과도 다른 세 번째 답입니다. 6개월도 시작 달에 따라 181일(1월 1일 → 7월 1일)에서 184일(7월 1일 → 이듬해 1월 1일)까지 오갑니다. 계약서의 “6개월”은 180일이 아니라 달력상 6개월입니다.
  3. 3급여일·결제일을 “31일”로 고정: 31일은 한 해에 일곱 달만 있습니다. 근로기준법 제43조 제2항은 임금을 매월 1회 이상 일정한 날짜를 정해 지급하도록 하는데, “매월 31일”은 2·4·6·9·11월에 없고 “매월 30일”은 2월에 없습니다. “매월 말일”로 적는 것이 안전합니다.

스프레드시트에는 함정이 하나 더 있습니다. 매월 말일 일정을 EDATE로 한 달씩 이어 붙이면 1월 31일 → 2월 28일 → 3월 28일 → 4월 28일로 굳어 버립니다. 2월에서 말일로 내려온 뒤에는 원래 31일이었다는 정보가 사라지기 때문입니다. 기준일에서 n개월을 한 번에 더하거나(1월 31일 + 2개월 = 3월 31일) EOMONTH를 써야 합니다.

판단 기준 정리

  • “한 달 뒤”가 도구마다 다르면 먼저 시작일이 29·30·31일인지 봅니다. 아니라면 입력이나 시간대 문제입니다.
  • 개월·일 분해는 총 일수로 먼저 맞춰 보고, 어느 순서로 셌는지를 문서에 남깁니다.
  • 법적 기간은 초일 산입 여부(0시 시작인지)와 말일 규칙(민법 제157조·제160조)을 먼저 정하고, 말일이 토·공휴일이면 제161조 적용 여부를 확인합니다.
  • 날짜만 필요한 값에 시각을 붙이지 않습니다. 연·월·일만 저장하고 일수 차이는 UTC 자정 기준으로 구합니다.
  • 365일·30일·180일은 근사치입니다. 계약·급여·나이는 달력 단위로 세고, 2월 29일과 월말이 낀 계산은 달력으로 한 번 더 확인합니다.

직접 계산해 보기

참고 자료

이 글은 공개된 법령·기관 자료를 바탕으로 Fixit Dev 이 작성했습니다. 법령과 기준은 개정될 수 있으니, 중요한 판단에는 위 원문을 함께 확인하세요.