철도 시간표가 유닉스 시간이 되기까지 글을 읽고 정리한 내용
GMT #
19세기 초 지역마다 각자의 지방 평균시(Local Mean Time, LMT)를 사용했다.
지방 평균시란,
각자의 지역에서 태양이 최고 고도에 이르는 시각을 기준으로 삼는 시간 체계이다.
지방 평균시의 경우, 지역에 따라 시간이 달라질 수 있다.
(다양한 이유가 있겠지만, 글을 기반으로 하여) 철도 이슈로 인해 GMT 를 사용하자고 권고했다. (이렇게 GMT가 표준으로써 사용되기 사작한다.)
GMT 는 그리니치 천문대에서 관측한 평균시다.
- 본초 자오선은 그리니치를 지나는 자오선을 의미한다.
- 본초 자오선을 기준으로 15도마다 1시간 차이가 발생한다. 우리나라는 8시간 30분(GMT+08:30) 차이가 있다고 하는데, 127.5도 정도 차이가 발생하나보다.
- 자연스럽게 GMT는 세계시(Universal Time)의 기준이 되었다.
평균시라는 말을 쓰는 이유는, 태양시 기준으로 하루(오늘 태양 최고 고도 ~ 다음날 태양 최고 고도)가 정확하게 24시간이 걸리지 않기 때문이다. (지구 공전 등의 이유로)n 초 정도의 차이가 있다고 한다. 이러한 오차를 보정하기 위해 태양시의 평균을 낸 시간 쳬게를 평균시라고 한다.
즉, 하루가 24시간이라는 말은 하루가 ‘평균적으로 24시간’ 이라는 의미라고 한다.
태양시(Solar Time)
태양(태양의 고도)을 기준으로 측정하는 시간 체계
태양시 -> 원자시 #
20세기 초에 지구의 자전 속도가 불규칙하다는 사실을 알게 된다. (= 1초의 기준이 불규칙하다는 것을 발견한다.)
" 이전까지는 지구가 태양 주위를 한 바퀴 도는 시간인 31,556,992초를 기준으로 1 / 31556992를 1초의 정의로 사용했는데, 그 정의가 불규칙하다는 뜻이 된다. “
20세기 중반에 ‘원자를 기준으로 하는 1초’ 를 정의했다. 이를 원자시(Atomic Time)라고 한다. 원자시를 바탕으로 국제 원자시(International Atomic Time, TAI)표준을 정한다.
UTC #
1초의 기준이 태양시가 아닌 원자시로 변경됨에 따라 세계시의 기준도 GMT -> 원자시를 기반으로 한 협정 세계시(UTC) 로 변경되었다.
GMT vs UTC #
GMT와 UTC는 소수점 단위 정도의 차이만 발생할 뿐만 아니라, UTC도 그리니치 자오선과 거의 차이가 없는 자오선을 본초 자오선으로 삼고 있기 때문에 거의 차이가 없다.
때문에 일상에서는 UTC와 GMT를 혼용해서 사용한다.
시간대 #
서울은 UTC+08:30(GMT+08:30) 시간대에 놓인다.
하지만 각국의 시간대는 위 시간대를 그대로 따르지 않고 각자의 결정권에 따라 채택하여 사용한다.
- 서울은 UTC+08:30 시간대에 있지만 UTC+9(= Korean Standard Time, KST)를 사용한다.
- 파리는 UTC+0이 시간대에 있지만 UTC+1(= 중앙 유럽 표준시, Central European Time, CET)를 사용한다.
서머 타임(Summer Time) 혹은 일광절약시간제(Daylight Saving Time, DST)를 사용하는 국가에서는 계절에 따라 다른 시간대를 사용한다.
윤초 (Leap Second) #
태양시를 기준으로 하는 GMT와 원자시를 기준으로 하는 UTC 사이에는 오차가 발생함을 알게 됐다.
국제지구자전좌표국(IERS)은 태양시와 원자시 사이의 오차가 0.9초를 넘으면 이를 보정하기 위해 UTC에 1초를 더하거나 빼주어 보정한다. 이를 ‘윤초’ 라고 한다.
“태양시를 중심으로 사용하던 과거에는 지구의 자전 속도에 따라 시간 체계가 통째로 영향을 받았지만, 이제는 자전 속도가 변한만큼 윤초를 이용해 시간을 보정할 수 있게 된 것이다.”
윤초는 UTC 기준으로 6월 30일 23시 59분 59초 또는 12월 31일 23시 59분 59초에 적용한다. 1초 뒤를 0시 0분 0초가 아닌 23시 59분 60초로 표현하는 방식으로 적용한다. (IERS 는 통상 6개월 전에 윤초 적용을 예고한다.)
운영체제 시간 #
RTC #
컴퓨터는 RTC(Real Time Clock)라는 하드웨어 장치를 이용해 시간을 측정한다. (오늘날 시간 정보가 필요한 대부분의 전자기기에는 RTC가 들어있다.)
“대부분의 RTC는 수정 발진기(Quartz oscillator)를 사용하는데, 석영 결정에 전압을 걸었을 때 32.768kHz 주파수로 진동하는 것을 1초의 기준으로 삼는 원리다. 전원이 분리되어 있어서 컴퓨터가 꺼져도 RTC는 꾸준히 시간을 측정할 수 있다.”
유닉스 시간 #
운영체제 수준에서는 컴퓨터 시스템 전역에 설정된 시간을 **시스템 시간(System Time)**이라고 한다.
유닉스 계열의 운영체제는 UTC 기준 1970년 1월 1일 0시 0분 0초로부터 몇 초가 지났는지를 기준으로 시스템 시간을 관리한다. 이를 유닉스 시간, POSIX 시간, 에포크 시간(Epoch Time)이라고 한다.
에포크(Epoch)는 UTC 기준 1970년 1월 1일 자정을 일컫는다.
왜 “1970년 1월 1일” 일까?
이유는 대단하지 않다. 벨 연구소에서 유닉스 시스템을 개발한 데니스 리치가 당분간 오버플로우가 발생하지 않을마한 기원 날짜를 정했는데, 그게 1970년 1월 1일이었다고 한다.
유닉스 시간은 일반적으로 초 또는 밀리초 단위의 **타임스탬프(Timestamp)**로 표현한다. 예를 들어, UTC 기준 2022년 1월 1일 0시 0분 0초의 타임스탬프는 1640995200 이다.
이 타임스탬프(1640995200)는 시간대에 상관없이 **특정 순간(Instant)**을 표현하기 때문에 여러 시간에 대응될 수 있다.
시간대 | 1640998800 |
---|---|
UTC | 2022년 1월 1일 1시 0분 0초 |
CET (UTC+1) | 2022년 1월 1일 2시 0분 0초 |
KST (UTC+9) | 2022년 1월 1일 10시 0분 0초 |
“날짜, 시간 데이터에 대한 표준 규격은 ISO 8601에서 정의하고 있으며, 인터넷 표준으로는 RFC 3339에서 ISO 8601을 기반으로 정의하고 있다.”
“32비트 정수형을 사용하는 유닉스 시간은 2,147,483,647까지 밖에 표현할 수 없는데, 이로 인해 UTC 기준 2038년 1월 19일 3시 14분 7초를 지나면 오버플로우가 발생한다. 이를 2038년 문제(Year 2038 Problem, Y2K38)라고 한다. 64비트 시스템에서는 이미 유닉스 시간에 64비트 정수형을 사용하고 있지만, 구형 시스템은 조치가 필요하다.”
“다행히 대부분 언어의 표준 라이브러리는 단조 시계(Monotonic clock)를 사용할 수 있는 API를 제공한다. 단조 시계는 현재 시각을 가리키는 것이 아니라, 일반적으로 운영체제 구동 이후 몇 초가 지났는지를 가리키기 때문에 시간이 역행하지 않음을 보장한다. 파이썬의 표준 라이브러리 모듈 time에는 monotonic 함수가 있다.”
네트워크 타임 프로토콜(Network Time Protocol, NTP)을 이용해 원자 시계와 동기화된 서버로부터 시간을 가져와 동기화 하는 것도 가능하다. 여기에는 윤초도 적용될 것이며, 실제로 리눅스는 윤초를 처리하기 위해 NTP를 사용하고 있다.
NTP는 네트워크 지연 시간에 굉장히 민감할 것이다. (이 부분이 참 신기하다.)
NTP 시스템은 네트워크 지연을 최소화하기 위해 계층 구조를 이룬다. 0계층(Stratum 0)원자 시계와 직접 동기화되는 1계층 NTP 서버가 있고, 1계층과 동기화되는 2계층 NTP 서버가 있다. 한국에서는 한국표준과학연구원, 포항공과대학교 등에서 1계층 NTP 서버를 운영하고 있다.
애플리케이션 시간 #
불특정 다수에게 서비스할 때는 클라이언트가 어떤 시간대를 사용하고 있을지 특정할 수 없다. 다양한 시간대와 시간 표현을 고려해야 한다. 사용자는 UTC+0 시간대를 사용할 수 있고, UTC+9 시간대를 사용할 수도 있다. (또 어떤 사용자는 DST가 적용된 시간대를 사용할 수도 있다.)
이들 모두에게 효율적으로 서비스하기 위해, 서버 시간대는 보통 UTC+0으로 설정하면 좋을 것이다. (서버와 클라이언트 사이에 사용하는 API도 UTC+0 시간대를 전제로 하면 좋을 것이다.)
API를 통해 서버와 클라이언트가 시간 데이터를 주고받을 때는 몇 가지 고려사항이 있다.
- 연호를 사용하는 일본력은 어떻게 표현할 지
- 1970년 이전에 태어난 사람의 생일은 어떻게 표현할 지
- …
마이크로소프트 REST API 가이드라인은 ECMAScript 언어 명세에서 정의한 YYYY-MM-DDTHH:mm:ss.sssZ
포맷을 사용하는 DateLiteral
형식이나 시간의 종류(kind
)나 값(value
)을 함께 제공할 수 있는 StructuredDateLiteral
형식을 제시하고 있다.
현대의 대부분의 프로그래밍 언어들은 효과적으로 시간을 다루기 위한 타입 시스템을 갖추고 있다.
Java, Kotlin 의 경우 아래와 같은 클래스들이 제공된다.
- 에포크 시간의 타임스탬프를 다루는 Instant 클래스
- 기간을 다루는 Duration 클래스
- 시간대 정보가 없는 LocalDateTime 클래스
- 시간대 정보를 지닌 ZonedDateTime 클래스
만약 시간대 정보가 없는 LocalDateTime 클래스를 Instant 객체로 변환하고 싶다면 시간대 정보가 필요하다.
LocalDateTime.of(2022, 1, 1, 0, 0, 0).toInstant(ZoneOffset.of("+0900"))
LocalDateTime을 사용한다면 항상 UTC+0 기준임을 전제하는 것이 혼란을 줄이는 데 도움이 된다.
마찬가지로 LocalDateTime 시각을 특정 시간대의 시각으로 변환할 때, 취급할 시간대 정보를 명시해야 한다.
fun LocalDateTime.toKST(zoneId: ZoneId = ZoneId.of("UTC")) =
ZonedDateTime.of(this, zoneId)
.withZoneSameInstant(ZoneId.of("Asia/Seoul"))
.toLocalDateTime()
“UTC 시각을 KST 시각으로 바꾸기 위해 plusHours(9)를 적용하는 것보다 훨씬 우아하다.”
“한 국가의 표준시는 정치적, 사회적 이유로 언제든 변경될 수 있다. 한국은 1954년에 표준시를 GMT+9에서 GMT+08:30으로 변경했다가 1961년부터 다시 GMT+9(UTC+9)를 쓰고 있다. 2013년에는 표준시를 UTC+08:30으로 변경하는 표준시법 개정안이 발의되기도 했다. 또한 1948년부터 60년까지, 그리고 87년부터 88년까지 DST를 시행했다. 많은 시스템이 과거와 현재, 그리고 미래의 시간대 정보까지 정확하게 보장받기 위해 별도의 표준 데이터베이스인 TZDB(IANA Time Zone Database)를 참조한다. TZDB는 엔지니어, 역사학자 커뮤니티가 운영하고 있어 상당히 신뢰도가 높다.”