2011-01-13 157 views
3

我有我寫了一個函數(如果有一個很好的標準替代,請讓我知道...)轉換爲unix時間戳不正確

time_t get_unix_time(string time_str) { 
    time_t loctime; 
    time(&loctime); 

    struct tm *given_time; 
    time_str = time_str.substr(0, time_str.find_first_of('.')); 

    replace(time_str.begin(), time_str.end(), ':', ','); 
    replace(time_str.begin(), time_str.end(), '-', ','); 
    replace(time_str.begin(), time_str.end(), '/', ','); 
    replace(time_str.begin(), time_str.end(), ' ', ','); 

    given_time = localtime(&loctime); 
    vector<string> trecord = split_string(time_str, ','); 

    given_time->tm_year = atoi(trecord.at(0).c_str()) - 1900; 
    given_time->tm_mon = atoi(trecord.at(1).c_str()) - 1; 
    given_time->tm_mday = atoi(trecord.at(2).c_str()); 
    given_time->tm_hour = atoi(trecord.at(3).c_str()); 
    given_time->tm_min = atoi(trecord.at(4).c_str()); 
    given_time->tm_sec = atoi(trecord.at(5).c_str()); 

    return mktime(given_time); 
} 

輸入(time_str)的函數是格式1970-01-01 00:00:00.0。所述split_string()函數把字符串time_str到含有載體:

{1970年,01,01,00,00,00}

其用於填寫given_time結構。

我寫了一個函數來測試它,並正確地通過它輸入(epoch開始)。但是,它給我的時間是21600,這是1970-01-01 06:00:00UTC + 6。期望的輸出是(時期的開始)。

注意:我在美國中部時區,即UTC - 6。1970年1月1日午夜CST,time @ UTC將在1970年1月1日06:00:00。

我的函數中是否有任何特定於我的時區的東西?我在這個函數中做了什麼錯誤,或者我可以做些不同的事情來使它獨立於區域,或者至少總是UTC。

+0

此外,我不能使用boost庫或任何其他庫。我只能使用標準的C++東西。 – Sagar 2011-01-13 16:22:55

+1

C++庫中的時區處理很隱晦。如果你的環境變量`TZ`的值設置爲UTC,那麼這段代碼就可以工作。 – Omnifarious 2011-01-13 16:36:18

+1

mktime將當前時間解釋爲給定時間。所以如果你給1970-01-01 00:00:00它會在你的當地時間,所以mktime將返回UTC-0時間,即1970-01-01 06:00:00 – Robert 2011-01-13 16:38:42

回答

5

如果您正在使用glibc你有timegm功能在您的處置,這是mktime版本總是解釋的時候,好像它是在GMT時區。不幸的是,該函數的文檔基本上聲明它不能使用標準庫調用來實現。所以,除非你擁有它,否則你的運氣不好。

1

也許你應該使用gmtime而不是time,以消除時區問題。

編輯: 我不明白爲什麼你用當前時間填充結構,然後覆蓋它的所有組件。爲什麼不乾脆:

time_t get_unix_time(const string& time_str) 
{ 
    vector<string> trecord = split_string(time_str, ','); 

    tm given_time; 
    given_time.tm_year = atoi(trecord.at(0).c_str()) - 1900; 
    given_time.tm_mon = atoi(trecord.at(1).c_str()) - 1; 
    given_time.tm_mday = atoi(trecord.at(2).c_str()); 
    given_time.tm_hour = atoi(trecord.at(3).c_str()); 
    given_time.tm_min = atoi(trecord.at(4).c_str()); 
    given_time.tm_sec = atoi(trecord.at(5).c_str()); 

    return mktime(&given_time); 
} 

另一個編輯:

唉,mktime考慮本地時間太長。我不確定如何解決這個問題,而不是將您的時區區域設置爲UTC。

+0

- 替換當前時間並替換我需要的並不是真正的問題。我嘗試了你的函數,它仍然給我21600而不是0. 21600 is = UTC + 6. – Sagar 2011-01-13 16:41:01

+0

@Sagar:「呃,mktime也考慮當地時間。我不太確定你怎麼能繞過這個,除了設置您的時區區域設置爲UTC。「是你做的嗎?或者你只是把代碼放在裏面,改變別的,並希望最好的? – 2011-01-13 16:45:09

0

當您調用mktime時,它將該參數解釋爲本地時間。 你也使用了像「本地時間」這樣的功能,似乎沒用,我想你可以放棄它們。

4

mktime在當地時區需要一段時間。所以,如果你通過它1970-01-01 00:00:00當地時間,它會返回1970-01-01 06:00:00 UTC,因爲它應該。

作爲替代方案,如果您使用的是glibc,則可以致電timegm。如果你不使用的glibc,你暫時用TZ環境變量調用搞亂時mktime改變本地時間,UTC,作爲對timegm手冊頁描述:

time_t my_timegm (struct tm *tm) { 
    time_t ret; 
    char *tz; 
    tz = getenv("TZ"); 
    setenv("TZ", "", 1); 
    tzset(); 
    ret = mktime(tm); 
    if (tz) 
     setenv("TZ", tz, 1); 
    else 
     unsetenv("TZ"); 
    tzset(); 
    return ret; 
} 

此外,您的調用localtime是不必要的,您可能應該設置given_time->tm_isdst,以避免可能的夏令時問題。

0

你可以編寫一個圍繞strptime進行解析的包裝。

struct tm given_time; 

strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &given_time); 

return mktime(&given_time); 

@Josh Kelley的回答徹底解釋了時區問題。

1

只要避免這些尷尬的功能,並自己做數學。 POSIX指定time_t是自「時代」(1970-01-01 00:00:00 GMT)以來沒有任何閏秒廢話(所有日期恰好爲86400 日曆秒,與不同的算術類型SI秒),所以除了少量的閏年邏輯之外,計算非常簡單。

像這樣的日曆計算是一個標準的入門編程練習,所以我相信你可以在網上找到解決方案。另外,也許ISO C和POSIX忽略這樣一個功能的原因是,與涉及時間區域的轉換不同,該時間區域可以是任意複雜的,只有主機的庫可以在不同的應用程序中可靠且一致地執行,GMT轉換是純粹的算術沒有外部參數。