·6분 읽기
타임스탬프 변환기 활용법 — Unix 시간 쉽게 이해하기
1711843200 같은 숫자가 뭔지 궁금했나요? Unix 타임스탬프의 개념과 변환 방법, 실전 활용법을 쉽게 설명해드릴게요.

Unix 타임스탬프란
Unix 타임스탬프(Epoch Time)는 1970년 1월 1일 00:00:00 UTC부터 흐른 초(seconds)를 표현하는 숫자예요. 1711843200을 풀면 2024년 3월 31일 00:00:00 UTC가 나와요.
왜 이렇게 정해 놓았냐면, 날짜를 단일 숫자로 잡으면 비교·정렬·계산이 단순해지거든요. '2024-03-31'과 '2024-04-01' 중에 누가 더 나중인지 문자열로 따지는 것보다, 1711843200과 1711929600을 비교하는 쪽이 빠르고 안전해요. 시간대 문자열 파싱하다 어그러질 일도 없고요.
1970년이 기준점인 건 Unix 운영체제가 그 무렵 만들어진 자취예요. 지금도 거의 모든 시스템이 이 기준을 따라요. 윈도·리눅스·맥·iOS·안드로이드 다 같은 epoch에서 출발해요.
타임스탬프가 사용되는 곳
데이터베이스 기록 시간 컬럼이 첫째 자리예요. `created_at`·`updated_at` 같은 필드가 거의 다 timestamp 타입이거든요.
API 응답에서도 시간 데이터는 보통 타임스탬프 숫자로 와요. JSON에 `"created_at": 1711843200` 이런 식으로 박혀 있어요. 사람 읽기 어렵지만 클라이언트가 변환하기 좋아요.
로그 파일에 이벤트 시각을 박을 때, 캐시 만료 시각, JWT 토큰의 `iat`·`exp` 필드, 쿠키 expires도 다 타임스탬프예요. 분산 시스템에서 시간 순서 보장이 필요하면 타임스탬프가 표준이거든요.
Toolkio 타임스탬프 변환기로 숫자→날짜, 날짜→숫자를 즉시 오갈 수 있어요. API 디버깅·로그 분석·DB 점검할 때 한 번씩 켜두면 편해요.
초 단위 vs 밀리초 단위
타임스탬프 형식은 두 가지가 가장 흔해요.
- **초(10자리)**: 1711843200 — Unix·Python `time.time()`·PHP `time()`·MySQL `UNIX_TIMESTAMP()`
- **밀리초(13자리)**: 1711843200000 — JavaScript `Date.now()`·Java `System.currentTimeMillis()`
- **마이크로초(16자리)**: 1711843200000000 — Python `time.time_ns()` / 1000, 거의 안 쓰임
자릿수만 세면 형식이 바로 보여요. 2024~2030 범위라면 10자리는 초, 13자리는 밀리초예요. JavaScript에서 백엔드 초 단위를 다룰 때 1000을 곱해서 `new Date(ts * 1000)`로 넘기는 게 실수 1순위예요. 반대로 백엔드는 밀리초 받아서 1000으로 나누고 저장.
시간대(Timezone) 주의사항
타임스탬프 자체에는 시간대가 없어요. 그냥 UTC 기준 숫자예요. 사람이 읽을 날짜로 풀 때 시간대를 곱해야 해요.
한국(KST)은 UTC+9이라서, 1711843200을 한국 시간으로 풀면 2024년 3월 31일 09:00:00이에요. 같은 숫자가 LA(UTC-7)에서는 3월 30일 17:00이고요.
개발할 때 자주 사고 나는 패턴이 이거예요.
- 서버는 UTC로 저장 → 안전
- DB 컬럼이 `TIMESTAMP WITH TIME ZONE`인지 확인 (PostgreSQL은 보통 OK, MySQL은 함정)
- 화면 노출 직전에만 로컬 시간으로 변환
- 로그·CSV 내보낼 때 시간대 명시(`2024-03-31 09:00 KST`)
특히 서머타임 적용 국가(미국·유럽)는 4월·10월 경계에 1시간 어긋나요. 한국은 서머타임 없으니 그쪽 걱정은 안 해도 돼요.
2038년 문제(Y2K38)
32비트 정수로 초 단위 타임스탬프를 저장하던 옛 시스템은 2038년 1월 19일 03:14:07 UTC에 한계에 닿아요. 그 이후로는 음수로 돌아가 버려요.
현대 시스템은 64비트로 바뀌어서 사실상 무한대(약 2,920억 년)거든요. 그래도 레거시 C 코드·임베디드 시스템·오래된 DB 컬럼에는 아직 32비트가 남아 있어요. MySQL `TIMESTAMP` 타입은 기본 32비트라 2038 한계에 걸려요. 새 프로젝트 시작하시면 `DATETIME` 또는 64비트 BIGINT를 쓰세요.
임베디드·IoT 분야는 여전히 32비트가 많아서 2030년쯤부터 본격 점검이 들어가요. 일찍 64비트로 옮기면 나중에 큰 비용을 안 써도 돼요.
Toolkio 타임스탬프 변환기 사용법
Toolkio 타임스탬프 변환기는 두 방향 변환이 즉시 돼요.
1. 숫자(예: 1711843200) 입력 → KST·UTC 날짜 동시 표시
2. 날짜 입력 → 초·밀리초 두 형식 동시 표시
3. 현재 시각 1초 단위 갱신 — 디버깅 비교용
4. 자릿수 자동 감지 — 10자리는 초, 13자리는 밀리초로 자동 처리
모든 계산이 브라우저에서 끝나요. 로그·내부 데이터 붙여넣어도 외부 서버로 안 빠져요.
지금 당장 할 수 있는 것
지금 다루고 있는 API 응답이나 로그 한 줄 가져와서 변환기에 붙여넣어 보세요. 시간대가 맞는지, 초·밀리초 단위가 맞는지 5초 안에 답이 나와요.
DB에 저장된 timestamp 컬럼 중 32비트인 게 있는지 확인하세요. MySQL이면 `SHOW COLUMNS FROM table_name`로 `TIMESTAMP` vs `DATETIME` 구분이 가능해요. `TIMESTAMP`만 있다면 2038 대비 마이그레이션 일정을 잡아두세요.
언어·DB별 타임스탬프 함수 정리표
현장에서 환경 옮길 때마다 함수 이름이 미묘하게 달라서 한 번씩 헷갈려요. 자주 쓰는 것만 정리해 둘게요.
**현재 시각 가져오기**
- JavaScript: `Date.now()` — 밀리초(13자리)
- Python 3: `time.time()` — 초(소수점 포함 float), `int(time.time())`로 정수화
- PHP 8: `time()` — 초, `microtime(true)` — 마이크로초 float
- Java: `System.currentTimeMillis()` — 밀리초, `Instant.now().getEpochSecond()` — 초
- Go 1.22+: `time.Now().Unix()` — 초, `.UnixMilli()` — 밀리초
- Ruby: `Time.now.to_i` — 초
- Swift: `Date().timeIntervalSince1970` — 초(Double)
**DB**
- MySQL 8: `UNIX_TIMESTAMP()` — 초, `CURRENT_TIMESTAMP(3)` — 밀리초 정밀도
- PostgreSQL 16: `EXTRACT(EPOCH FROM NOW())` — 초(double precision), `NOW()` — TIMESTAMPTZ
- SQLite: `strftime('%s', 'now')` — 초 문자열, 정수 변환 필요
- MongoDB: `new Date()` — ISODate, `new Date().getTime()` — 밀리초
- Redis: `TIME` 명령 — `[초, 마이크로초]` 두 원소 배열
**문자열 → 타임스탬프 파싱**
- JS: `new Date('2024-03-31T00:00:00Z').getTime()` — Z 접미어 빠뜨리면 로컬 시간 해석
- Python: `datetime.fromisoformat('2024-03-31T00:00:00+00:00').timestamp()`
- Java: `Instant.parse('2024-03-31T00:00:00Z').getEpochSecond()`
핵심 함정 두 개만 기억하세요. 첫째, JS는 디폴트 밀리초인데 백엔드는 초인 곳이 많아요. 1000 곱하기·나누기 한 줄을 빠뜨려서 시각이 1970년 어딘가로 표시되는 사고가 가장 흔해요. 둘째, Z(Zulu, UTC)가 빠진 ISO 문자열을 파싱하면 언어마다 해석이 달라요. JS는 로컬, Java는 에러, Python은 naive datetime이 나와서 이후 비교가 다 망가져요. ISO 8601 표기에는 항상 시간대를 박아 두세요.
윤초·서머타임·시간 동기화 — 자주 빠지는 함정
타임스탬프가 단순해 보여도 운영 중 부딪히는 시간 관련 함정이 있어요. 미리 알아두면 사고 디버깅 시간이 확 줄어요.
**1. 윤초(Leap Second)**
UTC는 지구 자전 속도와 원자 시계의 차이를 보정하려고 가끔 1초를 추가해요. 1972년 이후 27회 삽입됐고, 가장 최근은 2016년 12월 31일이에요. ITU와 BIPM은 2035년 이전에 윤초 폐지를 결정했어요(2022년 결의). 이 결정이 발효되기 전까지는 가능성이 남아 있고, 윤초 발생 시 23:59:60이라는 정상 범위를 벗어난 시각이 1초 동안 존재해요. Cloudflare는 2017년 윤초 직후 라우터 한 곳이 멈춘 사고를 공개 포스트로 남겼어요. 안전한 운영 패턴은 NTP 'leap smearing'(구글·AWS·Cloudflare가 채택) — 윤초 1초를 24시간에 걸쳐 살짝씩 분산해서 흡수해요.
**2. 서머타임(DST) 경계**
미국·유럽·일부 호주 지역은 3월·11월·4월·10월에 시각이 1시간 점프해요. 2026년 미국 DST 종료는 11월 1일이에요. 그 새벽 1:30이 두 번 존재해요. 'America/New_York' 같은 IANA 시간대 문자열로 변환하면 자동 처리되지만, '-05:00'처럼 고정 오프셋만 박아두면 6개월 후 1시간 어긋나요. 시간대는 이름으로 저장, 오프셋으로 저장은 비추예요.
**3. NTP 동기화 어긋남**
서버 클럭이 NTP로 동기화 안 되어 있으면 분 단위, 심지어 시간 단위 어긋남이 흔해요. 분산 시스템에서는 5초만 어긋나도 캐시 만료·JWT 검증·rate limit이 다 망가져요. 리눅스에서는 `chronyc tracking` 또는 `timedatectl status`로 동기화 상태를 확인하세요. 클라우드 환경(AWS·GCP)은 보통 자동 동기화돼 있어요.
**4. 타임존 데이터(tzdata) 갱신**
각국 정부가 시간대를 변경하면(예: 2018년 북한 +08:30 → +09:00 복귀) IANA tzdata가 업데이트돼요. OS·라이브러리에 tzdata가 오래되면 변경 후 시각이 어긋나요. Java는 별도 `tzupdater`, Node는 ICU가 묶여 있는 빌드와 시스템 tzdata에 의존하는 빌드가 달라요. 운영 컨테이너는 tzdata를 명시적으로 핀(pin)하고, 분기마다 갱신하세요.
운영 중 자주 만나는 타임스탬프 디버깅 시나리오
타임스탬프 관련 사고는 대부분 비슷한 패턴으로 반복돼요. 한 번씩 겪고 나면 다음에 빠르게 알아챌 수 있어요. 자주 만나는 시나리오를 정리해 둘게요.
**시나리오 1: 화면에 1970년 1월이 뜸**
사용자 가입일에 '1970-01-01 09:00:00 KST'가 보이면 거의 확실하게 타임스탬프 0(에포크 시작점)을 그대로 받아왔다는 뜻이에요. 백엔드에서 null·undefined·빈 문자열을 받아서 자동으로 0으로 형변환된 거거든요. 보통 두 군데를 확인해요. 첫째, DB 컬럼이 NOT NULL인데 INSERT 시 값을 안 넘겼나. 둘째, ORM의 hooks(beforeCreate)에서 잘못된 fallback을 줬나. 임시방편으로 화면에서만 0 체크하지 말고 데이터 흐름 전체를 점검하세요.
**시나리오 2: 어제 가입한 사용자 시각이 +3시간 어긋남**
DST 적용 국가 사용자에게 자주 보이는 패턴이에요. 백엔드는 UTC, DB는 'America/New_York'으로 저장됐는데 화면에서 다시 +9 적용하는 식의 이중 변환이 원인이에요. 시각 표기를 한 곳(보통 화면 직전)에서만 변환하는 규칙을 세워두면 깔끔해요.
**시나리오 3: JWT 토큰이 즉시 만료**
토큰을 발급하자마자 'expired'라고 거절되는 사고는 서버와 클라이언트 클럭 차이가 원인일 때가 많아요. JWT 라이브러리 대부분은 `iat`(issued at)를 발급 서버 시각으로 박는데, 검증 서버 시각이 30초 빠르면 'iat가 미래'로 인식돼서 즉시 invalid예요. clockTolerance 옵션으로 1~2분 여유를 주거나, 모든 인스턴스를 NTP로 동기화하세요.
**시나리오 4: 로그가 미래에 찍힘**
Grafana·Datadog 같은 로그 시각화 도구에서 미래 시각 로그가 찍히면 보통 라이브러리가 `Date.now()`를 밀리초로 보내야 하는데 초로 보내거나, 반대 케이스예요. 로그 스키마에 `timestamp_ms` 같이 단위를 명시하는 필드명을 쓰면 이런 사고가 줄어요.
**시나리오 5: 같은 정렬 쿼리에서 순서가 흔들림**
타임스탬프 정밀도가 초 단위면 같은 초에 여러 row가 INSERT 됐을 때 정렬 순서가 비결정적이에요. 정확한 순서가 필요하면 `created_at` 외에 자증 ID(`id ASC`)를 보조 정렬 키로 추가하세요. 또는 DB 컬럼을 마이크로초·나노초 정밀도로 바꿔주세요. PostgreSQL은 기본 마이크로초, MySQL은 `TIMESTAMP(6)`로 마이크로초까지 가능해요.