2038년 문제 (Y2K38) While이 에 작성. 2,693번 읽힘.
Y2K?
예전 1900년대 시절 날짜 표현시에 ’98, ’99 등으로 표현을 해온 경우가 많았다. Y2K문제는 여기서 1999년 12월 31일이 지나 2000년대가 될 경우, ’00년으로 표현을 할 수 있게 되는데 만약 전산에 1998등의 자료 입력이 아닌 98, 99, 00 등으로 자료가 입력이 되어 있으면 컴퓨터가 ’00년을 2000년으로 인식할지 1900년으로 인식할지 알 수 없는 문제가 바로 Y2K이다. 당시에 너무 떠들어대서 중대한 문제인걸로 인식 됐었는데, 사실 이는 별로 큰 문제가 아니다. 전산에 자료를 저장할때 중요한 자료라면 년도 앞의 19를 떼어먹고 뒷 2자리만 저장하는 경우는 상당히 드물기 때문에 그렇게 심각한 문제가 아니다. 정작 문제는 Y2K38 문제이다.
Y2K38?
이 문제도 그와 개념은 비슷하다. 유닉스에서 int형(숫자)으로 표시하는 방법의 대표적인 방법이 UNIXTAMP인데, 이것은 보통 32비트 int형으로 쓰고 있고 최대 값이 2147483647이다. 1당 1초씩 취급된다. 이 값이 0이면 1970년 1월 1일 0시 0분 0초(UTC)이고 이를 기준으로 2147483647초(2038년 1월 19일 화요일 03시 14분 7초 UTC)까지 표현 할 수 있는 표현방법이다. 64비트 운영체제는 int 형을 64비트도 표현 할 수 있다. 다만 지금까지 만들어진 많은 프로그램들은 32비트 기반으로 만들어졌고, 이를 64비트 기반으로 프로그램을 바꾼다 하더라도 단순히 64비트에서 돌아가게 만들 뿐이면서 내부적으로는 32비트 처리방식 그대로 따르는 식으로 변경이 되는 경우가 많이 32비트가 넘는 숫자를 표현 할 수 없는건 마찬가지이다. 또 64비트가 아닌 모바일 프로세서(휴대폰이나 MP3등의 기기)들에서는 아직도 32비트를 사용중인데 이것이 소프트웨어와 하드웨어가 모두 2038년 전까지 64비트 기반으로 대체 될지도 불분명하므로 작은 문제가 아니라고 할 수 있다.
64비트 기반으로 대체 된 후에는 이와 같은 문제가 약 3000억년 뒤에 일어날 수 있게 된다. 만약 그때까지 그레고리력이 단 한번도 개정되지 않고 쓰이고 있다면, 정확히 2922억 7702만 6596년 12월 4일 일요일 15:30:08 UTC에 문제가 발생한다. 그러나 우리의 태양은 50억년 정도밖에 살지 못하므로 지구상의 컴퓨터에서는 이 문제가 일어나지 않는다. 우주가 블랙홀로 붕괴하지 않는다는 것을 전제로 할 때 이 문제가 일어날 수 있으려면 태양이 글리제 581과 같은 적색 왜성이어야 한다. (위키백과)
아무튼 여기서 쓰는 32비트 int 형은 2147483647가 넘어가면 에러가 나거나, -2147483648이 되거나, 혹은 0이 될 경우도 있다. 문제는 여기서 발생한다. 2147483647초는 2038년 1월 19일 화요일 03시 14분 7초 (UTC)인데 -2147483648이 되면 1901년이고, 0이 되면 1970년이다. 혹은 에러가 나면 프로그램 자체가 멈출 것이다.
http://ko.wikipedia.org/wiki/2038%EB%85%84_%EB%AC%B8%EC%A0%9C 에서 오른쪽에 보이는 이미지는 Decimal 이 UNIXTAMP고, 아래 Date중 첫번째는 32비트 시스템에서 표현하는 날짜, 그 아래 Date는 실제로 표현해야 될 날짜이다. 집중해서 보고 있으면 2147483647이 넘어가면 아래 Date 는 계속 진행 되지만 위 Date는 갑자기 1901년대로 바껴버린다.
mysql 에서 Timestamp 형의 Y2K38 문제
mysql로 만들어진 제로보드4에서 내가 새로 만들고 있는 보드로 컨버팅 프로그램을 작업하다가 zb4의 회원이용정지필드(int형 timestamp)에서 2038년이 넘는 이용정지를 당한 회원이 새로운 보드로 이전을 하면서(timestamp형 필드) 0000년 00월 00일까지 정지로 바뀌는 문제가 발생했다. 이리저리 자료를 찾아보니 Timestamp형은 저 문제가 어쩔 수 없고 이를 해결하려면 datetime 필드를 쓰라고 했다.
mysql 의 datetime형은 timestamp형과 많이 닮아있다. 형식 자체는 거의 똑같은 형식이라고 볼 수 있다. 다른점은 timestamp에는 current_timestamp 라는 상수가 지원이 되고(디폴드값 가능) datetime 에는 그 상수가 지원이 안된다. 그래서 datetime 에 current_timestamp 를 디폴드 값으로 지정하는 기능을 쓰고 싶으면 innodb 로 전환하여 트리거를 이용하는 수 밖에 없다. 그외 unix_timestamp, from_unixtamp 등 날짜 관련 함수들은 모두 동일하게 쓰일 수 있다.
다만 지금 설명할 내용중 가장 중요한 내용인 datetime 의 장점은 0000년 00월 00일 00시 00분 00초부터 9999년 12월 31일 23시 59분 59초까지 표현이 가능한 형이라는 것이다. 아쉽게도 1만년이 넘어가는 날짜는 표현이 불가능하다. 사실 이건 지금 시점에서는 필요가 없으니까 그냥 패스해도 될 문제.
무튼 기존에 쓰던 timestamp 와 비슷하게 쓸 수 있어서 컨버팅하는데는 큰 불편이 없었다. 그러나 php 에서의 날짜비교를 할 땐 timestamp 를 쓰는게 아주 편하므로 이것마저 DateTime class 로 바꿀 노릇은 못할거 같아서 0000년부터 9999년까지 timestamp 를 표현 할 수 있는 함수를 만들어 대신 썼다. 일반적으로 많이 쓰는 strtotime 같은 함수로는 역시 저 버그때문에 불가능하고, DateTime 클래스를 이용하는걸로 alias 같은 함수를 하나 만들어서 쓰고 있다.
// timestamp -> DateTime (String) function from_unixtime($timestamp) { return date_format(new DateTime("@".$timestamp),"Y-m-d H:i:s"); } // DateTime (String) -> timestamp function unix_timestamp($str) { $res = date_timestamp_get(new DateTime($str)); if ($res < 0) return 0; return $res; }
이런 함수를 만들고 기존에 select unix_timestamp(field) as field ~ 로 하던 쿼리를 select field ~ 로 바꾸고, PHP상에서 $data['field'] = unix_timestamp($data['field']); 를 추가해줬다. 좀 코드가 구리게 보이지만 mysql 에서 64비트를 제대로 지원하기 전까지는 별수 없으니 이정도면 처리 끝.