본문으로 건너뛰기
LeedyBox
후원

유닉스 타임스탬프 알아보기

유닉스 타임스탬프는 한 시점을 하나의 숫자로 나타냅니다. 저장하고 비교하기는 쉽지만, 단위와 시간대, 텍스트 형식 때문에 변환 실수가 자주 생깁니다.

유닉스 시간이란

유닉스 시간(에포크 시간 또는 POSIX 시간이라고도 함)은 유닉스 에포크인 1970-01-01 00:00:00 UTC부터 흐른 초를 셉니다. 0은 에포크 자체이고, 1700000000은 2023-11-14 22:13:20 UTC이며, 음수 값은 1970년 이전의 시점입니다. -1은 1969-12-31 23:59:59 UTC입니다.

이 숫자에는 시간대가 들어 있지 않습니다. 어디에서나 같은 시점을 나타내므로 이벤트를 저장하고, 정렬하고, 비교하기에 편리합니다.

유닉스 시간은 윤초를 무시합니다. 모든 날은 정확히 86,400초입니다. UTC에 윤초가 추가되면 시스템은 1초를 반복하거나 더 긴 시간에 걸쳐 나눠 반영하므로("스미어링"), 윤초를 사이에 둔 두 시점의 차이는 실제로 흐른 시간보다 1초 짧습니다.

초, 밀리초, 마이크로초, 나노초?

많은 시스템이 더 작은 단위로 세는데, 숫자만 봐서는 어떤 단위인지 알 수 없습니다. 같은 시점이 전혀 다른 네 가지 숫자로 나타날 수 있습니다.

단위 2023-11-14 22:13:20 UTC 자릿수(2001~2286년) 주로 쓰이는 곳
초 1700000000 10 date +%s, 많은 API, JWT exp 클레임
밀리초 1700000000000 13 JavaScript의 Date.now(), Java의 System.currentTimeMillis()
마이크로초 1700000000000000 16 일부 데이터베이스와 로깅 시스템
나노초 1700000000000000000 19 Go의 UnixNano(), Python의 time.time_ns()

현재 날짜라면 자릿수를 세는 것이 가장 빠른 확인 방법입니다. LeedyBox 변환기도 같은 원리를 씁니다. 1011까지의 값은 초로, 1014까지는 밀리초로, 1017까지는 마이크로초로, 그보다 큰 값은 나노초로 읽습니다. 이 경험칙은 오늘과 동떨어진 날짜에서는 맞지 않으므로, 결과가 이상해 보이면 단위를 확인하세요.

정밀도도 중요합니다. JavaScript 숫자는 253(약 9 × 1015)까지만 정수를 정확하게 표현하므로, 1700000000123456789 같은 나노초 값을 일반 숫자로 파싱하면 마지막 자릿수가 사라집니다. 이런 값은 문자열로 유지하거나 BigInt를 사용하세요. 변환기는 내부적으로 이렇게 처리합니다. 토큰의 exp 클레임을 읽으려면 Base64 / URL / JWT 도구로 디코딩하세요.

시간대와 서머타임

타임스탬프를 날짜와 시각으로 표시하려면 시간대가 필요합니다. UTC가 기준이며, 현지 시간에는 서울의 +09:00이나 겨울철 뉴욕의 −05:00 같은 오프셋이 더해집니다.

고정 오프셋이나 약어보다 Asia/Seoul, America/New_York 같은 IANA 시간대 이름을 쓰세요. 이름에는 해당 시간대의 변경 이력과 서머타임 규칙이 담겨 있지만, CST 같은 약어는 여러 시간대를 가리킵니다. 규칙은 1년에도 여러 번 바뀌는 시간대 데이터베이스에서 오며, 브라우저, 운영 체제, 프로그래밍 언어는 저마다 자체 사본을 가지고 있으므로 과거나 미래 날짜의 결과가 다를 수 있습니다.

서머타임 때문에 현지 시간을 타임스탬프로 변환할 때 곤란한 경우가 두 가지 생깁니다.

  • 건너뛰는 시간: 시계를 앞당길 때 일부 현지 시간은 존재하지 않습니다. America/New_York에서는 시계가 02:00에서 03:00으로 건너뛰기 때문에 2026-03-08 02:30이 없습니다.
  • 겹치는 시간: 시계를 되돌릴 때 일부 현지 시간은 두 번 나타납니다. 2026-11-01 01:30은 EDT(UTC−04:00)로 한 번, 한 시간 뒤 EST(UTC−05:00)로 한 번 더 나타납니다.

라이브러리마다 이런 경우를 다르게 처리합니다. LeedyBox 변환기는 건너뛰는 시간에서는 그 뒤의 유효한 시각(이 예에서는 03:30)을, 겹치는 시간에서는 앞선 시각을 사용하며, 이를 안내 문구로 설명합니다. 예약 작업도 같은 문제를 겪습니다. Cron 표현식 읽는 방법을 참고하세요.

ISO 8601, RFC 3339, HTTP 날짜

시점을 사람이 읽을 수 있어야 한다면 ISO 8601이나 그보다 엄격한 인터넷용 프로필인 RFC 3339 형식으로 쓰세요.

2023-11-14T22:13:20Z
2023-11-15T07:13:20+09:00
2023-11-14T22:13:20.500Z

Z는 UTC를 뜻합니다. Z나 오프셋이 있으면 문자열의 의미가 분명합니다. 2023-11-14T22:13:20처럼 둘 다 없는 문자열은 알 수 없는 시간대의 현지 시간이며, 파서마다 해석이 다릅니다.

Last-Modified, Expires 같은 HTTP 헤더는 이메일 날짜 형식(RFC 2822, 현재는 RFC 5322)에서 파생된 오래된 형식을 사용하며 GMT로 표기합니다. 예: Tue, 14 Nov 2023 22:13:20 GMT

2038년 문제

많은 오래된 시스템은 유닉스 시간을 부호 있는 32비트 정수에 저장합니다. 이 정수의 최댓값인 2147483647은 2038-01-19 03:14:07 UTC입니다. 1초 뒤에는 카운터가 오버플로되어 -2147483648이 되며, 이는 1901-12-13 20:45:52 UTC로 읽힙니다. 현재의 운영 체제와 프로그래밍 언어는 대부분 수천억 년을 표현할 수 있는 64비트 시간 값을 쓰지만, 32비트 필드는 임베디드 기기, 파일 형식, 데이터베이스 열, 네트워크 프로토콜에 여전히 남아 있습니다. 코드나 데이터가 초를 32비트로 저장한다면 미리 변경을 계획하고, 2038년 이후의 날짜를 지금 테스트하세요.

코드와 데이터베이스의 타임스탬프

JavaScript는 밀리초 단위로 동작합니다.

Math.floor(Date.now() / 1000);                  // 현재 유닉스 시간(초)
new Date(1700000000 * 1000).toISOString();      // "2023-11-14T22:13:20.000Z"
Date.parse("2026-10-03T09:00:00+09:00") / 1000; // 1790985600

Python의 time.time()은 초를 부동소수점 수로 반환합니다. tz=timezone.utc를 전달하세요. 그렇지 않으면 fromtimestamp()가 시간대 정보 없이 컴퓨터의 현지 시간을 반환합니다.

import time
from datetime import datetime, timezone

int(time.time())                                     # 초
datetime.fromtimestamp(1700000000, tz=timezone.utc)  # 2023-11-14 22:13:20+00:00
datetime.fromisoformat("2026-10-03T09:00:00+09:00").timestamp()  # 1790985600.0

SQL에서는 데이터베이스마다 함수가 다릅니다.

-- PostgreSQL
SELECT extract(epoch FROM now());   -- 초(소수점 이하 포함)
SELECT to_timestamp(1700000000);    -- timestamp with time zone
-- MySQL
SELECT UNIX_TIMESTAMP();
SELECT FROM_UNIXTIME(1700000000);   -- 세션 시간대 기준

저장할 때는 몇 가지 습관만 지켜도 대부분의 문제를 막을 수 있습니다.

  • 이미 일어난 시점은 UTC로 저장하세요. 시간대 포함 타임스탬프 열에 저장하거나, created_at_ms처럼 열 이름에 단위를 넣은 64비트 정수로 저장합니다.
  • 서울에서 09:00에 열리는 회의처럼 미래의 현지 일정은 현지 날짜·시간과 IANA 시간대 이름으로 저장하고, 필요할 때 타임스탬프를 계산하세요. 그래야 나중에 규칙이 바뀌어도 회의 시각이 어긋나지 않습니다.
  • 한 열에 단위를 섞지 마세요. 단위가 틀리면 값이 1,000배 이상 차이 납니다.

자주 묻는 질문

타임스탬프를 변환하면 왜 1970년 날짜가 나오나요?

값을 잘못된 단위로 읽었을 가능성이 큽니다. 초를 밀리초로 읽으면 1970년 1월이 되고, 밀리초를 초로 읽으면 수만 년 뒤의 미래가 됩니다. 자릿수를 세거나 변환기에서 단위를 선택하세요.

유닉스 타임스탬프는 내 시간대에 따라 달라지나요?

아니요. UTC 기준의 고정된 시점부터 초를 셉니다. 타임스탬프를 날짜와 시각으로 표시할 때만 시간대에 따라 달라집니다.

유닉스 시간으로 윤초를 나타낼 수 있나요?

아니요. 유닉스 시간은 모든 날을 86,400초로 취급하므로 23:59:60 같은 UTC 시각에는 고유한 타임스탬프가 없습니다.