C, чтобы получить локальное смещение по времени в минутах относительно UTC?

Я не нашел тривиального способа получить временное смещение в минутах между местным временем и временем UTC.

Сначала я намеревался использовать tzset() но не обеспечивает летнее время. Согласно странице man, это просто целое число, отличное от нуля, если действует дневное свечение. Хотя обычно это час, в некоторых странах это может быть полчаса.

Я бы предпочел избегать вычисления разницы во времени между текущим UTC, возвращаемым gmtime() и localtime() .

Более общее решение даст мне эту информацию для определенного местоположения и положительного значения time_t или, по крайней мере, локально.

Редактирование 1 : прецедент заключается в получении правильного локального смещения по времени для https://github.com/chmike/timez . BTW. Если вы считали, что функции libc для манипулирования временем были Ok, прочитайте это https://rachelbythebay.com/w/2013/03/17/time/ .

Edit 2 : лучшее и самое простое решение, которое я до сих пор использовал для вычисления смещения по времени для UTC в минутах

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

Проблема состоит в том, чтобы определить время сбережения реального времени.

Редактировать 3 : Вдохновленный ответом @trenki , я придумал следующее решение. Это взлом в том, что он трюки mktime() рассматривает выход gmtime() как gmtime() время. Результат является неточным, когда изменение DST находится в промежутке времени между временем UTC и местным временем.

 #include  #include  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; } 

    … получить локальное смещение по времени … относительно UTC?

    @Serge Ballesta ответ хороший . Поэтому, хотя я бы проверил его и очистил несколько деталей. Я бы разместил как комментарий, но, очевидно, слишком большой для этого. Я использовал это только для своего часового пояса, но, хотя другие могут захотеть попробовать их машину и зону.

    Я сделал для wiki сообщества, чтобы не собирать репутацию. Имитация – это самая искренняя форма лести

    Этот ответ сродни @trenki, за исключением того, что он вычитает близкие значения struct tm вместо того, чтобы предположить, что сдвиг DST равен 1 часу, а time_t – в секундах.

     #include  #include  #include  #include  // return difference in **seconds** of the tm_mday, tm_hour, tm_min, tm_sec members. 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, then end-of-month wrap 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"); } 

    Выход

      v----v Difference in seconds 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 

    Этот C-код вычисляет локальное смещение по времени в минутах относительно UTC. Предполагается, что DST всегда смещается на один час.

     #include  #include  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. Почему вы не хотите использовать эти функции?

    IMHO единственный надежный и портативный способ – использовать localtime и gmtime и вручную вычислять дельта за минуту, потому что эти 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; } 

    Опасайтесь, я не тестировал все возможные угловые случаи, но это может быть отправной точкой для вашего требования.

    Поддерживает ли ваша система 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]