TEUS.me

 
 

정리 차원에서 간단히 기록...

 

유닉스/리눅스와 윈도우는 근본적으로 다른 점들이 꽤 있다.

그 중 하나가 시간을 계산하는 기준.

 

유닉스/리눅스의 Epoch time은 1970년 1월 1일 00:00:00GMT부터 누적된 시간(초)[각주:1]이다.

윈도우 환경에서도 이 Epoch time을 읽는 함수가 있다.

밀리초 이하의 단위를 사용하지 않고, 단순하게 초 단위에서만 계산하려면 아래와 같이 사용하면 된다.

 

#include <stdio.h>
#include <stdlib.h>

#ifdef _MSC_VER
#include <time.h>
#elif __GNUC__
#include <sys/time.h>
#include <unistd.h>
#endif

int main()
{
#ifdef _MSC_VER
    time_t ltime;
    time(&ltime);
    unsigned long long ullEpoch = (unsigned long long)ltime;
#elif __GNUC__
    struct timeval tmv;
    gettimeofday(&tmv, NULL);
    unsigned long long ullEpoch = (unsigned long long)tmv.tv_sec;
#endif
    printf("Time in seconds since UTC 1970-1-1:\t%llu\n", ullEpoch);
}

 

하지만, 이 방식은 사실 많은 문제들을 내포하고 있다.

일단, 정밀도가 떨어지며, 32비트 환경이라면 윈도우 역시 2038년 1월 이후에는 정상적으로 동작하지 않는다[각주:2].

 

gcc의 timeval 구조체는 마이크로초 단위의 값을 추가로 저장한다.

이것과 비교할 수 있는 윈도우의 FILETIME 구조체도 역시 넓은 시간 범위를 지정할 수 있도록 만들어졌다.

문제는, 이 녀석은 기준일자가 1601년 1월 1일이고, 시간 정밀도는 100나노초(0.1 마이크로초)[각주:3] 라는 것.

 

이를 활용해서 윈도우/리눅스에서 Epoch time을 밀리초 단위로 읽으려면 이 정도로 하면 된다.

 

#include <stdio.h>
#include <stdlib.h>

#ifdef _MSC_VER
#include <Windows.h>
#elif __GNUC__
#include <sys/time.h>
#include <unistd.h>
#endif

int main()
{
#ifdef _MSC_VER
    FILETIME ftm = {};
    
    //직접 포인터를 타입캐스팅하지 말 것: 정렬 문제 발생 가능
    //https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
    ULARGE_INTEGER ul = {};
    GetSystemTimeAsFileTime(&ftm);

    ul.LowPart = ftm.dwLowDateTime;
    ul.HighPart = ftm.dwHighDateTime;

    unsigned long long tm = (ul.QuadPart - 116444736000000000LL) / 10;

    unsigned long long ullEpoch = tm / 1000000UL;
    unsigned long long mSec = (tm % 1000000UL) / 1000;
#elif __GNUC__
    struct timeval tmv;
    gettimeofday(&tmv, NULL);
    unsigned long long ullEpoch = (unsigned long long)tmv.tv_sec;
    unsigned long long mSec = ((unsigned long long)tmv.tv_usec) / 1000;
#endif
    printf("Time in seconds since UTC 1970-1-1:\t%llu (%llu msec)\n", ullEpoch, mSec);
}

 

하는 김에 밀리초 단위의 숫자 하나로 만들어주는 함수를 구현하면 아래와 같은 형태가 된다.

 

#include <stdio.h>
#include <stdlib.h>

#ifdef _MSC_VER
#include <Windows.h>
#elif __GNUC__
#include <sys/time.h>
#include <unistd.h>
#endif

unsigned long long GetEpochInMSec()
{
#ifdef _MSC_VER
    static FILETIME ftm = {};
    static ULARGE_INTEGER ul = {};

    GetSystemTimeAsFileTime(&ftm);

    ul.LowPart = ftm.dwLowDateTime;
    ul.HighPart = ftm.dwHighDateTime;

    const unsigned long long tm = (ul.QuadPart - 116444736000000000LL) / 10;

    return tm / 1000;
#elif __GNUC__
    static struct timeval tmv;
    gettimeofday(&tmv, NULL);

    const unsigned long long mSec = ((unsigned long long)tmv.tv_usec) / 1000;

    return (unsigned long long)tmv.tv_sec * 1000 + mSec;
#endif
}

int main()
{
    unsigned long long tm = GetEpochInMSec();

    printf("Time in seconds since UTC 1970-1-1:\t%llu (%llu msec)\n", tm / 1000, tm % 1000);
}

 

이 코드는 당연히 리눅스 환경에서도...

 

윈도우 환경에서도...

 

동일한 결과를 보여준다.

 

 

  1. 이 값이 바로 32비트로 관리하면 2038년 1월까지밖에 못 쓴다고 하는 그 값임 [본문으로]
  2. 64비트 환경에서는 2038년 문제가 없음 [본문으로]
  3. 실제 측정 정밀도는 이렇게 높지 않음, 단지 저장의 정밀도일 뿐임 [본문으로]

공유하기

facebook twitter kakaoTalk kakaostory naver band

댓글

비밀글모드