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

u5rb5r59  于 2024-01-06  发布在  其他
关注(0)|答案(6)|浏览(122)

我没有找到一个简单的方法来获得本地时间和UTC时间之间的时间偏移。
起初我打算使用tzset(),但它不提供日光节约时间。根据手册页,如果日光节约生效,它只是一个与零相差的整数。虽然它通常是一个小时,但在某些国家可能是半个小时。
我宁愿避免计算gmtime()localtime()返回的当前UTC之间的时差。
更一般的解决方案是给予指定位置和正的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()的输出视为本地时间。当夏令时变化在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;
}

ddhy6vgd

ddhy6vgd1#

您系统的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]

7qhs6swi

7qhs6swi2#

此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,为什么你不想使用这些函数呢?

pzfprimi

pzfprimi3#

.获取相对于UTC的本地时间偏移量?
@Serge Ballesta回答是 * 好 *.但我决定测试它并清理一些细节.注意:我只测试了我的时区,所以其他人可能会想在他们的系统和他们的时区检查这种方法.
这个答案类似于@trenki发布的答案,只是这个答案减去了struct tm值,而不是假设DST偏移为1小时,time_t以秒为单位。

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

// 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


我会把这个作为一个评论,但显然下面是太大了。我不想为这个答案赢得声誉,但Imitation is the sincerest form of flattery

wxclj1h5

wxclj1h54#

恕我直言,唯一的傻瓜和便携式的方法是使用localtimegmtime,并手动计算增量分钟,因为这两个函数存在于所有已知的系统。例如:

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

字符串
请注意,我没有测试所有可能的极端情况,但它可能是您的需求的起点。

m2xkgtsf

m2xkgtsf5#

我想对这个问题提出另一个答案,一个AFAICS也处理IDL的答案。
这个解决方案依赖于timegmmktime。在Windows上,timegm可以作为_mkgmtime从CRT获得,换句话说,定义一个条件宏。

#if _WIN32
#    define timegm _mkgmtime
#endif

int local_utc_offset_minutes ( ) {
    time_t t  = time ( NULL );
    struct tm * locg = localtime ( &t );
    struct tm locl;
    memcpy ( &locl, locg, sizeof ( struct tm ) );
    return (int)( timegm ( locg ) - mktime ( &locl ) ) / 60;
}

字符串

z9smfwbn

z9smfwbn6#

这是我的方式:

time_t z = 0;
struct tm * pdt = gmtime(&z);
time_t tzlag = mktime(pdt);

字符串
使用struct tm的自动本地存储的替代方案:

struct tm dt;
memset(&dt, 0, sizeof(struct tm));
dt.tm_mday=1; dt.tm_year=70;
time_t tzlag = mktime(&dt);


tzlag,以秒为单位,将是UTC偏移量的负数;您的时区标准时间与UTC相比的滞后:
LocalST + tzlag = UTC
如果你还想考虑“夏令时”,从tzlag中减去tm_isdst,其中tm_isdst是特定本地时间struct tm的字段,在应用mktime之后(或在使用localtime获得它之后)。
为什么它工作:
集合struct tm用于“epoch”时刻,即1970年1月1日,对应于time_t为0。在该日期调用mktime()将其转换为time_t,就像它是UTC一样(因此得到0),然后从中减去UTC偏移量以产生输出time_t。因此它产生UTC_offset的负数。

相关问题