C代码获取相对于UTC的本地时间偏移(以分钟为单位)?

我没有找到一种简单的方法来获得当地时间和UTC时间之间的分钟数。

起初我打算使用tzset()但它不提供夏令时。 根据手册页,如果日光节省有效,它只是一个不等于零的整数。 虽然通常是一小时,但在某些国家可能需要半小时。

我宁愿避免计算gmtime()返回的当前UTC与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()的输出视为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做了不是为了获得代表。 模仿是最真诚的奉承forms

这个答案类似于@trenki,除了它减去附近的struct tm值而不是假设DST shift为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。 你为什么不想使用这些function?

恕我直言,唯一简单易用的方法是使用localtimegmtime并在分钟内手动计算delta,因为所有已知系统都存在这两个函数。 例如:

 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]