Menu

자바스크립트 Date 완벽 정리: 생성·포맷·비교

자바스크립트 Date 객체의 동작 원리를 제대로 짚어봅니다. 날짜 생성부터 포맷 변환, 날짜 연산, 타임존 처리, 그리고 많은 개발자가 한 번씩 걸려 넘어지는 함정까지 전부 다룹니다.

Date 객체는 곧 '시간상의 한 순간'이다

자바스크립트에서 Date는 어떤 한 시점을 나타낸다. 내부적으로는 그냥 숫자 하나인데, 1970년 1월 1일 UTC(이른바 "Unix epoch")로부터 흘러간 밀리초다. 연도, 월, 일, 타임존, 날짜 포맷 같은 건 전부 이 숫자 위에 덧씌운 표현 방식일 뿐이다.

index.js
Output
Click Run to see the output here.

now.getTime()은 결국 밀리초 단위의 숫자일 뿐입니다. Date가 하는 모든 일(비교, 날짜 더하기, 포맷팅)도 알고 보면 이 숫자를 조작한 뒤 다시 해석하는 과정에 지나지 않습니다.

이 개념을 머릿속에 꼭 새겨두세요. Date는 "파리 시각으로 3월 14일"이 아닙니다. 그것은 하나의 절대적인 순간이고, 어떤 타임존으로 바라보느냐에 따라 파리에서는 3월 14일로, 로스앤젤레스에서는 3월 13일로 _표시될 수 있을 뿐_입니다.

Date 객체 만들기 (new Date() 사용법)

Date를 생성하는 방법은 크게 네 가지가 있습니다:

index.js
Output
Click Run to see the output here.

주의할 점 두 가지:

  • 인자로 값을 넘기는 생성자는 월이 0부터 시작합니다. 2는 3월이고, 1월은 0이에요. 여기서 off-by-one 버그가 끊임없이 발생합니다. 다행인 건 API 전체가 일관되게 0부터 세기 때문에, 최소한 자기 자신과는 일관적이라는 점이죠.
  • new Date("2026-03-14")처럼 시간 없이 넘기면 UTC 자정으로 파싱됩니다. 반면 new Date("2026-03-14T09:30")처럼 Z 없이 시간만 붙이면 로컬 타임으로 파싱돼요. 이 비대칭이 아주 고전적인 함정입니다.

"지금 이 순간"을 숫자로만 얻고 싶다면 Date.now()를 쓰는 편이 좋습니다. 객체를 새로 만들지 않아 더 가볍거든요:

index.js
Output
Click Run to see the output here.

Date.now()는 경과 시간 측정이나 타임아웃처럼 달력 계산까지는 필요 없는 경우에 딱 맞는 도구입니다.

Date 객체에서 값 꺼내기

Date 객체를 만들었다면, 게터(getter) 메서드로 각 구성 요소를 꺼낼 수 있습니다. 게터는 로컬 시간 기준과 UTC 기준, 두 가지 버전이 있습니다.

index.js
Output
Click Run to see the output here.

로컬 계열 메서드는 코드를 실행하는 사람의 기기에 따라 결과가 달라집니다. 여러 사용자나 서버 간에 날짜를 저장하거나 비교할 일이 있다면, 처음부터 UTC를 명시적으로 쓰세요. 안 그러면 원인 모를 버그를 쫓아다니게 됩니다. 기본 원칙은 간단합니다. DB나 로그에 들어가는 값은 UTC 계열 getter, 사람에게 보여줄 값은 로컬 계열 getter를 쓰면 됩니다.

getYear()는 쓰지 마세요. year - 1900을 반환하는 레거시 메서드라, 오로지 하위 호환용으로만 남아 있습니다. 연도가 필요하면 무조건 getFullYear()입니다.

사람이 읽기 좋게 포맷하기

중요한 곳에 date.toString()은 쓰지 않는 게 좋습니다. 결과가 로케일과 엔진에 따라 제각각이거든요. 대신 알아둘 만한 포맷터가 두 가지 있습니다.

기계가 읽기 좋은 표준 문자열이 필요하면 toISOString()을 씁니다:

index.js
Output
Click Run to see the output here.

로그를 남기거나 JSON에 저장할 때, 또는 네트워크로 전송할 때는 이 포맷을 쓰면 됩니다. 항상 UTC 기준이고, 해석에 혼동이 없거든요.

반면 사용자에게 보여줄 문자열이 필요하다면 Intl.DateTimeFormat이나 이를 감싼 toLocale* 계열 메서드를 활용하세요:

index.js
Output
Click Run to see the output here.

Intl.DateTimeFormat은 로케일, 타임존은 물론 필요한 필드 조합을 거의 다 처리해 줍니다. ${year}-${month}-${day} 같은 걸 수동으로 조립하기 전에 먼저 이걸 떠올리세요 — 월 off-by-one 버그는 바로 그런 문자열 조립에서 나옵니다.

자바스크립트 날짜 비교하기

같은 시점을 나타내는 두 Date 객체라도 ===로 비교하면 같지 않다고 나옵니다. ===는 값이 아니라 객체의 동일성을 확인하기 때문이죠. 대신 타임스탬프 값으로 비교하세요:

index.js
Output
Click Run to see the output here.

정렬이나 크기 비교는 비교 연산자를 그대로 써도 됩니다. 내부적으로 숫자로 변환되기 때문이죠:

index.js
Output
Click Run to see the output here.

뺄셈을 하면 두 시점 사이의 간격이 밀리초 단위로 나옵니다. 일(day) 단위로 바꾸려면 1000 * 60 * 60 * 24로 나누면 되고요. 처음에는 이 상수를 풀어서 쓰는 걸 추천하지만, 조금 익숙해지면 86_400_000만 봐도 "아, 하루구나" 하고 바로 알아볼 수 있습니다.

자바스크립트 날짜 더하기, 빼기

아쉽게도 addDays 같은 메서드는 없습니다. 대신 관용적으로 쓰는 방법은 setDate, setMonth 같은 메서드를 활용하는 것인데요. 이 메서드들은 범위를 벗어나는 값을 넣어도 알아서 다음 달, 다음 해로 넘겨주기 때문에 아주 편리합니다:

index.js
Output
Click Run to see the output here.

짚고 넘어갈 포인트 두 가지:

  • new Date(date)는 날짜를 복사합니다. 반면 setDate는 원본을 직접 변경(mutate)하기 때문에 반드시 먼저 복사해서 써야 합니다. 안 그러면 호출자가 넘긴 값까지 같이 바뀌어 버립니다.
  • 31일까지 있는 달에서 setDate(35)를 호출하면 자동으로 다음 달로 넘어갑니다. setMonth(14)도 마찬가지로 연도가 자동으로 올라가죠. 덕분에 날짜 계산이 생각보다 훨씬 덜 골치 아픕니다.

단, 영업일 계산이나 반복 일정, 월 단위를 고려한 기간 계산 같은 복잡한 작업은 라이브러리를 쓰는 게 정답입니다 (date-fns, Luxon, 혹은 곧 표준이 될 Temporal API). "며칠 더하기" 수준을 넘어서는 달력 연산을 직접 구현하기 시작하면 그때부터는 헤어나오기 힘든 늪입니다.

타임존이라는 현실

날짜 버그의 가장 큰 원흉은 단연 타임존입니다. 아래 규칙은 꼭 머리에 새겨두세요:

  • Date 객체는 UTC 기준의 순간(instant)을 저장합니다. 타임존은 값을 꺼내 읽거나 포맷할 때 비로소 적용됩니다.
  • getHours(), getDate() 등이 사용하는 타임존은 _코드를 실행하는 머신의 로컬 타임존_입니다. 서버와 브라우저의 타임존이 다른 경우가 흔하니 주의해야 합니다.
  • new Date("2026-03-14")처럼 날짜만 주면 UTC로 파싱됩니다. new Date("2026-03-14T00:00")처럼 시간까지 있지만 타임존이 없으면 로컬로 파싱되고, new Date(2026, 2, 14)처럼 숫자로 넘기면 역시 로컬 기준입니다.
index.js
Output
Click Run to see the output here.

특정 타임존으로 날짜를 표시해야 할 때는 Intl.DateTimeFormattimeZone 옵션을 넘겨주면 됩니다:

index.js
Output
Click Run to see the output here.

같은 시각인데 보는 방식만 다를 뿐, Date 객체 자체는 전혀 바뀌지 않습니다.

간단한 실전 예제

지금까지 배운 걸 모아서, 어떤 일이 얼마나 전에 일어났는지 표시해 주는 함수를 만들어 봅시다:

index.js
Output
Click Run to see the output here.

타임스탬프를 넣으면 사람이 읽을 수 있는 문자열이 나온다. 실무에서 만나는 날짜 코드의 90%가 바로 이 패턴이다. 두 시점을 빼고, 단위로 나누고, 반올림한 다음, 포맷팅.

핵심 정리

  • Date는 UTC 기준의 순간(instant)이다. 타임존은 읽거나 포맷할 때만 끼어든다.
  • 타임스탬프가 필요하면 Date.now(), 달력 계산이 필요하면 new Date().
  • 저장이나 로그에는 toISOString(), 사용자에게 보여줄 때는 Intl.DateTimeFormat.
  • 날짜 비교는 getTime()이나 <, >로. ===는 절대 안 된다.
  • 월(month)은 0부터 시작한다. 날짜만 있는 문자열 파싱 함정도 조심하자.
  • 복잡한 날짜 연산을 해야 한다면 라이브러리를 쓰자.

다음: URL과 쿼리 스트링

날짜는 URL에도 자주 등장한다. 날짜 범위로 필터링하거나 타임스탬프를 쿼리 파라미터로 넘기는 식이다. URL을 손으로 직접 조립하고 파싱하는 건 날짜를 수동으로 포맷하는 것만큼이나 버그를 부르는 일인데, 다행히 표준 라이브러리에 이걸 깔끔하게 처리해주는 URL 객체가 있다. 다음 글에서 다뤄보자.

자주 묻는 질문

자바스크립트에서 현재 날짜는 어떻게 가져오나요?

인자 없이 new Date()를 호출하면 됩니다. 생성자가 실행된 바로 그 시점을 나타내는 Date 객체가 반환되죠. 숫자 타임스탬프(1970년 이후 밀리초)만 필요하다면 Date.now()를 쓰세요. 객체를 따로 만들지 않으니 훨씬 빠릅니다.

자바스크립트에서 두 날짜는 어떻게 비교하나요?

Date 객체 자체가 아니라 타임스탬프끼리 비교해야 합니다. a.getTime() < b.getTime()이 정석이고, a < b도 잘 동작합니다. < 연산자가 내부적으로 Date를 숫자로 변환하기 때문이죠. 다만 a === b는 통하지 않습니다. ===는 객체의 동일성을 보기 때문에, 같은 시점을 가리키는 두 Date 객체라도 절대 같다고 판정되지 않습니다.

자바스크립트에서 날짜 포맷은 어떻게 바꾸나요?

사용자에게 보여주는 용도라면 Intl.DateTimeFormat이나 date.toLocaleDateString()을 쓰세요. 로케일과 타임존을 알아서 처리해 줍니다. 기계가 읽을 문자열이 필요하다면 date.toISOString()2026-03-14T09:30:00.000Z 같은 표준 형식을 돌려줍니다. 저장용으로 date.toString()은 피하세요. 출력 형식이 로케일마다 달라집니다.

자바스크립트 날짜가 하루 어긋나는 이유가 뭔가요?

대부분 타임존 문제입니다. new Date('2026-03-14')는 UTC 자정으로 파싱되는데, date.getDate()로컬 타임존 기준 날짜를 반환하거든요. 그래서 한국(UTC+9)이든 미국 서부든 하루 전날이 찍혀 나올 수 있습니다. UTC 기준 날짜가 필요하면 getUTCDate()를 쓰고, 아니면 처음부터 로컬 시간으로 해석되는 new Date(year, month, day) 형태로 만드세요.

Coddy로 코딩 배우기

시작하기