struct tmはタイムゾーン情報をデータメンバーとして保存しますか


8

次のC ++コードを検討してください。

#include <ctime>
#include <iostream>

int main()
{
    std::time_t now = std::time(nullptr);
    struct tm local = *std::localtime(&now);
    struct tm gm = *std::gmtime(&now);
    char str[20];
    std::strftime(str, 20, "%Z", &local);
    std::cout << str << std::endl;          // HKT
    std::strftime(str, 20, "%Z", &gm);
    std::cout << str << std::endl;          // UTC

    return 0;
}

だから、中に格納されてnowいる間、明確な積分値であるlocalgmされstruct tmている店舗の人間が読める日付/時刻情報。次に、struct tmオブジェクトのみに基づいてフォーマットされた情報(タイムゾーン)を出力します。

cplusplus リファレンスによると、のデータメンバーstruct tm

tm_sec  
tm_min  
tm_hour 
tm_mday 
tm_mon  
tm_year 
tm_wday 
tm_yday 
tm_isdst

それがaにstruct tm含まれているすべての場合、プログラムはどのようにしてそれからタイムゾーン情報を認識しますか?つまり、それはどのようにタイムゾーンがあることを知っているHKTためlocal、タイムゾーンがあることUTCのためにgm

それだけではない場合struct tmは、タイムゾーン情報の格納方法を説明してください。

ちなみに、デモコードはC ++ですが、この質問は本質的には正当なCの質問でもあると思います。


2
tmタイムゾーン情報は含まれていません。strftime舞台裏のブードゥー教によってタイムゾーンを取得します。一般的にタイムゾーンを取得したい場合、それは少し混乱しています。(現在)タイムゾーンを取得する標準的な方法はありません。幸いハワードヒナントは、そのジョブにあります...
user4581301

ありがとう@ user4581301これは私の質問に部分的に答えます。しかし、私はまだフォローアップの質問がある:に保存されているすべての情報与えられtm、どのようにしてstrftime2枚に異なる方法で応答するために知っているstruct tmオブジェクト?ない限り、tmのようないくつかの情報が含まれ、これはtmによって作成されlocaltimeそのtmことによって作成されますgmtime
-aafulei

tm構造体は、タイムゾーンに関する情報が格納されていない、あなたはそれが何だと思いますか?違いは、とへの呼び出しにgmtime()ありlocaltime()ます。
Ulrich Eckhardt

マニュアルページでは、POSIXシステムでタイムゾーン情報を取得する方法について説明しています。まだstrftime吸盤を区別する方法を探しています。POSIXは何が起こるかを未定義のままにしておく必要があります。
user4581301

1
WindowsではMSYS2のgcc 9.2.0で完全に失敗します。それを見てtm、追加の情報を含む非標準のsを見たことを思い出しました。こちらが1つです。const char *tm_zoneメンバーに注意してください。どのプラットフォーム用にコンパイルしていますか?見てみましょうtm、彼らは構造を拡張しましたかどうかを確認するために実施。
user4581301

回答:


5

C標準では、7.27.1時間の構成要素で次のように述べています。

tm構造は含まなければならない、少なくとも任意の順序で、以下のメンバーを。メンバーのセマンティクスとその通常の範囲はコメントに記載されています。318)

int tm_sec;    // seconds after the minute — [0, 60]
int tm_min;    // minutes after the hour — [0, 59]
int tm_hour;   // hours since midnight — [0, 23]
int tm_mday;   // day of the month — [1, 31]
int tm_mon;    // months since January — [0, 11]
int tm_year;   // years since 1900
int tm_wday;   // days since Sunday — [0, 6]
int tm_yday;   // days since January 1 — [0, 365]
int tm_isdst;  // Daylight Saving Time flag

(強調は私のものです)

つまり、実装では、でtm見つけたように、にメンバーを追加できますglibc/time/bits/types/struct_tm.h。POSIX仕様はほぼ同一の文言を持っています。

その結果、%Z(または%z)での移植性は考えられませんstrftime。の仕様%Zはこれを反映しています:

%Zロケールのタイムゾーン名または省略形、またはタイムゾーンが特定できない場合は文字なしで置き換えられます [tm_isdst]

つまり、ベンダーは自分の手を投げて、「タイムゾーンは決定できなかったので、文字をまったく出力しません」と単純に言うことが許可されています。

私の意見:CタイミングAPIは混乱しています。


<chrono>ライブラリ内の次期C ++ 20標準に向けて改善を試みています。

C ++ 20仕様では、これが「文字なし」から、time_zone省略形が利用できない場合にスローされる例外に変更されています。

http://eel.is/c++draft/time.format#3

明示的に要求されない限り、クロノタイプのフォーマットの結果には、タイムゾーンの省略形とタイムゾーンのオフセット情報は含まれません。情報が利用可能な場合、変換指定子%Zおよび%zはこの情報を(それぞれ)フォーマットします。[ 注:情報が利用できず、%Zまたは%z 変換指定子がchrono-format-specにある場合、format_­error上記のようにtypeの例外がスローされます。—エンドノート ]

上の段落は、Cさんを記述されていないことを除いてstrftime、新しいformatを操作する関数std::chronoの種類、ではありませんtm。:さらに新しいタイプがある std::chrono::zoned_timehttp://eel.is/c++draft/time.zone.zonedtime)ことを常に持っているtime_zone略語を(オフセット)が使用可能と前述上記でフォーマットすることができるformat機能が。

コード例:

#include <chrono>
#include <iostream>

int
main()
{
    using namespace std;
    using namespace std::chrono;
    auto now = system_clock::now();
    std::cout << format("%Z\n", zoned_time{current_zone(), now});   // HKT (or whatever)
    std::cout << format("%Z\n", zoned_time{"Asia/Hong_Kong", now}); // HKT or HKST
    std::cout << format("%Z\n", zoned_time{"Etc/UTC", now});        // UTC
    std::cout << format("%Z\n", now);                               // UTC
}

(免責事項:format関数のフォーマット文字列の最終的な構文は少し異なる可能性がありますが、機能はそこにあります。)

このライブラリのプレビューを試してみたい場合は、こちらから無料でオープンソースを入手できます:https : //github.com/HowardHinnant/date

いくつかのインストールが必要です:https : //howardhinnant.github.io/date/tz.html#Installation

このプレビューでは、ヘッダーを使用する必要があり"date/tz.h"、ライブラリのコンテンツはではnamespace dateなくにありnamespace std::chronoます。

プレビューライブラリは、C ++ 11以降で使用できます。

zoned_timestd::chrono::duration、時点の精度を指定するにテンプレート化されており、C ++ 17のCTAD機能を使用して上記のコード例で推定されています。このプレビューライブラリをC ++ 11またはC ++ 14で使用している場合、構文は次のようになります。

cout << format("%Z\n", zoned_time<system_clock::duration>{current_zone(), now});

または、推論を行う標準化されていない提案されたヘルパーファクトリ関数があります:

cout << format("%Z\n", make_zoned(current_zone(), now));

(#CTAD_eliminates_factory_functions)


2

正しい方向を示すのに役立つ質問へのすべてのコメントをありがとう。以下に自分の研究の一部を掲載します。私は、GitHubで見つけたGNU Cライブラリのアーカイブリポジトリに基づいて話しています。バージョンは2.28.9000です。

glibc/time/bits/types/struct_tm.hあり

struct tm
{
  int tm_sec;           /* Seconds. [0-60] (1 leap second) */
  int tm_min;           /* Minutes. [0-59] */
  int tm_hour;          /* Hours.   [0-23] */
  int tm_mday;          /* Day.     [1-31] */
  int tm_mon;           /* Month.   [0-11] */
  int tm_year;          /* Year - 1900.  */
  int tm_wday;          /* Day of week. [0-6] */
  int tm_yday;          /* Days in year.[0-365] */
  int tm_isdst;         /* DST.     [-1/0/1]*/

# ifdef __USE_MISC
  long int tm_gmtoff;       /* Seconds east of UTC.  */
  const char *tm_zone;      /* Timezone abbreviation.  */
# else
  long int __tm_gmtoff;     /* Seconds east of UTC.  */
  const char *__tm_zone;    /* Timezone abbreviation.  */
# endif
};

struct tm少なくともこの実装では、タイムゾーン情報を保存しているようです。


1

日付と時刻のプログラミングが非常に難しい理由の1つは、それが根本的に少なくともやや難しい問題であるということです。「30日間は9月です」、6 進数の算術演算、タイムゾーン、夏時間、うるう年などです。うるう秒について話します。

しかし、それが難しいもう1つの理由は、あまりに多くのライブラリと言語が完全に混乱していることです。残念ながら、Cも例外ではありません。(ハワードが彼の答えで言及しているように、C ++はより良くしようとしています。)

誰もがグローバル変数が悪いことを知っていますが、Cの日付/時刻関数は基本的にそれらのいくつかを使用します。実際、「このシステムの現在のタイムゾーン」の概念はグローバル変数であり、そのタイムゾーンを説明するグローバルデータはlocaltimestrftimeと他の多くの関数との間でまったく共有されません。

そのstrftimeため、値の一部として渡されない場合でも、そのグローバルデータを入力して%zそれに%Z基づくことができstruct tmます。

これは明らかに最適とは言えない配置であり、プログラムが使用したいタイムゾーンと残りの部分を動的に変更する方法があった場合、実際の問題を引き起こし始めlocaltimeます。(そして、プログラムが使用しているローカルタイムゾーンを変更するための、実際には適切で移植可能な標準的な方法がないため、この配置は持続します。)

長年にわたって、いくつかの混乱を片付けるためのさまざまな中途半端な試みがありました(もちろん、下位互換性を維持しながら)。それらの試みの1つは、一部のシステムのバージョンので発見した拡張フィールドtm_gmtofftm_zoneフィールドを含みますstruct tm。これらの追加は大幅な改善です-それらなしでシステムで深刻な日付/時刻プログラミングを行うことは想像できません-しかし、それらはまだ標準ではなく、それらを持たない多くのシステムがまだあります( 「隠された」スペル__tm_gmtoff__tm_zone)。

あなたはこの論文ではCでの日付/時刻のサポートの下劣な歴史についてはるかに読むことができます:Cでの時間、時計、およびカレンダーのプログラミングを、エリック・レイモンドによって。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.