如何找到当前系统时区?

ale*_*gle 23 c c++ linux timezone

在Linux上,我需要找到当前配置的时区作为Olson位置.我希望我的(C或C++)代码可以移植到尽可能多的Linux系统.

例如.我住在伦敦,所以我现在的奥尔森位置是"欧洲/伦敦".我对时区ID 感兴趣,比如"BST","EST"等等.

Debian和Ubuntu有一个/etc/timezone包含这些信息的文件,但我认为我不能依赖那个文件,我可以吗?Gnome有一个函数oobs_time_config_get_timezone()也返回正确的字符串,但我希望我的代码可以在没有Gnome的系统上运行.

那么,在Linux上获得当前配置的时区作为Olson位置的最佳通用方法是什么?

psm*_*ars 24

这是很难得到一个可靠的答案.依靠这样的事情/etc/timezone可能是最好的选择.

(如其他答案中所建议的,变量tznametm_zone成员struct tm通常包含缩写,例如GMT/ BSTetc,而不是问题中请求的Olson时间字符串).

  • 在基于Debian的系统(包括Ubuntu)上,/etc/timezone是一个包含正确答案的文件.
  • 在一些基于Redhat的系统(包括至少某些版本的CentOS,RHEL,Fedora)上,您可以使用readlink()on获取所需的信息/etc/localtime,这是(例如)的符号链接/usr/share/zoneinfo/Europe/London.
  • OpenBSD似乎使用与RedHat相同的方案.

但是,上述方法存在一些问题.该/usr/share/zoneinfo目录还包含诸如GMT和之类的文件GB,因此用户可以将符号链接配置为指向那里.

此外,没有什么可以阻止用户在那里复制正确的时区文件而不是创建符号链接.

绕过它的一种可能性(似乎适用于Debian,RedHat和OpenBSD)是将/ etc/localtime文件的内容与/ usr/share/zoneinfo下的文件进行比较,看看哪些匹配:

eta:~% md5sum /etc/localtime
410c65079e6d14f4eedf50c19bd073f8  /etc/localtime
eta:~% find /usr/share/zoneinfo -type f | xargs md5sum | grep 410c65079e6d14f4eedf50c19bd073f8
410c65079e6d14f4eedf50c19bd073f8  /usr/share/zoneinfo/Europe/London
410c65079e6d14f4eedf50c19bd073f8  /usr/share/zoneinfo/Europe/Belfast
410c65079e6d14f4eedf50c19bd073f8  /usr/share/zoneinfo/Europe/Guernsey
410c65079e6d14f4eedf50c19bd073f8  /usr/share/zoneinfo/Europe/Jersey
410c65079e6d14f4eedf50c19bd073f8  /usr/share/zoneinfo/Europe/Isle_of_Man
...
...
Run Code Online (Sandbox Code Playgroud)

当然缺点是这会告诉你所有与当前时区相同的时区.(这意味着完全意义上的相同 - 不仅仅是"当前同时",而且"在系统知道的同一天"也总是在同一天改变它们的时钟".)

您最好的选择可能是结合上述方法:/etc/timezone如果存在则使用; 否则尝试解析/etc/localtime为符号链接; 如果失败,搜索匹配的时区定义文件; 如果失败了 - 放弃回家;-)

(而且我不知道上述任何一个是否适用于AIX ......)


Eva*_*ran 6

这没有标准的 c或c ++功能.但是,GNU libc有一个扩展.它struct tm有两个额外的成员:

long tm_gmtoff;           /* Seconds east of UTC */
const char *tm_zone;      /* Timezone abbreviation */
Run Code Online (Sandbox Code Playgroud)

这意味着如果您使用填充a struct tm(例如localtimeor gmtime)的函数之一,则可以使用这些额外字段.这当然只有在您使用GNU libc(以及它的最新版本)时才会这样.

此外,许多系统都有一个int gettimeofday(struct timeval *tv, struct timezone *tz);功能(POSIX),它将填写一个struct timezone.这包含以下字段:

struct timezone {
    int tz_minuteswest;     /* minutes west of Greenwich */
    int tz_dsttime;         /* type of DST correction */
};
Run Code Online (Sandbox Code Playgroud)

不完全是你要求的,但关闭......

  • 回复:gettimeofday的,最近的文档包含下面的注释:"使用该时区的结构已经过时;在TZ参数通常应指定为NULL的tz_dsttime场从未在Linux下使用,它一直没有,也不会得到支持.通过libc或glibc.内核源代码中每个出现的这个字段(声明除外)都是一个错误." (3认同)

小智 5

当天很晚,但我正在寻找类似的东西,发现 ICU 库提供了获取奥尔森时区 ID 的规定:http : //userguide.icu-project.org/datetime/timezone

它现在安装在大多数 linux 发行版上(安装 libicu-dev 包或同等版本)。代码:

#include <unicode/timezone.h>
#include <iostream>

using namespace U_ICU_NAMESPACE;

int main() {
  TimeZone* tz = TimeZone::createDefault();
  UnicodeString us;
  tz->getID(us);
  std::string s;
  us.toUTF8String(s);
  std::cout << "Current timezone ID: " << s << '\n';
  delete tz;

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

并获取缩写/POSIX 时区名称(也应该适用于 Windows):

#include <time.h>

int main() {
  time_t ts = 0;
  struct tm t;
  char buf[16];
  ::localtime_r(&ts, &t);
  ::strftime(buf, sizeof(buf), "%z", &t);
  std::cout << "Current timezone (POSIX): " << buf << std::endl;
  ::strftime(buf, sizeof(buf), "%Z", &t);
  std::cout << "Current timezone: " << buf << std::endl;
Run Code Online (Sandbox Code Playgroud)


Dua*_*lly 5

我看到两个主要的 linux 案例:

  1. 乌班图。应该有一个 /etc/timezone 文件。此文件应仅包含时区,不包含其他任何内容。
  2. 红帽。应该有一个 /etc/sysconfig/clock 包含如下内容: ZONE="America/Chicago"

此外,Solaris 应该有一个 /etc/TIMEZONE 文件,其中包含如下一行:TZ=US/Mountain

因此,基于上述内容,我相信这里有一些直接的 C 可以回答 OP 的问题。我已经在 Ubuntu、CentOS(红帽)和 Solaris(额外奖励)上对其进行了测试。

#include <string.h>
#include <strings.h>
#include <stdio.h>

char *findDefaultTZ(char *tz, size_t tzSize);
char *getValue(char *filename, char *tag, char *value, size_t valueSize);

int main(int argc, char **argv)
{
  char tz[128];

  if (findDefaultTZ(tz, sizeof(tz)))
    printf("Default timezone is %s.\n", tz);
  else
    printf("Unable to determine default timezone.\n");
  return 0;
}


char *findDefaultTZ(char *tz, size_t tzSize)
{
  char *ret = NULL;
  /* If there is an /etc/timezone file, then we expect it to contain
   * nothing except the timezone. */
  FILE *fd = fopen("/etc/timezone", "r"); /* Ubuntu. */
  if (fd)
  {
    char buffer[128];
    /* There should only be one line, in this case. */
    while (fgets(buffer, sizeof(buffer), fd))
    {
      char *lasts = buffer;
      /* We don't want a line feed on the end. */
      char *tag = strtok_r(lasts, " \t\n", &lasts);
      /* Idiot check. */
      if (tag && strlen(tag) > 0 && tag[0] != '#')
      {
        strncpy(tz, tag, tzSize);
        ret = tz;
      }
    }
    fclose(fd);
  }
  else if (getValue("/etc/sysconfig/clock", "ZONE", tz, tzSize)) /* Redhat.    */
    ret = tz;
  else if (getValue("/etc/TIMEZONE", "TZ", tz, tzSize))     /* Solaris. */
    ret = tz;
  return ret;
}

/* Look for tag=someValue within filename.  When found, return someValue
 * in the provided value parameter up to valueSize in length.  If someValue
 * is enclosed in quotes, remove them. */
char *getValue(char *filename, char *tag, char *value, size_t valueSize)
{
  char buffer[128], *lasts;
  int foundTag = 0;

  FILE *fd = fopen(filename, "r");
  if (fd)
  {
    /* Process the file, line by line. */
    while (fgets(buffer, sizeof(buffer), fd))
    {
      lasts = buffer;
      /* Look for lines with tag=value. */
      char *token = strtok_r(lasts, "=", &lasts);
      /* Is this the tag we are looking for? */
      if (token && !strcmp(token, tag))
      {
        /* Parse out the value. */
        char *zone = strtok_r(lasts, " \t\n", &lasts);
        /* If everything looks good, copy it to our return var. */
        if (zone && strlen(zone) > 0)
        {
          int i = 0;
          int j = 0;
          char quote = 0x00;
          /* Rather than just simple copy, remove quotes while we copy. */
          for (i = 0; i < strlen(zone) && i < valueSize - 1; i++)
          {
            /* Start quote. */
            if (quote == 0x00 && zone[i] == '"')
              quote = zone[i];
            /* End quote. */
            else if (quote != 0x00 && quote == zone[i])
              quote = 0x00;
            /* Copy bytes. */
            else
            {
              value[j] = zone[i];
              j++;
            }
          }
          value[j] = 0x00;
          foundTag = 1;
        }
        break;
      }
    }
    fclose(fd);
  }
  if (foundTag)
    return value;
  return NULL;
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案。几天前,我需要这样的东西,首先是在 Linux 上——所以我把它做成了一个包 [gettz for R,现在在 CRAN 上](https://cran.r-project.org/web/包/gettz/index.html)。来源是 [在 GitHub 上](https://github.com/eddelbuettel/gettz)。 (2认同)

How*_*ant 5

我一直在开发一个免费的开源C++ 11/14库,它只需一行代码即可解决这个问题:

std::cout << date::current_zone()->name() << '\n';
Run Code Online (Sandbox Code Playgroud)

它适用于所有最新版本的Linux,macOS和Windows.对我来说,这个程序输出:

America/New_York
Run Code Online (Sandbox Code Playgroud)

如果您下载此库,但它不起作用,欢迎使用错误报告.