Guide

윤초 처리

소개

윤초는 협정 세계시(UTC)를 지구의 자전과 동기화하기 위해 가끔씩 1초씩 조정하는 것입니다. 이는 소프트웨어 개발에서 시간 처리의 가장 복잡한 측면 중 하나를 나타냅니다.

간략한 요약: UTC는 UT1(태양시)의 0.9초 이내를 유지하기 위해 윤초를 추가합니다. 2026년 1월 기준으로 1972년 이후 37개의 윤초가 추가되어 UTC가 국제 원자시(TAI)보다 37초 느려졌습니다.

윤초란 무엇입니까?

정의

윤초는 다음을 고려하여 UTC에 적용되는 1초 조정입니다.

  1. 지구의 자전 감속: 지구의 자전이 점차 느려지고 있습니다.
  2. 불규칙한 자전율: 지구의 자전 속도는 예측할 수 없을 정도로 다양합니다.
  3. UT1-UTC 발산: UTC를 태양시(UT1)의 ±0.9초 이내로 유지합니다.
Leap Second Formula:
  If UT1 - UTC > 0.9 seconds → Add positive leap second
  If UT1 - UTC < -0.9 seconds → Add negative leap second

Result: UTC stays synchronized with Earth's rotation

윤초 작동 방식

윤초가 추가되면 UTC 날짜의 마지막 분은 60초가 아닌 61초가 됩니다.

Normal Day (no leap second):
  23:59:58 UTC
  23:59:59 UTC
  00:00:00 UTC (next day)

Leap Second Day:
  23:59:58 UTC
  23:59:59 UTC
  23:59:60 UTC  ← Leap second!
  00:00:00 UTC (next day)

참고: 음의 윤초는 실제로 발생한 적이 없지만 지구 자전이 갑자기 가속되면 이론적으로 가능합니다.

윤초의 역사

타임라인

연도이벤트UTC-TAI 오프셋
1972첫 번째 윤초 추가+10초
1972-198412 윤초 추가+22초
1985-19958 윤초 추가+29초
1996-2005윤초 3개 추가+32초
2008-2016윤초 3개 추가+35초
2017마지막 윤초+36초
2025년미래 윤초+37초

최근 윤초

All Leap Seconds (1972 - 2025):
  - 1972-06-30: +1 second (UTC-TAI = +11s)
  - 1972-12-31: +1 second (UTC-TAI = +12s)
  - 1973-12-31: +1 second (UTC-TAI = +13s)
  - 1974-12-31: +1 second (UTC-TAI = +14s)
  - 1975-12-31: +1 second (UTC-TAI = +15s)
  - 1976-12-31: +1 second (UTC-TAI = +16s)
  - 1977-12-31: +1 second (UTC-TAI = +17s)
  - 1978-12-31: +1 second (UTC-TAI = +18s)
  - 1979-12-31: +1 second (UTC-TAI = +19s)
  - 1981-06-30: +1 second (UTC-TAI = +20s)
  - 1982-06-30: +1 second (UTC-TAI = +21s)
  - 1983-06-30: +1 second (UTC-TAI = +22s)
  - 1985-06-30: +1 second (UTC-TAI = +23s)
  - 1987-12-31: +1 second (UTC-TAI = +24s)
  - 1988-12-31: +1 second (UTC-TAI = +25s)
  - 1989-12-31: +1 second (UTC-TAI = +26s)
  - 1990-12-31: +1 second (UTC-TAI = +27s)
  - 1992-06-30: +1 second (UTC-TAI = +28s)
  - 1993-06-30: +1 second (UTC-TAI = +29s)
  - 1994-06-30: +1 second (UTC-TAI = +30s)
  - 1995-12-31 +1 second (UTC-TAI = +31s)
  - 1997-12-31 +1 second (UTC-TAI = +32s)
  - 1998-12-31 +1 second (UTC-TAI = +33s)
  - 1999-12-31 +1 second (UTC-TAI = +34s)
  - 2000-12-31 +1 second (UTC-TAI = +35s)
  - 2005-12-31 +1 second (UTC-TAI = +36s)
  - 2008-12-31: +1 second (UTC-TAI = +37s)

윤초의 미래

국제전기통신연합(ITU)은 2035년까지 윤초를 폐지하여 전 세계적으로 시간 처리를 단순화하는 것을 고려하고 있습니다.

중요: 윤초가 폐지되면 UTC는 점차 태양시와 멀어지게 됩니다. 이는 천문학자, 소프트웨어 개발자, 시간 기록 조직 사이에서 논쟁의 여지가 있는 주제입니다.

TAI 대 UTC

국제원자시(TAI)

TAI는 전 세계 원자시계의 가중 평균을 기반으로 한 시간 척도입니다. 여기에는 윤초가 포함되지 않으므로 완벽하게 균일한 시간 척도가 됩니다.

TAI Characteristics:
  - Based on: 400+ atomic clocks worldwide
  - Precision: ±0.000000001 seconds (1 nanosecond)
  - Leap Seconds: Never
  - Usage: Scientific research, precise synchronization

Current TAI-UTC Offset: +37 seconds (as of January 2026)

TAI와 UTC 간의 변환

// Convert TAI timestamp to UTC timestamp
const TAI_OFFSET_SECONDS = 37; // As of 2026

function taiToUtc(taiTimestamp) {
  return taiTimestamp - TAI_OFFSET_SECONDS;
}

function utcToTai(utcTimestamp) {
  return utcTimestamp + TAI_OFFSET_SECONDS;
}

// Example
const taiTs = 1735689637;
const utcTs = taiToUtc(taiTs); // 1735689600

console.log('TAI Timestamp:', taiTs);
console.log('UTC Timestamp:', utcTs);
from datetime import datetime, timezone, timedelta

TAI_OFFSET_SECONDS = 37  # As of 2026

def tai_to_utc(tai_timestamp):
    return tai_timestamp - TAI_OFFSET_SECONDS

def utc_to_tai(utc_timestamp):
    return utc_timestamp + TAI_OFFSET_SECONDS

# Example
tai_ts = 1735689637
utc_ts = tai_to_utc(tai_ts)  # 1735689600

print(f'TAI Timestamp: {tai_ts}')
print(f'UTC Timestamp: {utc_ts}')

프로그래밍에서 윤초 처리하기

자바스크립트

JavaScript의 Date 객체는 윤초를 직접 지원하지 않습니다. 23:59:60 타임스탬프를 23:59:59로 반복합니다.

// Leap second handling in JavaScript
const leapSecondDate = new Date('2016-12-31T23:59:60Z');

// JavaScript treats this as 23:59:59Z
console.log(leapSecondDate.toISOString()); // "2016-12-31T23:59:59.000Z"

// Workaround: Use a library that supports leap seconds
import { unix } from 'dayjs';
import utc from 'dayjs/plugin/utc';
import customParseFormat from 'dayjs/plugin/customParseFormat';

// Note: Day.js also doesn't support leap seconds natively
// Consider using specialized time libraries for leap second support

파이썬

Python의 datetime 모듈은 윤초에 대한 지원이 제한되어 있습니다. 표준 라이브러리는 23:59:60을 나타내지 않습니다.

# Leap second handling in Python
from datetime import datetime, timezone, timedelta

# Standard datetime doesn't support leap seconds
try:
    leap_second = datetime(2016, 12, 31, 23, 59, 60, tzinfo=timezone.utc)
except ValueError as e:
    print(f'Error: {e}')  # ValueError: second must be in 0..59

# Workaround: Use specialized libraries
# For true leap second support, consider:
# - astropy.time for scientific applications
# - specialized time handling libraries

자바

Java 8+ java.time 패키지는 Instant 클래스에서 윤초를 지원합니다.

import java.time.Instant;
import java.time.temporal.ChronoUnit;

// Leap second handling in Java
Instant leapSecondInstant = Instant.parse("2016-12-31T23:59:60Z");

// Java correctly handles leap second in Instant
System.out.println("Leap Second: " + leapSecondInstant);

// Check if a timestamp contains a leap second
Instant timestamp = Instant.parse("2016-12-31T23:59:60Z");
boolean isLeapSecond = timestamp.getNano() == 0 &&
                       timestamp.getEpochSecond() % 60 == 59;

System.out.println("Is Leap Second: " + isLeapSecond);

이동

Go의 time 패키지에는 기본 윤초 지원이 없습니다.

package main

import (
    "fmt"
    "time"
)

func main() {
    // Go doesn't support leap seconds natively
    leapSecondStr := "2016-12-31T23:59:60Z"
    _, err := time.Parse(time.RFC3339, leapSecondStr)

    if err != nil {
        fmt.Println("Error:", err)
        // Go will reject leap second timestamps
    }
}

시간 번짐

타임 스미어링이란 무엇인가요?

타임 스미어링은 윤초 조정을 즉시 적용하는 대신 일정 기간(보통 12~24시간)에 걸쳐 점진적으로 분배하는 기술입니다.

Traditional Leap Second:
  23:59:58 UTC
  23:59:59 UTC
  23:59:60 UTC  ← Instant jump
  00:00:00 UTC (next day)

Smeared Leap Second (24-hour smear):
  Each second is ~1.16ms longer for 24 hours
  No instant jump, smooth transition

번짐 구현

시스템스미어링 방법기간
구글 트루타임선형 얼룩24시간
Amazon 시간 동기화 서비스선형 얼룩24시간
NTP 풀선택적 번짐1~24시간
리눅스커널 단계(스미어 없음)인스턴트

참고: 시간 번짐은 대규모 분산 시스템에서 동기화 문제를 방지하기 위해 사용됩니다. 그러나 이는 자체적인 문제를 야기합니다. 번진 시간은 표준 UTC가 아니며 다른 시간 시스템으로 안정적으로 변환할 수 없습니다.

IETF RFC 8536 권장사항

RFC 8536은 소프트웨어 시스템의 윤초 처리에 대한 지침을 제공합니다.

주요 권장 사항

  1. 내부 시간에 TAI 사용: 정확성을 위해 TAI 타임스탬프를 내부적으로 저장합니다.
  2. 디스플레이 전용 UTC로 변환: 사용자에게 표시할 때만 윤초 오프셋을 적용합니다.
  3. 동기화를 위해 NTP 사용: NTP 서버에서 정확한 시간 가져오기
  4. 윤초 처리 문서화: 시스템이 윤초를 처리하는 방법을 명확하게 문서화하세요.
  5. 윤초 이벤트 테스트: 테스트에서 윤초 전환을 시뮬레이션합니다.

모범 사례

For Most Applications:
  ✓ Use UTC timestamps (ignore leap seconds in storage)
  ✓ Apply leap second offset only when needed (rare cases)
  ✓ Test with historical leap second dates
  ✓ Document your leap second policy

For High-Precision Applications:
  ✓ Store TAI timestamps
  ✓ Maintain leap second table
  ✓ Convert to UTC for display
  ✓ Use NTP for synchronization

일반적인 문제 및 해결 방법

문제 1: 윤초 동안 시간 점프

문제: 윤초 전환 중에 시스템이 1초 점프를 경험합니다.

해결책: 시간 번짐을 사용하거나 윤초 인식을 구현합니다.

// Time smearing example (simplified)
function smearedTime(timestamp, leapSecondDate) {
  const diffHours = (timestamp - leapSecondDate) / (1000 * 60 * 60);
  const smearDuration = 24; // 24 hours
  const smearFactor = Math.min(Math.max(diffHours / smearDuration, 0), 1);

  return timestamp + smearFactor * 1000; // Add up to 1 second over 24 hours
}

문제 2: 데이터베이스 쿼리 실패

문제: '23:59:60'과 같은 타임스탬프가 대부분의 데이터베이스에서 유효하지 않기 때문에 윤초 동안 쿼리가 실패합니다.

해결책: 윤초 없이 타임스탬프를 저장하고 윤초 동작을 문서화합니다.

-- Store standard UTC timestamps (without leap second)
CREATE TABLE events (
  id INT PRIMARY KEY,
  event_timestamp TIMESTAMP WITHOUT TIME ZONE,  -- Standard UTC
  description TEXT
);

-- Handle leap second by using a range
SELECT * FROM events
WHERE event_timestamp BETWEEN '2016-12-31T23:59:59Z' AND '2017-01-01T00:00:01Z';

문제 3: 윤초 동안 오류 로깅

문제: 윤초 동안 로그 파일에 중복되거나 순서가 잘못된 타임스탬프가 표시됩니다.

해결책: 고해상도 타임스탬프와 고유 시퀀스 식별자를 사용하세요.

# Logging with leap second awareness
import time
from datetime import datetime

def log_event(message):
    # Use millisecond precision to handle leap seconds
    timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
    sequence_id = time.time_ns()  # Nanosecond precision

    print(f'[{timestamp}] [{sequence_id}] {message}')

시나리오별 코드 예시

시나리오 1: 윤초 오프셋을 사용하여 타임스탬프 변환

const LEAP_SECONDS = 37; // As of 2026

// Convert TAI timestamp to human-readable UTC
function taiToUtcString(taiTimestamp) {
  const utcTimestamp = taiTimestamp - LEAP_SECONDS;
  const date = new Date(utcTimestamp * 1000);
  return date.toISOString();
}

// Example
const taiTs = 1735689637;
console.log(taiToUtcString(taiTs)); // "2026-01-01T00:00:00.000Z"
from datetime import datetime, timezone, timedelta

LEAP_SECONDS = 37  # As of 2026

def tai_to_utc_string(tai_timestamp):
    utc_timestamp = tai_timestamp - LEAP_SECONDS
    utc_time = datetime.fromtimestamp(utc_timestamp, timezone.utc)
    return utc_time.isoformat()

# Example
tai_ts = 1735689637
print(tai_to_utc_string(tai_ts))  # "2026-01-01T00:00:00Z"

시나리오 2: 날짜가 윤초인지 확인

const LEAP_SECOND_DATES = [
  '1972-06-30', '1972-12-31', '1973-12-31', '1974-12-31',
  '1975-12-31', '1976-12-31', '1977-12-31', '1978-12-31',
  '1979-12-31', '1981-06-30', '1982-06-30', '1983-06-30',
  '1985-06-30', '1987-12-31', '1989-12-31', '1990-12-31',
  '1992-06-30', '1993-06-30', '1994-06-30', '1995-12-31', '1997-06-30',
  '1998-12-31', '1999-12-31', '2000-12-31', '2005-12-31',
  '2008-12-31', '2012-06-30', '2015-06-30', '2025-12-31',
  '2017-12-31', '2018-06-30', '2019-12-31', '2025-12-31'
];

function isLeapSecondDate(date) {
  const dateStr = date.toISOString().split('T')[0];
  return LEAP_SECOND_DATES.includes(dateStr);
}

// Example
const date = new Date('2016-12-31T23:59:59Z');
console.log(isLeapSecondDate(date)); // true
from datetime import datetime

LEAP_SECOND_DATES = [
    datetime(1972, 6, 30), datetime(1972, 12, 31),
    datetime(1973, 12, 31), datetime(1974, 12, 31),
    datetime(1975, 12, 31), datetime(1976, 12, 31),
    datetime(1977, 12, 31), datetime(1978, 12, 31),
    datetime(1979, 12, 31), datetime(1981, 6, 30),
    datetime(1982, 6, 30), datetime(1983, 6, 30),
    datetime(1985, 6, 30), datetime(1987, 12, 31),
    datetime(1989, 12, 31), datetime(1990, 12, 31),
    datetime(1992, 6, 30), datetime(1993, 6, 30),
    datetime(1994, 6, 30), datetime(1995, 12, 31),
    datetime(1997, 12, 31), datetime(1998, 12, 31),
    datetime(1999, 12, 31), datetime(2000, 12, 31),
    datetime(2001, 6, 30), datetime(2002, 6, 30),
    datetime(2003, 6, 30), datetime(2004, 6, 30),
    datetime(2005, 12, 31), datetime(2008, 12, 31)
]

def is_leap_second_date(date):
    return any(
        date.year == leap_date.year and
        date.month == leap_date.month and
        date.day == leap_date.day
        for leap_date in LEAP_SECOND_DATES
    )

# Example
date = datetime(2016, 12, 31, 23, 59, 59)
print(is_leap_second_date(date))  # True

도약초 처리 테스트

테스트 케이스

Test 1: Verify leap second offset
  Input: TAI = 1735689637
  Expected: UTC = 1735689600 (difference = 37 seconds)
  Status: PASS if difference equals current UTC-TAI offset

Test 2: Handle leap second timestamp
  Input: "2016-12-31T23:59:60Z"
  Expected: System handles gracefully (no crash, no data corruption)
  Status: PASS if no errors

Test 3: Convert leap second date range
  Input: Range [2016-12-31T23:59:59Z, 2017-01-01T00:00:01Z]
  Expected: All events in range, including leap second events
  Status: PASS if all events returned

Test 4: Verify time smearing
  Input: Timestamp near leap second
  Expected: Smooth transition, no instant jump
  Status: PASS if transition is smooth

모범 사례 요약

대부분의 애플리케이션에 사용

  1. 스토리지에서 윤초 무시(표준 UTC 타임스탬프 사용)
  2. 윤초 처리 정책을 문서화하세요.
  3. 역사적인 윤초 날짜를 사용하여 테스트
  4. 기본 시간 표준으로 UTC를 사용하세요.

고정밀 애플리케이션용

  1. 내부 계산을 위해 TAI 타임스탬프 저장
  2. 전환을 위한 윤초 테이블 유지
  3. 동기화를 위해 NTP 사용
  4. 중요한 코드 경로에 윤초 인식 구현

관련 도구

FAQ

Q: 윤초는 얼마나 자주 발생합니까?

A: 윤초는 1972년부터 27회(약 1~2년에 1회) 발생했지만, 최근 지구의 자전 속도가 느려지면서 그 빈도가 줄어들었습니다.

Q: 윤초는 영원히 계속되나요?

A: ITU는 2035년까지 윤초를 폐지하는 것을 논의하고 있습니다. 이로 인해 윤초 추가는 중단되지만 UTC는 점차 태양시와 멀어지게 됩니다.

Q: 내 애플리케이션에서 윤초를 처리해야 합니까?

A: 대부분의 애플리케이션에서는 표준 UTC 타임스탬프를 사용하지 않습니다. 시간이 중요한 시스템, 과학 애플리케이션 또는 분산 데이터베이스를 구축하는 경우에만 윤초를 처리하세요.

Q: 윤초 동안에는 어떤 일이 발생하나요?

A: UTC는 지구의 자전과 동기화를 유지하기 위해 1초(23:59:60)를 추가합니다. 대부분의 시스템은 23:59:59를 반복하거나 점프를 피하기 위해 시간 번짐을 사용합니다.

Q: 윤초 처리를 어떻게 테스트합니까?

A: 2016-12-31T23:59:60Z와 같은 과거 윤초 날짜를 사용하여 테스트하고 애플리케이션이 충돌하거나 잘못된 결과를 생성하지 않는지 확인하세요.