잃어버린 30분

Programming manipulate data

데이터 타입이 날짜-시간인 데이터를 다루는 것은 여간 성가시지 않습니다. 그런데도 아주 가끔 사용하는지라 작업을 할 때마다 새롭기만 합니다. 그런데, 날짜-시간 데이터에 심오한 것이 숨어 있더군요. 무엇이 또 즐겁게 해줄까요?

유충현 https://choonghyunryu.github.io (한국알사용자회)
2022-06-25

들어가기

역사에서 미래를 읽는다고 하나요?
과거를 이해해야 한다는 격언으로 치부되기에는 광오하기 그지 없습니다.
R에서 우연치 않게 접한 에러로 역사를 들춰서 감탄사를 내뱉었습니다. '오, 이런 것이!!!'

TL;DR

1961년 8월 10일 0시

“1961년 8월10일 0시. 한국에서 ’30분’이 일순간에 사라지는 사건이 일어났다. 나라 전체가 30분 후로 가는 타임머신을 탄 것처럼 9일 오후 11시30분이 눈 깜빡할 사이 10일 자정이 된 것이다. 모든 시계바늘은 30분 뒤로 맞춰졌고, 그 이후 사람들은 61년 8월9일보다 30분 빠른 삶을 살고 있다. 이때를 기점으로 우리나라는 표준시간을 규정하는 표준자오선을 동경 127도 30분에서 135도로 변경했다. 국가마다 기준이 되는 표준시는 영국의 그리니치 천문대(경도 0)를 기준으로 하며 경도가 15도 달라질 때마다 1시간씩 차이가 나게 된다. 표준자오선 변경으로 우리나라는 그리니치 표준시보다 9시간 빨라졌고, 동경 135도 자오선이 지나는 일본과 표준시가 일치하게 됐다. 양국간 시차가 전혀 나지 않는 이유다.”1

어, 여기서 왜 에러가 발생하지?

회사의 직원으로부터 에러가 발생한다는 코드를 접했습니다. 오라클 DBMS와 연동하는 작업을 수행하는 과정에서 특정 날짜가 표현되지 않는다는 것입니다.

> oraSql("SELECT TO_DATE('1961-08-10 00:00:00', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL ")
NULL
> oraSql("SELECT TO_DATE('1961-08-11 00:00:00', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL ")
TO_DATE('1961-08-1100:00:00','YYYY-MM-DDHH24:MI:SS')
1                                        1961-08-11

'1961-08-10 00:00:00'에서 에러가 발생한 것입니다. 즉, 해당일시를 표현하지 못한 것입니다. '1961-08-1100:00:00'는 제대로 표현한 것입니다. 영문을 모르겠습니다. 귀신이 곡할 노릇이었습니다.

구글링을 수행합니다. 키워드는 1961-08-10입니다. 그 결과 1961년 8월10일 0시를 기해서 30분을 앞당겼다는 것을 알 수 있었습니다.

표준자오선

“’1961년 8월 10일’이다. 박정희 정권은 5·16군사정변 성공 후 87일이 지난 이 날 표준시간을 30분 앞당겼다. 8월 10일 오전 0시를 오전 0시 30분으로 바꾸는 방식이었다.”2

표준시간 변경 소식을 전한 1961년 8월 5일자 동아일보

Figure 1: 표준시간 변경 소식을 전한 1961년 8월 5일자 동아일보

기사를 좀 더 읽어 내려갔습니다.

“현재의 표준자오선인 동경 127도 30분을 동경 135도로 변경하게 된 이유는 세계 각국에서 실시하는 표준시 제도가 영국의 그리니치 천문대를 통과하는 본초자오선을 표준으로 하는 국제 표준 시간을 기준으로 하여 정수(整數)의 시차로써 정하는 것을 관계로 하고 있는데 우리나라에서는 반정수(半整數)를 채택하고 있었기 때문에 항공항해 기상관측 등 시간 환산에 있어 일어나는 혼란을 시정키 위한 것이라고 설명했다.”3

표준자오선 변경이력, 출처: 동아일보

Figure 2: 표준자오선 변경이력, 출처: 동아일보

이 인포그래픽을 보면, 우리나라의 표준자오선, 즉 표준시간을 변경한 적이 여러번 있었습니다. 서울 기준의 표준자오선을 일제강점기에 도쿄를 기준으로 바꾸고, 해방 후 다시 서울 기준으로 바뀌고, 박정희 정권초에 다시 일본 기준으로 바꾼 것입니다.

이 글에서는 정치적인 이야기는 내려 놓겠습니다.

표준시

위키백과에서 경도의 정의를 다음과 같이 찾아 보았습니다.

“경도(longitude)는 지구상에서 본초 자오선을 기준으로 동쪽 또는 서쪽으로 얼마나 떨어져 있는지 나타내는 위치이다. 경도의 단위는 도(°)이며, 180°E(동경 180도)부터 180° W(서경 180도)까지의 범위 안에 있다. 자연스럽게 적도를 기준으로 잡는 위도와는 달리, 경도의 경우 자연적인 기준이 없기 때문에 임의적으로 하나의 기준이 필요했다. 이 기준은 한동안 지역에 따라 달랐으나 1884년에 국제 회의에서 그리니치 천문대를 지나는 본초 자오선을 표준으로 삼기로 결정했다.”4

타원체인 지구의 경도가 360되고, 하루가 24시간이니, 경도 15도마다 1시간의 시차가 발생합니다.

# 한시간 차이의 경도
360 / 24
[1] 15
# 위도 1단위의 시간차
24 / 360 * 60
[1] 4

정리하면, 다음과 같습니다.

서울은 동경 127도 30분이고, 도쿄는 135입니다. 실제로 두 도시의 거리는 30분의 시차가 발생합니다. 서울기준의 표준시를 도쿄 기준의 표준시로 바꾸었으니 30분이 당겨진 것입니다.

seoul <- 127.5 * (24 / 360)
seoul
[1] 8.5
tokyo <- 135 * (24 / 360)
tokyo
[1] 9
(tokyo - seoul) * 60
[1] 30

표준시(標準時)는 한 국가 또는 넓은 지역이 공통으로 사용되는 지방시(地方時)입니다.5 우리가 사용하는 한국 표준시는 도쿄가 위치한 동경 135도를 기준으로 하여 UTC(UTC±00:00)보다 9시간 빠른 표준시(UTC+09:00)입니다. UTC(Universal Time Coordinated)는 협정 세계시로 그리니치 평균시(GMT)라고도 부릅니다.

시간대

해외 여행이 대중화되면서 시차와 시간대는 이미 익숙합니다. 굳이 다음의 위키백과를 뒤지지 않아도 다들 알겁니다.

“시간대(time zone)는 영국의 그리니치 천문대(본초 자오선, 경도 0도)를 기준으로 지역에 따른 시간의 차이, 다시 말해 지구의 자전에 따른 지역 사이에 생기는 낮과 밤의 차이를 인위적으로 조정하기 위해 고안된 시간의 구분선을 일컫는다. 그리니치 천문대를 기준으로 세계의 0시가 결정된다.”6

시간대(time zone), 출처: 금성교과서

Figure 3: 시간대(time zone), 출처: 금성교과서

우리라나는 일본과 더불어 GMT+9를 쓴다는 것도 알고 있습니다. 영국의 그리니치 천문대를 기준으로 9시간이 빠른 시간대입니다. 새해가 밝을 때 많은 나라들보다 일찍 새해를 맞이하는 나라들 중에 하나입니다. 적어도 영국보다는 8시간 먼저 새해를 맞는 것이지요.

R에서의 시간 표현

POSIXct

R에서 날짜-시간은 POSIXct 클래스(POSIXlt도 있으나 여기서는 다루기 않습니다.)로 표현합니다. 이 클래스는 1970년 1월 1일 기준으로 초단위의 시간으로 정의합니다. 그러므로 부호가 있는 정수입니다.

# 지금 이 시간
z <- Sys.time()    
z
[1] "2022-06-26 19:29:09 -09"
class(z)
[1] "POSIXct" "POSIXt" 
unclass(z) 
[1] 1656304149

시간에 표현된 KST는 한국 표준시(Korea Standard Time)를 의미합니다. 즉 한국 표준시로 2022-06-26 19:29:09라는 의미입니다.

POSIXct 클래스는 시간대를 지정해 줘야 합니다. 그리고 R에서는 기본 시간대로 “Asia/Seoul”이 설정되어 있습니다. 이 설정으로 시간에 있어서, 한국 표준시를 생성하게 됩니다.

z <- 1472562988

as.POSIXct(z, origin = "1970-01-01")       
[1] "2016-08-30 04:16:28 -09"
as.POSIXct(z, origin = "1960-01-01")                # local (KST)
[1] "2006-08-30 04:16:28 -09"
as.POSIXct(z, origin = "1960-01-01", tz = "GMT")    # in UTC
[1] "2006-08-30 13:16:28 GMT"
# 기본 설정된 시간대
Sys.timezone()
[1] "Etc/GMT+9"

한국 표준시의 이슈

앞에서 살펴본 것처럼 한국 표준시를 변경하면서 30분의 시간이 없어졌습니다.

R에서는 어떤 문제가 발생할까요?

as.POSIXct("1961-08-09 23:59:59")
[1] "1961-08-09 23:59:59 -09"
> as.POSIXct("1961-08-10 00:00:00")
Error in as.POSIXlt.character(x, tz, ...) : 
  character string is not in a standard unambiguous format

> as.POSIXct("1961-08-10 00:29:59")
Error in as.POSIXlt.character(x, tz, ...) : 
  character string is not in a standard unambiguous format
as.POSIXct("1961-08-10 00:30:00")
[1] "1961-08-10 00:30:00 -09"
as.POSIXct("1961-08-11 00:00:00")
[1] "1961-08-11 -09"

문제의 30분인 1961-08-09 [00:00:00, 00:29:59] 구간에서 날짜-시간 인식을 하지 못하고 에러가 발생합니다. 없는 시간을 만들려고 했기 때문입니다. 놀랍지 않은가요? 역사적인 사건이 R에도 적용되어 있습니다.

회사의 직원에게 접한 에러는 생일을 저장하는 오라클 테이블의 컬럼의 데이터 타입이 문자형에서 날짜-시간을 저장하는 타입으로 변경되어서 발생한 이슈입니다. 즉, 그동안 “1961-08-10”이라는 표현의 생일이 “1961-08-10 00:00:00 KST”로 변경되면서 발생한 에러입니다.

이슈의 해결

R에서 지원하는 시간대를 조회합니다. 그런데, 이것은 운영체제별로 조금씩 차이가 있습니다. 여기서는 MacOS 기준입니다.

tz_list <- OlsonNames()
length(tz_list)
[1] 595
head(tz_list)
[1] "Africa/Abidjan"     "Africa/Accra"       "Africa/Addis_Ababa"
[4] "Africa/Algiers"     "Africa/Asmara"      "Africa/Asmera"     
grep("GMT", tz_list, value = TRUE)
 [1] "Etc/GMT"    "Etc/GMT-0"  "Etc/GMT-1"  "Etc/GMT-10" "Etc/GMT-11"
 [6] "Etc/GMT-12" "Etc/GMT-13" "Etc/GMT-14" "Etc/GMT-2"  "Etc/GMT-3" 
[11] "Etc/GMT-4"  "Etc/GMT-5"  "Etc/GMT-6"  "Etc/GMT-7"  "Etc/GMT-8" 
[16] "Etc/GMT-9"  "Etc/GMT+0"  "Etc/GMT+1"  "Etc/GMT+10" "Etc/GMT+11"
[21] "Etc/GMT+12" "Etc/GMT+2"  "Etc/GMT+3"  "Etc/GMT+4"  "Etc/GMT+5" 
[26] "Etc/GMT+6"  "Etc/GMT+7"  "Etc/GMT+8"  "Etc/GMT+9"  "Etc/GMT0"  
[31] "GMT"        "GMT-0"      "GMT+0"      "GMT0"      

한국 표준시가 "GMT"보다 9시간 빠른 "Etc/GMT+9"이기 때문에 기본으로 설정된 시간대인 "Asia/Seoul""Etc/GMT+9"로 변경했습니다. 그리고 정상적으로 잃어버린 30분 안에서의 연산이 가능해졌습니다.

Sys.getenv("TZ")
[1] "Etc/GMT+9"
[1] "2022-06-26 19:29:09 -09"
Sys.setenv(TZ = "Etc/GMT+9")
as.POSIXct("1961-08-10 00:29:59")
[1] "1961-08-10 00:29:59 -09"

또 다른 이슈와 해결

이번에는 직원이 또다른 문제를 제기했습니다. Sys.time() 함수로 시간 연산을 수행하는데, 현재 시간을 조회하는 Sys.time() 함수는 “Etc/GMT+9”가 적용된 환경에서 9시간 전의 시간을 반환한다는 것입니다. 한국 시간을 기대했는데, 영국 시간이 조회되어 버그가 발생한 것입니다.

Sys.setenv(TZ = "Asia/Seoul")
Sys.time()
[1] "2022-06-27 13:29:09 KST"
Sys.setenv(TZ = "Etc/GMT+9")
Sys.time()
[1] "2022-06-26 19:29:09 -09"

시간대를 “Etc/GMT+9”로 변경해서 해결된 것이 아닙니다. 그래서 시간대를 일본 기준인 “Asia/Tokyo”로 변경해야 했습니다. 다음처럼 9시간 차의 문제가 발생하지 않습니다.

Sys.setenv(TZ = "Asia/Seoul")
Sys.time()
[1] "2022-06-27 13:29:09 KST"
Sys.setenv(TZ = "Asia/Tokyo")
Sys.time()
[1] "2022-06-27 13:29:09 JST"
as.POSIXct("1961-08-10 00:29:59")
[1] "1961-08-10 00:29:59 JST"

어쩔 수 없습니다. 기분은 썩 좋지 않지만, 부작용을 회피하면서 잃어버린 30분을 찾기 위해서는 시간대를 “Asia/Tokyo”를 사용해야 합니다.

한국 표준시를 토교에 맞추었기 때문에 시간이 변경될 일은 없습니다. 그리고 일본의 역사에는 잃어버린 30분이 없습니다. 이제 원하는 솔루션을 찾은 것 같습니다.

단상

역사적 사건의 흔적은 우리가 의식하지 못한 채로 여기 저기에 남아 있습니다. R에서 발생한 에러를 풀기 위한 실마리가 역사의 한 켠에 숨어 있었습니다.

오늘 작성한 이 포스트도 훗날 R 역사의 한 페이지로 남아 있기를 기원합니다.


  1. 경향신문, ‘1961년 표준자오선 동경 135도로 변경’, https://m.khan.co.kr/people/people-general/article/201008092147425#c2b↩︎

  2. 동아일보, ‘[백 투 더 동아/8월 10일]1961년 오늘, 대한민국 30분 빨라지다’, https://www.donga.com/news/Society/article/all/20170810/85768074/1↩︎

  3. 동아일보, ‘[백 투 더 동아/8월 10일]1961년 오늘, 대한민국 30분 빨라지다’, https://www.donga.com/news/Society/article/all/20170810/85768074/1↩︎

  4. 출처: 위키백과, https://ko.wikipedia.org/wiki/경도↩︎

  5. 출처: 위키백과, https://ko.wikipedia.org/wiki/표준시↩︎

  6. 출처: 위키백과, https://ko.wikipedia.org/wiki/시간대↩︎

Citation

For attribution, please cite this work as

유충현 (2022, June 25). Dataholic: 잃어버린 30분. Retrieved from https://choonghyunryu.github.io/2022-06-25-lost-time.Rmd

BibTeX citation

@misc{유충현2022잃어버린,
  author = {유충현, },
  title = {Dataholic: 잃어버린 30분},
  url = {https://choonghyunryu.github.io/2022-06-25-lost-time.Rmd},
  year = {2022}
}