1. 程式人生 > >c++時間轉換與表示之03(與日期和時間相關的資料結構)

c++時間轉換與表示之03(與日期和時間相關的資料結構)

在標準C/C++中,我們可通過tm結構來獲得日期和時間,tm結構在time.h中的定義如下:

#ifndef _TM_DEFINED
struct tm {
        int tm_sec;     /* 秒 – 取值區間為[0,59] */
        int tm_min;     /* 分 - 取值區間為[0,59] */
        int tm_hour;    /* 時 - 取值區間為[0,23] */
        int tm_mday;    /* 一個月中的日期 - 取值區間為[1,31] */
        int tm_mon;     /* 月份(從一月開始,0代表一月) - 取值區間為[0,11] */
        int tm_year;    /* 年份,其值等於實際年份減去1900 */
        int tm_wday;    /* 星期 – 取值區間為[0,6],其中0代表星期天,1代表星期一,以此類推 */
        int tm_yday;    /* 從每年的1月1日開始的天數 – 取值區間為[0,365],其中0代表1月1日,1代表1月2日,以此類推 */
        int tm_isdst;   /* 夏令時識別符號,實行夏令時的時候,tm_isdst為正。不實行夏令時的進候,tm_isdst為0;不瞭解情況時,tm_isdst()為負。*/
        };
#define _TM_DEFINED
#endif
ANSI C標準稱使用tm結構的這種時間表示為分解時間(broken-down time)。
而日曆時間(Calendar Time)是通過time_t資料型別來表示的,用time_t表示的時間(日曆時間)是從一個時間點(例如:1970年1月1日0時0分0秒)到此時的秒數。在time.h中,我們也可以看到time_t是一個長整型數:
#ifndef _TIME_T_DEFINED
typedef long time_t;         /* 時間值 */
#define _TIME_T_DEFINED     /* 避免重複定義 time_t */
#endif
大家可能會產生疑問:既然time_t實際上是長整型,到未來的某一天,從一個時間點(一般是1970年1月1日0時0分0秒)到那時的秒數(即日曆時間)超出了長整形所能表示的數的範圍怎麼辦?對time_t資料型別的值來說,它所表示的時間不能晚於2038年1月18日19時14分07秒。為了能夠表示更久遠的時間,一些編譯器廠商引入了64位甚至更長的整形數來儲存日曆時間。比如微軟在Visual C++中採用了__time64_t資料型別來儲存日曆時間,並通過_time64()函式來獲得日曆時間(而不是通過使用32位字的time()函式),這樣就可以通過該資料型別儲存3001年1月1日0時0分0秒(不包括該時間點)之前的時間。

//在time.h標頭檔案中,我們還可以看到一些函式,它們都是以time_t為引數型別或返回值型別的函式:

double difftime(time_t time1, time_t time0);
time_t mktime(struct tm * timeptr);
time_t time(time_t * timer);
char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);
此外,time.h還提供了兩種不同的函式將日曆時間(一個用time_t表示的整數)轉換為我們平時看到的把年月日時分秒分開顯示的時間格式tm:

struct tm * gmtime(const time_t *timer);                                          
struct tm * localtime(const time_t * timer);

通過查閱MSDN,我們可以知道Microsoft C/C++ 7.0中時間點的值(time_t物件的值)是從1899年12月31日0時0分0秒到該時間點所經過的秒數,而其它各種版本的Microsoft C/C++和所有不同版本的Visual C++都是計算的從1970年1月1日0時0分0秒到該時間點所經過的秒數。

二、 Windows中的時間(SYSTEMTIME和FILETIME)

typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME, *PFILETIME;
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;
比較一下,很明顯,FILETIME與time_t類似,是64位整型,不過FILETIME是以100納秒(ns)為單位。SYSTEMTIME與tm類似,不過多了一項wMilliseconds。可以看出,SDK時間比CRT的時間提供了更高的精度。同時SDK提供了更豐富的函式來處理時間。
void GetSystemTime(
LPSYSTEMTIME lpSystemTime);
void GetLocalTime(
LPSYSTEMTIME lpSystemTime);
這兩個函式獲得SYSTEMTIME形式的當前時間,不過GetSystemTime函式獲得當前的UTC時間,GetLocalTime獲得當前的本地時間,可以想象,獲得的兩個時間存在著時差。類似於CRT中提供tm與time_t之間的轉換,SDK也提供了兩個函式來轉換SYSTEMTIME時間與FILETIME時間。
BOOL SystemTimeToFileTime(
const SYSTEMTIME* lpSystemTime,
LPFILETIME lpFileTime);
BOOL FileTimeToSystemTime(
const FILETIME* lpFileTime,
LPSYSTEMTIME lpSystemTime);

三、 SYSTEMTIME與time_t之間的轉換
 //SYSTEMTIME轉time_t
time_t systime_to_timet(const SYSTEMTIME& st)
{
    struct tm gm = {st.wSecond, st.wMinute, st.wHour, st.wDay, st.wMonth-1, st.wYear-1900, st.wDayOfWeek, 0, 0};
    return mktime(&gm);
}
//time_t轉SYSTEMTIME
SYSTEMTIME Time_tToSystemTime(time_t t)
{
    tm temptm = *localtime(&t);
    SYSTEMTIME st = {1900 + temptm.tm_year, 
                                   1 + temptm.tm_mon, 
                                   temptm.tm_wday, 
                                   temptm.tm_mday, 
                                   temptm.tm_hour, 
                                   temptm.tm_min, 
                                   temptm.tm_sec, 
                                   0};
    return st;
}
還有一種是通過struct FILETIME作為中間量來轉換time_t和systemtime
//time_t轉SYSTEMTIME
SYSTEMTIME TimetToSystemTime(time_t t)
{
    FILETIME ft;
    SYSTEMTIME pst;
    LONGLONG nLL = Int32x32To64(t, 10000000) + 116444736000000000;
    ft.dwLowDateTime = (DWORD)nLL;
    ft.dwHighDateTime = (DWORD)(nLL >> 32);
    FileTimeToSystemTime(&ft, &pst);
    return pst;
}
//SYSTEMTIME轉time_t
time_t SystemTimeToTimet(SYSTEMTIME st)
{
    FILETIME ft;
    SystemTimeToFileTime( &st, &ft );
    LONGLONG nLL;
    ULARGE_INTEGER ui;
    ui.LowPart = ft.dwLowDateTime;
    ui.HighPart = ft.dwHighDateTime;
    nLL = (ft.dwHighDateTime << 32) + ft.dwLowDateTime;
    time_t pt = (long)((LONGLONG)(ui.QuadPart - 116444736000000000) / 10000000);
    return pt;
}