Skip to content
블로그로 돌아가기
튜토리얼

유닉스 타임스탬프 완벽 가이드: 정밀도, 시간대, DST 팁 | 온라인 개발자 도구

유닉스 타임스탬프를 한눈에: 에폭의 기원, 초·밀리초·마이크로초 변환, 시간대 처리, DST 함정, JavaScript·Python·Go 코드 예제를 온라인 가이드로 정리했습니다.

14분 소요

유닉스 타임스탬프 완벽 가이드: 초·밀리초·마이크로초 변환과 시간대 베스트 프랙티스

유닉스 타임스탬프(Unix timestamp)는 “유닉스 에폭(1970년 1월 1일 00:00:00 UTC)으로부터 경과한 시간”을 나타냅니다. 전 세계 웹 서버의 95% 이상, 데이터베이스 시스템의 90% 이상이 내부적으로 이 표현을 사용합니다. 이 가이드에서는 정밀도 차이, 언어별 구현, 시간대 처리, 일광 절약 시간제 주의점을 정리합니다.

유닉스 타임스탬프의 기원과 정의

유닉스 에폭은 1970년 1월 1일 00:00:00 UTC에 시작합니다. 유닉스 타임스탬프 0은 바로 이 순간을 가리키고, 1262304000은 2010-01-01 00:00:00 UTC를 나타냅니다. 기본적으로 윤초(leap second)의 영향은 무시합니다.

원래는 32비트 부호 있는 정수로 저장했기 때문에 Y2038 문제가 생겼습니다. 즉, 최댓값이 2038년 1월 19일 03:14:07 UTC에 오버플로하는 셈입니다. 요즘 시스템은 64비트 정수를 사용하므로, 표현 가능한 시간 범위가 크게 확장되었습니다.

시간 정밀도 비교

단위1초당 개수자릿수대표 용도
초 (s)110자리전통적인 Unix/Linux 시스템
밀리초 (ms)1,00013자리JavaScript, Java, 로깅
마이크로초 (μs)1,000,00016자리분산 트레이싱, 데이터베이스
나노초 (ns)1,000,000,00019자리Go 언어, 성능 분석

실용적인 판별법: 일반적으로 10자리는 초, 13자리는 밀리초, 16자리는 마이크로초, 19자리는 나노초로 기억하면 편리합니다.

JavaScript의 타임스탬프

JavaScript(자바스크립트)는 기본 단위로 밀리초를 사용합니다. Date() 생성자는 입력 숫자를 밀리초로 간주하므로, 초 단위 타임스탬프를 넘길 때는 1000을 곱해야 합니다.

// 현재 유닉스 타임스탬프 가져오기 (밀리초)
const timestampMs = Date.now();
console.log(timestampMs);  // 출력 예시: 1692268800123

// 초 단위 타임스탬프: 밀리초를 1000으로 나누고 내림
const timestampSec = Math.floor(Date.now() / 1000);
console.log(timestampSec); // 출력 예시: 1692268800

// 유닉스 타임스탬프를 Date 객체로 변환
let ts = 1692268800;
let date = new Date(ts * 1000);
console.log(date.toISOString()); // "2023-08-17T16:00:00.000Z"

Python의 타임스탬프

Python의 time.time()은 초 단위의 유닉스 타임스탬프를 부동소수점 값으로 반환합니다.

import time
from datetime import datetime, timezone

# 현재 유닉스 타임스탬프 가져오기 (초, 부동소수점)
now_sec = time.time()
print(now_sec)  # 출력 예시: 1692268800.123456

# 밀리초 가져오기 (정수)
now_millis = int(time.time() * 1000)
print(now_millis)  # 예시: 1692268800123

# 나노초 가져오기 (Python 3.7 이상)
now_nanos = time.time_ns()
print(now_nanos)  # 예시: 1692268800123456789

# datetime으로 변환
ts = 1692268800
dt_local = datetime.fromtimestamp(ts)
dt_utc = datetime.fromtimestamp(ts, timezone.utc)
print(dt_local.strftime("%Y-%m-%d %H:%M:%S"))  # 로컬 시각
print(dt_utc.strftime("%Y-%m-%d %H:%M:%S"))    # UTC 시각

Go 언어의 타임스탬프

Go의 time 패키지는 여러 단계의 정밀도를 지원합니다.

package main

import (
    "fmt"
    "time"
)

func main() {
    // 현재 유닉스 타임스탬프 가져오기
    sec := time.Now().Unix()        // 초
    msec := time.Now().UnixMilli()  // 밀리초 (Go 1.17 이상)
    nsec := time.Now().UnixNano()   // 나노초

    fmt.Println(sec)   // 예시: 1692268800
    fmt.Println(msec)  // 예시: 1692268800123
    fmt.Println(nsec)  // 예시: 1692268800123456789

    // 타임스탬프를 Time 객체로 변환
    t := time.Unix(sec, 0)
    fmt.Println(t.UTC())
    fmt.Println(t)
}

흔한 실수와 베스트 프랙티스

실수 예시: 단위가 모호함

// ✗ 오류: 타임스탬프 단위가 모호함
const timestamp = 1692268800;
const date = new Date(timestamp); // 오류: 밀리초로 해석되어 1970년이 표시됨

// ✓ 올바름: 단위를 명확히 구분
const timestampSec = 1692268800;
const timestampMs = 1692268800000;
const dateFromSec = new Date(timestampSec * 1000);

필드 네이밍 베스트 프랙티스

log_entry = {
    "timestamp_ms": 1692268800123,           # 밀리초 단위
    "timestamp_iso": "2023-08-17T16:00:00Z", # ISO 8601 UTC
    "event_type": "user_login",
    "user_id": 12345
}

시간대 처리의 함정

시간대를 다룰 때 주의해야 할 네 가지 함정이 있습니다.

  1. 로컬 시각과 UTC 시각의 혼동 — 유닉스 타임스탬프는 언제나 UTC 기준입니다. 로컬 시각 변환은 표시 시점에만 하십시오.
  2. 시간대 정보를 저장하지 않음 — 모호성을 만듭니다. 항상 UTC로 저장하거나 오프셋을 함께 기록하십시오.
  3. 시스템 간 시간대 불일치 — 모든 시스템이 동일한 시간대 기준(UTC 권장)을 사용해야 합니다.
  4. DST 오프셋을 수동으로 계산 — 내장 라이브러리에 맡기고, 하드코딩은 피하십시오.

권장 접근법은 **“저장은 표준화, 표시는 현지화”**입니다. 저장과 전송 단계에서는 UTC 또는 유닉스 타임스탬프를 사용해 일관성을 확보하고, 표시 시점에는 사용자의 시간대에 맞춰 포매팅합니다. 한국 서비스라면 KST 변환은 표시 레이어에서만 수행하는 편이 안전합니다.

일광 절약 시간제(Daylight Saving Time, DST) 이슈

DST는 문제가 되는 두 가지 구간을 만듭니다.

  • 건너뛰는 시간: DST 시작 시 시계가 앞으로 튑니다. 예를 들어 02:00이 곧장 03:00으로 넘어가면서 02:30은 존재하지 않는 시각이 됩니다.
  • 겹치는 시간: DST 종료 시 01:00–01:59가 두 번 나타나 모호성을 만듭니다.

유닉스 타임스탬프 자체는 연속적이며 DST의 영향을 받지 않지만, 로컬 시각으로 변환할 때는 세심한 처리가 필요합니다. 한국(KST)은 현재 DST를 채택하지 않지만, 해외 서버나 사용자를 다룰 때는 DST 대응이 필수입니다.

로깅, 데이터베이스, API 베스트 프랙티스

로깅 시스템

UTC 시간대 + ISO 8601 포맷으로 통일하십시오. 필요에 따라 밀리초 또는 마이크로초 정밀도의 타임스탬프를 함께 기록합니다.

데이터베이스

시간대를 포함한 네이티브 날짜·시각 타입을 우선 사용하십시오. 숫자형 타임스탬프에는 BIGINT를 쓰고, 필드 이름으로 단위를 명시합니다(예: *_epoch_ms).

API

포맷과 단위를 명확히 규정하십시오. 외부 인터페이스에는 ISO 8601(가독성이 높고 시간대 정보 포함)을 쓰고, 내부 시스템에서는 숫자형 타임스탬프를 써도 되지만 문서에 단위를 반드시 기재해야 합니다.

자주 발생하는 오류 시나리오

  • 13자리 파싱 실패: 밀리초 타임스탬프를 초 단위를 기대하는 함수에 넣으면 오버플로가 발생합니다. 우선 자릿수로 단위부터 확인하십시오.
  • 포맷 불일치: 잘못된 날짜, 시간대 누락, DST 전환 시점이 파싱 오류의 원인이 됩니다.
  • 시간대 오프셋 어긋남: 정수 시간 단위로 어긋난다면(예: ±8시간) 시간대 통일이 누락되었을 가능성이 높습니다. 내부에서는 UTC 사용을 권장합니다.
  • 숫자 오버플로: 32비트 시스템은 2038년 제약을 받으므로 64비트 정수를 우선 고려하십시오.

자주 묻는 질문

왜 하필 1970년 1월 1일인가요?

유닉스 운영체제 개발이 1970년에 시작되었고, 10년 단위로 끊어지는 해라 기억하고 계산하기 쉬웠습니다. 32비트 정수로 1970년부터 2038년까지 표현할 수 있었고, 당시에는 그 정도로도 충분했습니다.

초와 밀리초는 어떻게 구분하나요?

세 가지 방법이 있습니다. 자릿수로 판단하기(10자리=초, 13자리=밀리초, 16자리=마이크로초, 19자리=나노초), 파싱 후 결과가 타당한지 역검산하기, 또는 온라인 변환 도구 사용하기입니다.

Y2038 문제는 아직 유효한가요?

64비트 정수를 쓰는 최신 시스템에서는 대부분 해소되었습니다. 주요 프로그래밍 언어는 이미 64비트 타임스탬프를 지원합니다. 다만 레거시 32비트 시스템이나 임베디드 기기는 여전히 주의가 필요합니다.

DST가 문제를 일으키는 이유는 무엇인가요?

건너뛰는 시간대의 불연속성과 겹치는 시간대의 중복이 파싱 모호성을 낳기 때문입니다. 시스템마다 모호한 시각을 처리하는 전략이 다릅니다. 해결책은 내부에서 UTC를 쓰고 표시 시점에만 로컬 시각으로 변환하는 것입니다.

JavaScript와 Python의 타임스탬프가 다른 이유는 무엇인가요?

JavaScript의 Date.now()는 밀리초(13자리)를 반환하고, Python의 time.time()은 초(마이크로초 정밀도의 부동소수점)를 반환하기 때문입니다. 변환하려면 1000으로 나누거나 곱해야 합니다.

최신 동향과 앞으로의 전망

  • 고정밀화: 금융 거래, IoT, 실시간 시스템에서는 나노초 수준의 정확도가 요구됩니다
  • 분산 동기화: NTP와 PTP 프로토콜로 정확도가 개선되고 있습니다
  • 블록체인 타임스탬프: 암호 자산은 변조 방지와 고정밀을 겸비한 타임스탬프가 필요합니다
  • 성능 최적화: SIMD 명령어, 메모리 캐싱, 병렬 처리로 변환 속도가 빨라지고 있습니다

직접 해보기

유닉스 타임스탬프 변환기에서 유닉스 타임스탬프를 즉시 변환하실 수 있습니다. 초·밀리초·마이크로초를 자동으로 감지하며, 100% 브라우저 안에서 동작합니다.

PostgreSQL을 쓰고 계신가요? PostgreSQL의 timestamp 컬럼에는 정확히 무엇이 저장될까?에서 Postgres 특유의 시간대 함정을 확인해 보시기 바랍니다.

요약

이 종합 가이드에서는 유닉스 타임스탬프의 핵심을 다뤘습니다. 에폭의 기원과 다중 정밀도 변환부터 언어별 구현, 시간대 처리, DST 함정, 그리고 로깅·데이터베이스·API 설계에서의 엔지니어링 베스트 프랙티스까지 이어집니다. 핵심 한 줄: 저장은 UTC로, 표시는 로컬 시각으로, 타임스탬프 단위는 언제나 명시적으로.

타임스탬프는 개발자 도구 가운데 한 조각일 뿐입니다. 인코딩, 해싱, 데이터 변환 등 다른 브라우저 기반 도구는 홈페이지의 도구 카테고리에서 함께 살펴볼 수 있습니다.