web-dev-qa-db-ja.com

現在のシステムのタイムゾーンを確認するにはどうすればよいですか?

Linuxでは、現在構成されているタイムゾーンをOlsonの場所として見つける必要があります。 (CまたはC++)コードをできるだけ多くのLinuxシステムに移植できるようにしたい。

例えば。私はロンドンに住んでいるので、現在のOlsonの場所は「Europe/London」です。私はnot "BST"、 "EST"などのタイムゾーンIDに興味があります。

DebianとUbuntuには、この情報を含むファイル_/etc/timezone_がありますが、そのファイルが常に存在しているとは思えません。 Gnomeには関数oobs_time_config_get_timezone()があり、これも正しい文字列を返しますが、コードをGnomeのないシステムで動作させたいです。

では、Linuxで現在構成されているタイムゾーンをOlsonの場所として取得するための最も一般的な方法は何ですか?

23
alex tingle

信頼できる答えを得るのは難しい です。 /etc/timezoneのようなものに依存することが最善の策かもしれません。

(他の回答で提案されているように、変数tznameおよびtm_zonestruct tmメンバーには、通常、次のようなOlson時間文字列ではなく、GMT/BSTなどの省略形が含まれています。質問で要求された)。

  • Debianベースのシステム(Ubuntuを含む)では、/etc/timezoneは正しい答えを含むファイルです。
  • 一部のRedhatベースのシステム(CentOS、RHEL、Fedoraの少なくとも一部のバージョンを含む)では、/etc/localtimeのreadlink()を使用して必要な情報を取得できます。これは、/usr/share/zoneinfo/Europe/Londonへのシンボリックリンクです。
  • OpenBSDはRedHatと同じスキームを使用しているようです。

ただし、上記のアプローチにはいくつかの問題があります。 /usr/share/zoneinfoディレクトリにはGMTGBなどのファイルも含まれているため、ユーザーがシンボリックリンクを設定してそこを指す可能性があります。

また、シンボリックリンクを作成する代わりに、ユーザーが正しいタイムゾーンファイルをそこにコピーするのを止めることはありません。

これを回避する1つの可能性(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
...
...

もちろん、デメリットは、現在のタイムゾーンと同じすべてのタイムゾーンを示します。 (つまり、「現在同時に」だけでなく、「システムが認識している限り、常に同じ日にクロックを変更する」という意味で、完全に同一です。)

あなたの最善の策は、上記のメソッドを組み合わせることです:/etc/timezoneが存在する場合はそれを使用します。そうでない場合は、/etc/localtimeをシンボリックリンクとして解析してみてください。それが失敗した場合は、一致するタイムゾーン定義ファイルを検索します。それが失敗した場合-あきらめて家に帰る;-)

(そして、上記のいずれかがAIXに適用されるかどうかはわかりません...)

24
psmears

私は 無料のオープンソースC++ 11/14ライブラリ に取り組んできましたが、この質問に1行のコードで対処しています。

std::cout << date::current_zone()->name() << '\n';

Linux、macOS、Windowsの最近のすべてのフレーバーで移植可能であることを意味します。私にとって、このプログラムは出力します:

America/New_York

download このライブラリを使用しても機能しない場合は、 bug reports を使用してください。

6
Howard Hinnant

標準 cまたはc ++関数はありません。ただし、GNU libcには拡張機能があります。その_struct tm_には2つの追加メンバーがあります。

_long tm_gmtoff;           /* Seconds east of UTC */
const char *tm_zone;      /* Timezone abbreviation */
_

つまり、_struct tm_(localtimegmtimeなど)を生成する関数の1つを使用する場合、これらの追加フィールドを使用できます。もちろん、これはGNU libc(および十分に新しいバージョンのlibc)を使用している場合のみです。

また、多くのシステムには、_struct timezone_を埋めるint gettimeofday(struct timeval *tv, struct timezone *tz);関数(POSIX)があります。これには次のフィールドがあります。

_struct timezone {
    int tz_minuteswest;     /* minutes west of Greenwich */
    int tz_dsttime;         /* type of DST correction */
};
_

あなたが要求したものと正確には同じではありませんが、閉じます...

6
Evan Teran

Linuxの主なケースが2つあります。

  1. Ubuntu。/etc/timezoneファイルが必要です。このファイルには、タイムゾーンのみを含め、他には何も含めないでください。
  2. レッドハット。 ZONE = "America/Chicago"のようなものが含まれる/ etc/sysconfig/clockがあるはずです。

さらに、Solarisには、次のような行を含む/ etc/TIMEZONEファイルが必要です。TZ = US/Mountain

上記に基づいて、ここにOPの質問に答えると思われるストレートCをいくつか示します。 Ubuntu、CentOS(Red Hat)、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;
}
4
Duane McCully

かなり遅い時間ですが、似たようなものを探していたところ、ICUライブラリには、Olsonタイムゾーン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;
}

省略された/ 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;
4
sumwale

FWIW、RHEL/Fedora/CentOSには/etc/sysconfig/clock

ZONE="Europe/Brussels"
UTC=true
ARC=false
2
Serge Wautier

私はpsmearsの投稿を気に入って、このスクリプトを実装してリストの最初の出力を読み取りました。もちろん、これを行うためのよりエレガントな方法が必要ですが、あなたはそこにいます...

    /**
     * Returns the (Linux) server default timezone abbreviation
     * To be used when no user is logged in (Ex.: batch job)
     * Tested on Fedora 12
     * 
     * @param void
     * @return String (Timezone abbreviation Ex.: 'America/Sao_Paulo')
     */
    public function getServerTimezone()
    {

        $Shell = 'md5sum /etc/localtime';
        $q = Shell_exec($Shell);
        $Shell = 'find /usr/share/zoneinfo -type f | xargs md5sum | grep ' . substr($q, 0, strpos($q, '/') - 2);
        $q = Shell_exec($Shell);
        $q = substr($q, strpos($q, 'info/') + 5, strpos($q, " "));
        return substr($q, 0, strpos($q, chr(10)));

    }

私のブラジルのFedora 12では、次を返します:
ブラジル/東

そして、まさに私が必要としていることを行います。

ありがとうpsmears

Linuxのほとんどのバージョンで機能するコードを次に示します。

#include <iostream>
#include <time.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
using namespace std;

void main()
{
    char filename[256];
    struct stat fstat;
    int status;

    status = lstat("/etc/localtime", &fstat);
    if (S_ISLNK(fstat.st_mode))
    {
        cout << "/etc/localtime Is a link" << endl;
        int nSize = readlink("/etc/localtime", filename, 256);
        if (nSize > 0)
        {
            filename[nSize] = 0;
            cout << "    linked filename " << filename << endl;
            cout << "    Timezone " << filename + 20 << endl;
        }
    }
    else if (S_ISREG(fstat.st_mode))
        cout << "/etc/localtime Is a file" << endl;
} 
2
user361446

Libcは tzset が呼び出されたときにOlsonデータベースにアクセスし、後で簡略化されたタイムゾーンを使用します。 tzsetは最初にTZ環境変数を確認し、フォールバックして_/etc/localtime_のバイナリデータを解析します。

最初は systemd は、Olsonタイムゾーン名を_/etc/timezone_、 Debian-style で持つように標準化されています。 systemd 190と/ usrのマージ後、systemdは_/etc/localtime_の読み取りと更新のみを行い、ファイルが_/usr/share/zoneinfo/${OLSON_NAME}_へのシンボリックリンクであるという追加の要件があります。

TZ、次にreadlink("/etc/localtime")を見ると、libcのtzsetロジックを照合し、シンボリックオルソン名を保持する最も信頼できる方法です。 systemdシンボリックリンクの規則に従っていないシステムの場合、_/etc/timezone_を読み取ること(および/usr/share/zoneinfo/$(</etc/timezone)が_/etc/localtime_と同じであることを確認すること)は適切なフォールバックです。

シンボリック名なしで実行できる場合、 parsing _/etc/localtime_ tzfile は、非常に複雑ですが、できるだけ移植性があります。最後のフィールドだけを読み取ると、Posixタイムゾーンが得られます(例:_CST5CDT,M3.2.0/0,M11.1.0/1_)。これは、いくつかの時間処理ライブラリと相互運用できますが、メタデータの一部を削除します(履歴遷移情報はありません)。

0
Tobu

this ページによると、あなたは#include <time.h>以下を宣言します。

void tzset (void);
extern char *tzname[2];
extern long timezone;
extern int daylight;

これで必要な情報が得られますか?

0
torak

Tzselectは誰にも言及されておらず、ほぼ間違いのないソリューションが必要なため、Olsonが行った方法で作業してください。 elsieからtzcodeファイルとtzdataファイル、およびタブファイルを取得します。

ftp://elsie.nci.nih.gov

2017年3月の場合、ダウンロード元は ftp://ftp.iana.org/tz/releases (そしてダウンロードtzcode2017a.tar.gzおよびtzdata2017a.tar.gz)。

次に、glibcのダウンロードからtzselect.kshを取得します。次に、タイムゾーンをリバースエンジニアリングする方法を確認できます。 1つのこだわり:Linuxボックスがどの国や都市にあるかを尋ねなければならない場合があります。必要に応じてそのデータをシリアル化し、見つけたタイムゾーンデータと照合することができます。

たとえばプログラムのインストールの一部として、ユーザーが介入する可能性がない限り、これを常に確実に行う方法はありません。

アリゾナ、そして西インディアナでの幸運を祈ります。コードが他の場所で実行されることを願っています。

0
jim mcnamara

Linuxでは、現在のタイムゾーンをOlsonの場所として見つける必要があります。 (CまたはC++)コードをできるだけ多くのLinuxシステムに移植できるようにしたい。

ポータブルにしたい場合は、内部ではGMTのみを使用してください。マルチユーザーの遺産のため、* NIXシステムクロックは通常GMTであり、システム全体のタイムゾーンはありません-システムに接続しているさまざまなユーザーがさまざまなタイムゾーンに住んでいる可能性があるためです。

ユーザー固有のタイムゾーンはTZ環境変数に反映され、内部の日付/時刻をユーザーが読み取り可能な形式に変換する場合にのみ使用する必要がある場合があります。それ以外の場合は、localtime()が自動的に処理します。

0
Dummy00001