new Date('2026-05-14')가 하루 밀리는 이유와 해결법
date-only 문자열을 new Date()에 넣으면 시간대에 따라 날짜가 하루 밀립니다. 만기일 계산기에서 이 버그를 잡는 법과 1인 브랜드가 숫자 신뢰를 지키는 방법을 정리했어요.
계산기 도구에서 날짜가 하루 틀리면 사용자는 코드를 탓하지 않아요. "이 사이트 숫자는 못 믿겠다"고 생각하고 떠나요. 1인 브랜드에는 그 한 번의 의심이 꽤 아프게 돌아와요.
플랜씨처럼 적금 만기일 같은 날짜를 다루는 도구를 만들다 보면 이 함정을 꼭 한 번은 밟게 돼요. 원인과 고치는 법, 그리고 이 경험을 브랜드 자산으로 바꾸는 방법까지 정리해 볼게요.
하루가 밀리는 진짜 이유
new Date('2026-05-14')처럼 시간이 없는 날짜 문자열은 ECMAScript 규칙상 UTC 자정으로 해석돼요. 화면에 보여 줄 때는 브라우저의 로컬 시간대로 바뀌고요.
콘솔에서 TZ 환경변수를 바꿔 가며 직접 돌려 보니 결과가 이렇게 갈렸어요.
- 한국(UTC+9): 5월 14일 오전 9시라서 날짜는 그대로예요
- UTC보다 늦은 시간대(미국 등): 5월 13일 저녁이 돼서 하루가 밀려요
반대로 한국에서도 터지는 경로가 있어요. new Date(2026, 4, 14)로 로컬 자정을 만든 뒤 toISOString()을 쓰면 9시간을 빼서 UTC로 바꾸니까 2026-05-13이 나와요. 여기에 getMonth()가 0부터 시작한다는 점(1월=0, 12월=11)까지 겹치면 버그 찾기가 더 어려워져요.
고치는 법: 날짜는 문자열로, 계산은 UTC로
규칙은 두 줄이에요. 날짜만 필요한 값은 끝까지 YYYY-MM-DD 문자열로 들고 다니고, 계산할 때만 UTC 함수로 처리해요.
// keep date-only values as strings; do the math in UTC only
function addMonths(ymd: string, months: number): string {
const [y, m, d] = ymd.split('-').map(Number);
const dt = new Date(Date.UTC(y, m - 1 + months, d));
return dt.toISOString().slice(0, 10);
}이렇게 고쳐 놓고 시간대 3곳에서 다시 돌려 보니 결과가 모두 같았어요. 테스트에는 시간대를 바꾼 케이스를 꼭 넣어 두세요. 한국에서만 돌리는 테스트는 이 버그를 통과시켜 버려요.
하나 더 있어요. addMonths('2026-01-31', 1)은 2월 31일이 넘쳐서 2026-03-03이 돼요. 말일을 어떻게 처리할지는 코드가 아니라 정책이라서, 화면에 기준을 한 줄 적어 두는 편이 좋아요.
버그 수정을 브랜드 콘텐츠로 쓰는 법
적용해 봤더니 이런 수정 기록은 그 자체로 좋은 콘텐츠가 됐어요.
- 계산 기준을 화면에 공개
- 수정 내역을 개발 로그로 기록
- 같은 실수를 막는 체크리스트 공유
- 계산 기준 공개: "만기일은 가입일 기준 달력 월로 계산해요" 같은 문장 하나가 문의를 줄여 줘요
- 개발 로그로 남기기: 무엇이 틀렸고 어떻게 고쳤는지 쓰면 검색으로 들어오는 개발자 독자가 생겨요
- 체크리스트 공유: 시간대 테스트처럼 재사용할 수 있는 항목은 다음 글의 소재가 돼요
숫자를 다루는 도구는 정확해야 신뢰가 쌓여요. 틀렸던 지점을 숨기지 않고 기록해 두는 것도 그 신뢰를 지키는 방법이라고 생각해요. 라이프케어로그가 플랜 시리즈의 수정 과정을 글로 남기는 것도 같은 이유예요.
관련 글
댓글
아직 댓글이 없어요. 첫 댓글을 남겨주세요.