2015-09-06 24 views
7

我沒有找到一個簡單的方法來獲取本地時間和UTC時間之間的分鐘數。以相對於UTC的分鐘爲單位獲取本地時間偏移量的C代碼?

起初我打算使用tzset()但它不提供夏令時。根據手冊頁,如果日光節約生效,它只是一個不同的整數。雖然通常是一個小時,但在某些國家可能是半小時。

我寧願避免計算當前UTC返回gmtime()localtime()之間的時差。

更通用的解決方案會爲我提供指定位置和正值time_t值的信息,或者至少是本地的。

編輯1:使用情況是爲https://github.com/chmike/timez獲取正確的本地時間偏移量。 順便說一句,如果你認爲libc函數操作時間正確,請閱讀https://rachelbythebay.com/w/2013/03/17/time/

編輯2:最好的和最簡單的解決方案,我至今計算時間以分鐘爲單位偏移UTC是

// Bogus: assumes DST is always one hour 
tzset(); 
int offset = (int)(-timezone/60 + (daylight ? 60 : 0)); 

的問題是要確定真正的日光節約時間。

編輯3:受@trenki的回答啓發,我想出了以下解決方案。這是一個詭計,因爲它會哄騙mktime()gmtime()的輸出視爲本地時間。當DST更改處於UTC時間和本地時間之間的時間範圍內時,結果不準確。

#include <stdio.h> 
#include <time.h> 

int main() 
{ 
    time_t rawtime = time(NULL); 
    struct tm *ptm = gmtime(&rawtime); 
    // Request that mktime() looksup dst in timezone database 
    ptm->tm_isdst = -1;     
    time_t gmt = mktime(ptm); 
    double offset = difftime(rawtime, gmt)/60; 
    printf("%f\n", offset); 
    return 0; 
} 
+1

爲什麼需要這個?解釋更多關於背景;爲什麼你不能簡單地使用'gmtime'和'localtime'並且改變他們的'struct tm'?爲什麼你的用戶會關心本地時間偏移?並且有很多奇怪的情況(例如除夕,...) –

+1

此外,您的問題很可能是特定於操作系統... AFAIK,[locale(7)](http://man7.org/linux/ man-pages/man7/locale.7.html)不在標準C99中,但在POSIX –

+0

@BasileStarynkevitch語言環境在C99中,即使C89具有'strftime',唉只有'%Z'但不是'%z'。 – Jens

回答

3

@Serge Ballesta答案是。所以我儘管我會測試它並清理一些細節。我會張貼評論,但顯然太大了。我只是將它用於我的時區,但其他人可能想嘗試他們的機器和區域。

我向社區wiki作出不加入代表。 Imitation is the sincerest form of flattery

long tz_offset_second(time_t t) { 
    struct tm local = *localtime(&t); 
    struct tm utc = *gmtime(&t); 
    long diff = ((local.tm_hour - utc.tm_hour) * 60 + (local.tm_min - utc.tm_min)) 
      * 60L + (local.tm_sec - utc.tm_sec); 
    int delta_day = local.tm_mday - utc.tm_mday; 
    if ((delta_day == 1) || (delta_day < -1)) { 
    diff += 24L * 60 * 60; 
    } else if ((delta_day == -1) || (delta_day > 1)) { 
    diff -= 24L * 60 * 60; 
    } 
    return diff; 
} 

void testtz(void) { 
    long off = -1; 
    int delta = 600; 
    for (time_t t = 0; t < LONG_MAX-delta; t+=delta) { 
    long off2 = tz_offset_second(t); 

    // Print time whenever offset changes. 
    if (off != off2) { 
     struct tm utc = *gmtime(&t); 
     printf("%10jd %04d-%02d-%02dT%02d:%02d:%02dZ\n", (intmax_t) t, 
       utc.tm_year + 1900, utc.tm_mon + 1, utc.tm_mday, 
       utc.tm_hour, utc.tm_min, utc.tm_sec); 
     struct tm local = *localtime(&t); 
     off = off2; 
     printf("%10s %04d-%02d-%02d %02d:%02d:%02d %2d %6ld\n\n", "", 
       local.tm_year + 1900, local.tm_mon + 1, local.tm_mday, 
       local.tm_hour, local.tm_min, local.tm_sec, local.tm_isdst ,off); 
     fflush(stdout); 
    } 
    } 
    puts("Done"); 
} 

輸出

  0 1970-01-01T00:00:00Z 
      1969-12-31 18:00:00 0 -21600 

    5731200 1970-03-08T08:00:00Z 
      1970-03-08 03:00:00 1 -18000 

    26290800 1970-11-01T07:00:00Z 
      1970-11-01 01:00:00 0 -21600 

... 

2109222000 2036-11-02T07:00:00Z 
      2036-11-02 01:00:00 0 -21600 

2120112000 2037-03-08T08:00:00Z 
      2037-03-08 03:00:00 1 -18000 

2140671600 2037-11-01T07:00:00Z 
      2037-11-01 01:00:00 0 -21600 

Done 
2

請問你係統的strftime()函數支持%z%Z符?在FreeBSD,

%Z is replaced by the time zone name. 

%z is replaced by the time zone offset from UTC; a leading plus sign 
     stands for east of UTC, a minus sign for west of UTC, hours and 
     minutes follow with two digits each and no delimiter between them 
     (common form for RFC 822 date headers). 

,我可以用它來打印:

$ date +"%Z: %z" 
CEST: +0200 

ISO C99擁有這7.23.3.5的strftime功能

%z  is replaced by the offset from UTC in the ISO 8601 format 
     ‘‘−0430’’ (meaning 4 hours 30 minutes behind UTC, west of Greenwich), 
     or by no characters if no time zone is determinable. [tm_isdst] 
%Z  is replaced by the locale’s time zone name or abbreviation, or by no 
     characters if no time zone is determinable. [tm_isdst] 
+0

這將是一種獲取信息的方式。有點複雜,但它會奏效。但是,Visual studio不支持C99。不確定關於VC 2015,但這是一個已知的問題。我寧願避免依賴於C99。 – chmike

4

此C代碼計算相對於UTC的本地時間偏移量(以分鐘爲單位)。它假定DST總是一小時抵消。

#include <stdio.h> 
#include <time.h> 

int main() 
{ 
    time_t rawtime = time(NULL); 
    struct tm *ptm = gmtime(&rawtime); 
    time_t gmt = mktime(ptm); 
    ptm = localtime(&rawtime); 
    time_t offset = rawtime - gmt + (ptm->tm_isdst ? 3600 : 0); 

    printf("%i\n", (int)offset); 
} 

雖然它使用gmtime和localtime。你爲什麼不想使用這些功能?

+0

在豪勳爵島(澳大利亞),夏令時是30分鐘。見http://www.timeanddate.com/time/zone/australia/lord-howe-island。我寧願避免使用'gmtime()'和'localtime()',因爲1)它涉及的計算量比需要的多得多2)計算字段的時間偏移是棘手和容易出錯的。 – chmike

3

恕我直言,唯一的萬無一失的便攜方式是使用localtimegmtime並手動計算增量,因爲這2個函數存在於所有已知系統上。例如:

int deltam() { 
    time_t t = time(NULL); 
    struct tm *loc = localtime(&t); 
    /* save values because they could be erased by the call to gmtime */ 
    int loc_min = loc->tm_min; 
    int loc_hour = loc->tm_hour; 
    int loc_day = loc->tm_mday; 
    struct tm *utc = gmtime(&t); 
    int delta = loc_min - utc->tm_min; 
    int deltaj = loc_day - utc->tm_mday; 
    delta += (loc_hour - utc->tm_hour) * 60; 
    /* hack for the day because the difference actually is only 0, 1 or -1 */ 
    if ((deltaj == 1) || (deltaj < -1)) { 
     delta += 1440; 
    } 
    else if ((deltaj == -1) || (deltaj > 1)) { 
     delta -= 1440; 
    } 
    return delta; 
} 

請注意,我沒有測試所有可能的角落案例,但它可能是您的要求的起點。

+0

問題在於角落案例。這就是使這種方法困難的原因。 – chmike

+0

如果時間偏移跨越年份邊界會怎樣? – chmike

+0

@chmike:這就是我稱之爲黑客的原因。我不使用'tm_mon'和'tm_year'字段,因爲我假設如下:如果'tm_mday'字段之間的差異是0,1或-1,那麼它是真正的差異;如果它接近+30(在我的黑客中大於1),則會有月份變化,實際值爲-1;如果它接近-30(在我的代碼中<-1),則在對面有一個月的變化,實際值是+1。 –

相關問題