struct tm是否将时区信息存储为其数据成员

aaf*_*lei 8 c c++ ctime time.h

考虑以下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;
}
Run Code Online (Sandbox Code Playgroud)

因此,其中存储的now是一个明确的整数值,而localgmstruct tm存储了人类可读的日期/时间信息。然后,仅基于struct tm对象打印出格式化的信息(时区)。

根据CPLUSPLUS 参考,的数据成员struct tm

tm_sec  
tm_min  
tm_hour 
tm_mday 
tm_mon  
tm_year 
tm_wday 
tm_yday 
tm_isdst
Run Code Online (Sandbox Code Playgroud)

如果仅此struct tm而已,程序如何从中得知时区信息?也就是说,如何知道时区HKT用于local,时区UTC用于gm

如果还不够struct tm,请解释一下它如何存储时区信息。

顺便说一句,尽管演示代码是用C ++编写的,但我想这个问题在本质上也代表合法的C问题。

How*_*ant 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
Run Code Online (Sandbox Code Playgroud)

(重点是我的)

也就是说tm,如您在中发现的那样,允许实现将其他成员添加到中glibc/time/bits/types/struct_tm.h。POSIX规范的措词几乎相同。

结果是%Z(甚至%z)无法视为可移植strftime。的规格%Z反映了这一点:

%Z替换为语言环境的时区名称或缩写,如果无法确定时区,则不替换任何字符。 [tm_isdst]

也就是说,允许供应商举手示意:“没有可确定的时区,所以我根本不输出任何字符。”

我的看法:C计时API很烂。


我正在尝试为<chrono>库中即将推出的C ++ 20标准进行改进。

如果time_zone缩写不可用,则C ++ 20规范草案将其从“无字符”更改为引发异常:

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

除非明确要求,否则格式化计时器类型的结果将不包含时区缩写和时区偏移信息。如果信息可用,则转换说明%Z%z将格式化该信息(分别地)。[ 注意:如果该信息不可用,并且chrono-format-spec中 出现%Z%z转换说明符,则抛出类型异常,如上所述。—尾注 ]format_­error

除了上面的段落没有描述C之外strftime,而是一个formatstd::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
}
Run Code Online (Sandbox Code Playgroud)

(免责声明:该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_time在上std::chrono::duration指定时间点的精度的模板,并在上面的示例代码中使用C ++ 17的CTAD功能推导。如果在C ++ 11或C ++ 14中使用此预览库,则语法将更像:

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

或者有一个非标准的标准化助手工厂功能,它将为您提供推论:

cout << format("%Z\n", make_zoned(current_zone(), now));
Run Code Online (Sandbox Code Playgroud)

(#CTAD_eliminates_factory_functions)