1

我正在尝试组合一个 RFC3339 时间戳,该时间戳将用于将某个条目写入数据库。例如,这将被格式化为 2004-10-19 10:23:54+02,其中 +02 是与 GMT 的小时偏移量。事实证明,这个偏移量很麻烦——我似乎无法在 C 中得出这个值。

这是我正在使用的代码。当我尝试构建时,它说 tm 结构没有名为 tm_gmtoff 的成员:

#include <time.h>
#include <stdio.h>
int main(void)
{
    time_t now = time(NULL);
    struct tm *tm;
    int off_sign;
    int off;
    if ((tm = localtime(&now)) == NULL) {
            return -1;
    }
    off_sign = '+';
    off = (int) tm->tm_gmtoff;
    if (tm->tm_gmtoff < 0) {
            off_sign = '-';
            off = -off;
    }
    printf("%d-%d-%dT%02d:%02d:%02d%c%02d:%02d",
    tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
    tm->tm_hour, tm->tm_min, tm->tm_sec,
    off_sign, off / 3600, off % 3600);
    return 0;

}

4

1 回答 1

1

在 C 中构建 RFC3339 时间戳

如果目标是字符串,那么简单的解决方案是使用strftime().

  time_t now;
  time(&now);
  struct tm *p = localtime(&now);
  char buf[100];
  size_t len = strftime(buf, sizeof buf - 1, "%FT%T%z", p);
  // move last 2 digits
  if (len > 1) {
    char minute[] = { buf[len-2], buf[len-1], '\0' };
    sprintf(buf + len - 2, ":%s", minute);
  }
  printf("\"%s\"\n", buf);

输出

"2018-11-21T13:21:08-06:00"

如何获得时区偏移量?
当我尝试构建时,它说 tm 结构没有名为 tm_gmtoff 的成员:

是的tm_gmtoff可选额外成员struct tm。如果可用,该或其他类似的实现相关成员将很适合使用。


可以访问strftime()and "%z",正如@jwdonahue 所建议的那样。
"%z"从 C99 开始可用。

%z 被 ISO 8601 格式 ''−0430'' 中的 UTC 偏移量(意味着格林威治以西的 UTC 时间晚 4 小时 30 分钟)替换,如果无法确定时区,则替换为无字符。C11 §7.27.3.5 3

int main(void) {
  time_t now;
  time(&now);
  struct tm *p = localtime(&now);
  char buf[6];
  strftime(buf, sizeof buf, "%z", p);
  int h, m;
  sscanf(buf, "%3d%d", &h, &m);
  if (h < 0)  m = -m;
  printf("Minute difference %d\n", h*60+m);
}

CT输出

Minute difference -360

缺少上述选择,代码可以直接从 localtime()和推导出时区偏移量gmttime():做struct tm减法。

下面利用时间戳的差异在 1 月 1 日附近不超过 1 天。

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

// Return difference in seconds.
long tz_offset(time_t t) {
  struct tm *p = localtime(&t);
  if (p == NULL) return INT_MIN;
  printf("%s", asctime(p));
  struct tm local = *p;
  p = gmtime(&t);
  if (p == NULL) return INT_MIN;
  printf("%s", asctime(p));
  struct tm gmt = *p;

  int day = local.tm_yday - gmt.tm_yday;
  if (local.tm_year > gmt.tm_year) {
    day = 1;
  } else if (local.tm_year < gmt.tm_year) {
    day = -1;
  }
  int hour = day*24 + (local.tm_hour - gmt.tm_hour);
  if (local.tm_isdst) {
    ; // no adjustment
  }
  long diff = (hour*60L + (local.tm_min - gmt.tm_min))*60 + (local.tm_sec - gmt.tm_sec);
  return diff;
}

int main(void) {
  time_t now;
  time(&now);
  printf("tz offset %g\n", tz_offset(now)/3600.0);
  
  // Check time 6-months from now, maybe different daylight setting
  struct tm *p = localtime(&now);
  p->tm_mon += 6;
  now = mktime(p);
  printf("tz offset %g\n", tz_offset(now)/3600.0);
  return 0;
}

CT的样本输出

Tue Feb 13 11:40:00 2018
Tue Feb 13 17:40:00 2018
tz offset -6
Mon Aug 13 12:40:00 2018
Mon Aug 13 17:40:00 2018
tz offset -5

要打印每个RFC3339给定的时区偏移量hour, minute

  // Alway print the sign and leading zero digits
  printf("%+02d:%02d", hours, abs(minute));

另请参阅ISO 8601 和 RFC 3339 日期格式有什么区别?

于 2018-02-13T17:47:21.220 回答