IDL/Programming

날짜 문자열 갖고 놀기 [1]

이상우_IDL 2014. 10. 16. 16:35
728x90

유저 여러분 안녕하십니까. 오랜만에 글을 올리네요. 오늘은 날짜 정보가 담긴 문자열을 갖고 좀 놀아보기로 하겠습니다. 물론 "논다"는 의미가 좀 이상할 수도 있겠습니다. 그냥 이런 짓을 이렇게도 할 수 있구나라는 의미 정도로 읽어주시면 좋겠습니다.


저는 업무 특성상 날짜(Date)라는 형태의 데이터를 굉장히 많이 다루고 있습니다. 오늘 소개되는 내용도 제가 일때문에 실제로 사용했던 기법을 바탕으로 하고 있습니다. 그럼 내용을 시작해보기로 하죠. 다음과 같이 년, 월, 일, 시간의 값들로 이루어진 문자열이 있다고 가정해봅시다.


ts = '201410152100'


보시다시피 2014년 10월 15일 21시라는 정보를 갖는 문자열입니다. 만약 이 시점보다 1시간 뒤로 경과된 시점이라면 당연히 2014년 10월 15일 22시가 될 것입니다. 그리고 이 시간에 해당되는 문자열은 '201410152200'이 되겠지요. 만약 1시간이 아닌 4시간이 경과된 시점이라면 어떻게 될까요? 그러면 2014년 10월 16일 01시가 될 것입니다. 즉 날짜가 하루 넘어가게 됩니다. 그렇다면 이러한 계산이 반영된 결과를 프로그램적으로는 어떻게 얻어야 할까요? 그냥 문자열인 상태에서는 연산이 이루어지지 않습니다. 더구나 날짜의 경우에는 각 단위마다 유효한 범위가 있기 때문에 더 골치가 아파집니다. 23시에서 1시간이 더해지면 24시가 아니라 다음날 0시가 되고, 59분에서 1분이 더해지면 60분이 아니라 다음 시간의 0분이 되기 때문입니다.


그래서 이런 목적을 위해서는 날짜를 문자열인 상태에서만 다루지 말고, 숫자형의 값을 갖는 형태로 변환한 다음 필요한 연산을 수행하고 다시 이를 문자열로 역변환하는 과정을 밟아줘야 합니다. 이럴 때 사용되는 것이 바로 Julian Day입니다. 이것은 우리가 흔히 날짜를 년, 월, 일, 시, 분, 초 단위별로 인식하는 방식이 아니라 그냥 하나의 절대값처럼 인식하는 방식입니다. 그리고 IDL에서는 JULDAY라는 내장함수가 있어서, 우리가 쓰는 방식의 날짜를 Julian Day 기반의 날짜로 바꿔주는 역할을 합니다. 기본 사용법은 아래와 같습니다.


result = JULDAY(월, 일, 년, 시, 분, 초)


여기서 JULDAY 함수에 들어가는 인자들의 순서를 유의해야 합니다. 그래서 앞서 언급된 '201410152100'이라는 문자열이 의미하는 날짜를 Julian Day로 바꾸려면 문자열에서 년, 월, 일, 시, 분을 분할해야 하는데요. 이를 위해서 STRMID라는 문자 추출용 내장함수가 사용됩니다. 다음과 같은 방식으로 진행하면 됩니다. 내용이 길어 보일 수 있지만 그냥 년, 월, 일, 시, 분 쪼개서 JULDAY 함수에 넣어준 것 뿐입니다. 그리고 이렇게 얻어진 Julian Day의 값을 출력도 해봅시다.


yr = STRMID(ts, 0, 4)

mo = STRMID(ts, 4, 2)

da = STRMID(ts, 6, 2)

hr = STRMID(ts, 8, 2)

mn = STRMID(ts, 10, 2)

tj = JULDAY(mo, da, yr, hr, mn)

PRINT, tj


사실 이 값은 일반 실수가 아니라 2배 정밀도(Double Precision) 실수입니다. 그리고 이 값 자체는 우리가 신경쓸 필요는 별로 없습니다. 단 우리의 관심사는 이 날짜에서 1시간 또는4시간이 경과된 날짜를 어떻게 얻느냐입니다. 그러려면 이 Julian Day에 그만큼을 더해주면 됩니다. 만약 1시간을 더하여 새로운 Julian Day를 얻고자 한다면 다음과 같이 하면 됩니다.


tj_new = tj + 1D/24


여기서 1D/24를 한 이유는, Julian "Day"는 일 단위의 값이기 때문입니다. 따라서 1시간이란 크기는 Julian Day의 관점에서 보면 24시간이 1일이므로 1/24이 됩니다. 다만 1을 1D로 쓴 이유는 2배 정밀도 실수라는 점을 고려한 것입니다. 따라서 만약 4시간을 더하겠다고 하면 4D/24를 더하면 됩니다. 자 그럼 이제는 이렇게 1시간이든 4시간이든 더해진 Julian Day를 다시 우리가 알아볼 수 있는 년, 월, 일, 시, 분 정보가 담긴 문자열로 바꾸는 작업이 필요합니다. 이를 위해서는 새로 얻어진 Julian Day의 값을 STRING 함수를 사용하여문자열로 바꿔주면 됩니다. 물론 그냥은 안되고 약간의 테크닉이 필요한데요. 결론부터 말하면 다음과 같이 해주면 됩니다.


ts_new = STRING(tj_new, FORMAT='(C(CYI4.4, CMoI2.2, CDI2.2, CHI2.2, CMI2.2))')

PRINT, ts_new


이 내용을 보면 STRING 함수의 키워드인 FORMAT에 뭔가 이상한 내용의 형식이 들어가 있는 것이 보입니다. 이것은 날짜에 대해서만 사용되는 특별한 형식인데요. 예를 들어, CYI4.4라고 하면 년도에 해당되는 값을 정수 4자리로 표기하라는 의미이고, CMoI2.2는 월에 해당되는 값을 정수 2자리로 표기하라는 의미입니다. 그리고 그냥 CMoI2가 아니라 CMoI2.2라고 한 이유는 월의 값이 한 자리일 경우에도 십의 자리에는 0을 채워넣으라는 의미입니다. 나머지 단위들도 비슷한 맥락으로 이해하시면 됩니다. 사실 이러한 특수 형식에 대한 더 자세한 내용은 IDL 도움말에서 'format'이라고 검색해보면 나오기 때문에 더 자세한 설명은 생략합니다. 하여간 이런 방법으로 얻어진 ts_new를 출력해보면 다음과 같이 나옵니다. 정확히 그 시간만큼을 더한 날짜가 규칙에 맞게 얻어진 것을 볼 수 있습니다.


201410152200 ; 1D/24를 더할 경우

201410160100 ; 4D/24를 더할 경우


이런 요령이 사실 꽤 큰 효율의 증대를 가져올 수 있는 부분이 분명히 있습니다. 제가 전에도 이 블로그상의 다른 게시물에서 언급한 적이 있지만, 날짜나 문자열 다루는 좀 더 심도깊은 기법들을 잘 알아두면 자료 처리에 있어서 큰 도움이 됩니다. 아무래도 날짜 기반의 데이터들을 다루는 경우는 얼마든지 있지 않겠습니까? 오늘은 이 정도로 마무리해볼까 합니다. 제가 사실 게시물의 제목에 [1]이라는 문구를 붙였는데, [2]가 언제가 될지는 잘 모르겠습니다. 하지만 비슷한맥락에서 뭔가 여러분께 언급할만한 테크닉이 있다면 [2]라는 딱지를 붙이고 또 소개하기로 하겠습니다.



LIST