Unix 에포크 — 모든 시각이 기점으로 삼는 영점
Unix 타임스탬프는 1970-01-01 00:00:00 UTC부터 흐른 초(시스템에 따라 밀리초·마이크로초)를 센 값입니다. 이 순간이 "Unix 에포크" 이며 AT&T 벨연구소 팀이 1970년대 초에 채택했습니다. 당시 기준으로 표현이 충분히 작게 끝나는 과거이면서, 음수 날짜로 다루기 까다로워지는 시기까지의 여유도 남는 시점입니다. Unix 계열 환경의 시계 인지 시스템은 최종적으로 이 스칼라로 환원됩니다.
알아 둘 가치가 있는 귀결이 둘 있습니다. 첫째, 타임스탬프는 시간대 를 무시합니다. 서울과 로스앤젤레스의 두 기기가 같은 순간에 취득한 Unix 값은 동일하며 사람을 위한 표기만 달라집니다. 둘째, 윤초는 표현되지 않습니다. Unix 타임스탬프는 모든 날을 정확히 86,400초 로 다루며, IERS가 UTC 일말에 윤초를 삽입하면 Unix 값은 조용히 같은 초를 반복하거나 평활화로 흡수합니다. POSIX가 이 동작을 규정하며 대부분의 애플리케이션은 신경 쓰지 않아도 됩니다.
같은 순간을 서로 다른 형식으로 표기하는 법
같은 순간이 시스템에 따라 대여섯 가지 다른 형식으로 등장합니다. 어느 형식인지 파악하면 알맞은 파서를 고를 수 있고, 정밀도를 알면 밀리초·마이크로초 값을 "먼 미래의 초"로 오인하는 일을 막을 수 있습니다.
| 형식 | 예 | 정밀도 | 주로 마주치는 곳 |
|---|
| Unix seconds | 1747353600 | 1초 | 대부분의 데이터베이스, JWT의 exp/iat, Unix 파일 수정 시각. |
| Unix milliseconds | 1747353600000 | 1밀리초 | JavaScript의 Date.now(), Java의 System.currentTimeMillis(), Kafka 로그 시각. |
| Unix microseconds | 1747353600000000 | 1마이크로초 | PostgreSQL의 TIMESTAMP, Python의 time.time_ns() / 1000, 일부 추적 시스템. |
| ISO 8601 | 2025-05-16T00:00:00Z | 가변 | REST API, RFC 3339(엄밀한 부분집합), HTML <time>, 로그 형식. |
| RFC 2822 | Fri, 16 May 2025 00:00:00 +0000 | 1초 | 메일 헤더(Date:), RFC 9110의 HTTP Date 헤더. |
| JS Date.toString | Fri May 16 2025 09:00:00 GMT+0900 (JST) | 1초 | V8 기본값. 표준이 아니라 엔진별로 다르므로 직렬화에는 쓰지 않습니다. |
시간대·UTC 오프셋·IANA 데이터베이스
타임스탬프 자체는 모호함이 없습니다. 어디서나 같은 순간을 가리킵니다. 반면 "2025-05-16 09:00" 같은 벽시계 형식의 날짜는 모호해서 타임라인 위 한 점으로 고정하려면 시간대가 필요합니다. "순간"과 "시계 표면" 사이의 간극이 날짜 관련 버그가 거의 모두 사는 자리입니다.
+09:00 같은 UTC 오프셋은 어느 한 순간의 UTC와 현지 시각의 차이를 잘라낸 값입니다. 시간대와 같지 않습니다. America/Los_Angeles는 시간대이며 연 2회 -07:00과 -08:00을 전환합니다. IANA 시간대 데이터베이스(tzdata)는 1970년 이후의 모든 지역·모든 전환을 기술하며 대부분의 OS와 런타임이 함께 배포합니다. 순간을 저장할 때는 UTC + 시간대 식별자나 Unix 타임스탬프를 저장합니다. 벽시계 형식 문자열만 저장하면 안 됩니다. 사용자 표시용으로 정렬할 때는 시간대를 조회해 적용합니다. 오늘 필요한 오프셋이 다음 주 일요일 에도 같으리라는 보장은 없습니다.
Y2038과 그 외 시계 롤오버 기한
Y2038은 Y2K의 사촌 격 문제입니다. Unix 초를 32비트 부호 있는 정수로 보관하면 2038-01-19T03:14:07Z에서 오버플로가 발생합니다. 다음 1초에 값이 음수로 뒤집어지고, 대부분의 코드는 이를 1901년 으로 읽습니다. 초를 32비트로 보관하는 시스템(오래된 임베디드 펌웨어, 일부 C 구조체(32비트 Linux의 time_t), 옛 데이터베이스 컬럼 등)은 64비트로 옮기기 전에 그 순간을 맞이하면 오작동 합니다.
인접한 형식에도 다른 롤오버 기한이 도사립니다. GPS 주(week) 번호 는 10비트 카운터로 1999년과 2019년에 되감겼습니다. NTP의 32비트 초 카운터는 2036년에 넘칩니다. Windows의 FILETIME은 1601-01-01 기점의 100나노초 단위라 서기 30828년까지 안전합니다. JavaScript의 Number는 2^53 밀리초까지 정수를 엄밀히 표현할 수 있어 대략 서기 287396년에 해당하며 충분한 여유가 있지만, 직접 산술을 작성한다면 명시적으로 의식할 가치가 있습니다. 시각을 저장하는 자리를 점검해 32비트 초가 남아 있으면 기한 전에 이전을 끝내세요. 사고 발생 시점에 대응하는 것은 최악의 타이밍입니다.
엔지니어가 매월 발등을 찍는 함정 5가지
첫째, 초와 밀리초의 혼동입니다. 1747353600은 2025년 5월이지만, 뒤에 0을 셋 더 붙인 1747353600000도 밀리초로 2025년 5월입니다. 둘을 뒤바꾸면 서기 57344년 부근으로 튕깁니다. 위 도구는 자릿수로 자동 판정하지만, 본인 코드에서는 어느 단위를 다루는지 명시 하세요.
둘째, JavaScript의 Date는 월이 0부터 시작합니다. new Date(2025, 5, 1)은 5월이 아니라 6월입니다. ISO 문자열을 쓰거나 date-fns· Luxon 같은 라이브러리를 써서 이 함정을 피하세요.
셋째, "자정"은 모호합니다. 로컬 00:00:00은 본인 로케일이 UTC가 아닌 한 UTC의 00:00:00과 같은 순간이 아닙니다. 일간 보고서에서는 경계가 로컬 자정인지 UTC 자정인지 먼저 결정하고 사양에 적어 두세요.
넷째, DST와 역사적 시간대 변경입니다. "하루를 더한다"를 365번 반복하면 DST 경계에서 1시간이 어긋납니다. 원시 밀리초 산술이 아니라 시간대 인식 산술을 제공하는 라이브러리를 쓰세요. 역사적 변경도 실재합니다. 러시아는 2011년에 DST를 폐지하고 2014년에 부활시켰습니다. 터키는 2016년에 일광 절약 시간을 영구화했습니다. tzdata에 모두 반영되어 있습니다.
다섯째, 마이크로초 정밀도는 손실됩니다. JavaScript의 Date는 1ms 정밀도이며, PostgreSQL TIMESTAMP를 JavaScript를 거쳐 직렬화하면 끝의 마이크로초가 조용히 잘립니다. 고정밀 텔레메트리 에서는 완전 정밀도 값을 문자열이나 BigInt로 전송하고, 표시 계층에서만 변환하세요.
사용법
Unix 타임스탬프(초 또는 밀리초), ISO 8601 문자열, RFC 2822 형식 날짜, 또는 `new Date()`가 해석할 수 있는 임의의 문자열을 붙여 넣으세요. 도구가 형식을 자동 판별해 같은 순간을 7가지 표현으로 동시에 보여줍니다. Unix초, Unix밀리초, UTC ISO 8601, 현지 타임존 ISO 8601, 사람용 UTC표기, 사람용 현지 표기, 그리고 매초 갱신되는 상대 시간("3분 전" 등)입니다.
Now 버튼은 현재 Unix초 값을 입력란에 채워 넣습니다. 초와 밀리초는 자릿수로 구분합니다. 10^12 이상은 밀리초로 처리하므로 10자리 값은 초, 13자리 값은 밀리초가 됩니다. 이 임계는 33658년까지 유지되므로 여유가 충분합니다. 변환은 모두 JavaScript의 Date·Intl API로 브라우저 안에서 끝나며 서버 왕복이 없습니다.
예제
로그 라인의 Unix초 타임스탬프 변환
출력
Unix sec: 1716800400
Unix ms: 1716800400000
UTC: 2024-05-27T09:00:00.000Z
Local: 2024-05-27T18:00:00+09:00 (Asia/Tokyo)
Relative: about 2 years ago
대부분의 서버 로그(nginx, syslog, Go의 `time.Now().Unix()`나 Python의 `time.time()`에서 나온 애플리케이션 타임스탬프)는 초 단위로 출력합니다. 현지 표시는 브라우저 타임존을 적용하므로 다른 리전 서버 로그와 대조할 때 유용합니다.
밀리초 타임스탬프 자동 판별
출력
Unix sec: 1716800400
Unix ms: 1716800400000
UTC: 2024-05-27T09:00:00.000Z
JavaScript의 `Date.now()`, Java의 `System.currentTimeMillis()`, 대부분의 JSON API는 밀리초로 출력합니다. 13자리 길이로 밀리초 판별이 작동하며 같은 순간의 초 표기는 10자리입니다. 어느 단위인지 외울 필요 없이 붙여 넣으면 판별됩니다.
오프셋이 포함된 ISO 8601 문자열 파싱
입력
2024-05-27T18:00:00+09:00
출력
Unix sec: 1716800400
UTC: 2024-05-27T09:00:00.000Z
Local: 2024-05-27T18:00:00+09:00
입력에 오프셋이 포함되면 파싱이 모호함 없이 결정됩니다. 타임존 추측이 끼지 않습니다. 오프셋이 없는 `2024-05-27 18:00:00`은 브라우저 타임존으로 읽힙니다. API 계약에서는 명시 오프셋(또는 UTC를 의미하는 끝의 `Z`)을 우선하세요.
자주 묻는 질문
초인지 밀리초인지 어떻게 판별하나요?
값의 크기로 판별합니다. 10^12 이상은 밀리초, 그보다 작은 숫자 입력은 초로 읽습니다. 임계 값을 초로 환산하면 33658년에 해당하므로 금세기의 모든 타임스탬프는 모호함 없이 판별됩니다. 굳이 단위를 강제하고 싶다면 초 값에 `000`을 붙여 밀리초 대역으로 올리거나, 밀리초 값을 외부에서 나누세요.
Y2038 문제가 무엇이고 이 도구는 영향을 받나요?
Y2038 문제는 부호 있는 32비트 Unix타임스탬프가 2038-01-19 03:14:07 UTC를 경계로 오버플로해 1901년으로 감기는 현상입니다. 이 도구는 JavaScript Number(53비트 안전 정수 범위)로 초·밀리초를 다루므로 변환 자체에 2038 경계는 없습니다. 위험은 하류에 잠복합니다. 32비트 플랫폼에서 `time_t`를 사용하는 C 프로그램에 2^31을 넘는 값을 전달하면 잘못 읽힙니다. 끝부터 끝까지 64비트(`int64`·`BIGINT`)로 저장·전송하세요.
ISO 8601과 RFC 3339는 같은 건가요?
RFC 3339는 인터넷 프로토콜용으로 좁힌 ISO 8601의 엄격한 부분 집합입니다. ISO 8601은 기본 형식(하이픈 없음), 주차 표기, 연내 일수 표기, 콤마 소수점 같은 여러 선택 형식을 허용합니다. RFC 3339는 이를 좁힙니다. 확장 형식만, 날짜와 시각의 구분자는 `T` 또는 공백, 오프셋 필수, 점 소수점이 규칙입니다. JavaScript의 `Date` 파서는 RFC 3339 형식을 확실히 받지만 ISO 8601의 일부 변종은 실패할 수 있습니다. API를 설계할 때는 "ISO 8601" 대신 "RFC 3339"라고 적으면 느슨한 해석을 피할 수 있습니다.
현지 표시가 UTC와 다른 날짜로 나오는 이유는?
Unix타임스탬프는 단일 순간을 나타냅니다. 같은 순간이 도쿄에서는 "오늘"이고 뉴욕에서는 "어제"일 수 있기 때문에 두 표시가 필요한 것입니다. 서버 로그가 `1716800400`, 대시보드가 `2024-05-27 18:00 JST`로 보일 때, 캘리포니아의 동료는 같은 행을 `2024-05-27 02:00 PDT`로 봅니다. 타임스탬프를 화제 삼을 때는 반드시 렌더링 규칙(UTC·로컬·고정 타임존 중 무엇인지)을 함께 적어 주세요.
1970년 이전 날짜도 변환되나요?
예. 음수 Unix타임스탬프도 유효합니다. `−1`은 1969-12-31T23:59:59Z를 의미합니다. `1969-06-20T20:17:40Z`(아폴로 11호 달 착륙) 같은 ISO 8601 문자열을 붙여 넣으면 대응하는 음수 Unix값이 나옵니다. JavaScript Date는 에포크에서 약 ±1억 일을 다루므로 일반 업무에 나오는 과거·먼 미래 날짜는 문제없이 파싱됩니다.
GPS 시각과 비교하면 18초가 어긋나는 이유는?
Unix시각은 하루를 정확히 86400초로 다루며 윤초를 조용히 건너뜁니다. GPS시각은 그렇지 않습니다. 2026년 중반 기준 누적 차이는 18초(Unix가 GPS보다 18초 늦음)입니다. 양쪽이 Unix시각을 쓰는 소프트웨어에서는 사실상 보이지 않으며, GNSS수신기, 천문 소프트웨어, TAI·GPS시각을 저장하는 레거시 시스템과 연동할 때만 문제가 됩니다.
관련 개념
Unix타임스탬프는 1970-01-01T00:00:00 UTC로부터 경과한 초 수이며, 하루를 86400초로 다룹니다(윤초는 조용히 건너뜁니다). 같은 순간을 달력 형식으로 표시하려면 추가 정보 3가지가 필요합니다. 달력(그레고리력), 타임존(UTC 오프셋 + DST 규칙), 로케일("May" / "5월" / "5月"의 언어와 순서). 이 도구는 이 세 층을 나란히 표시하므로 어느 것이 절대 순간이고 어느 것이 렌더링 선택인지 명확합니다.
단위 혼동은 생각보다 큰 사고를 부릅니다. Unix초는 현재 10자리, 밀리초는 13자리, 마이크로초(데이터베이스나 일부 로그 프레임워크에서 흔함)는 16자리, 나노초(Go의 `time.Now().UnixNano()`, 현대 syslog)는 19자리입니다. 단위를 잘못 읽으면, 밀리초 필드를 초로 읽었을 때 52000년 미래로 날아가고, 초 필드를 밀리초로 읽으면 1970년 1월에 떨어집니다. 이 도구가 사용하는 10 대 13 자릿수 판별은 현재 타임스탬프에는 유효하지만, 고정밀 값은 명시 변환이 필요할 수 있습니다.
인접한 표준 3가지도 알아 둘 가치가 있습니다. **ISO 8601**은 캘린더·날짜 광역 표준으로 `2024W221`이나 `2024-148T18:00:00` 같은 여러 선택 형식을 포함합니다. **RFC 3339**는 인터넷 프로토콜용으로 좁힌 엄격한 부분 집합이며, 대부분의 API문서가 "ISO 8601"이라 말할 때 실제로 의미하는 것이 이쪽입니다. **TAI**는 윤초를 실제로 세는 원자시계열이며 현재 Unix시각보다 18초 앞서갑니다. 이 차이는 GNSS, 천문, `chrony`를 TAI 모드로 동기화하는 시스템에서 문제가 됩니다.