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是一个明确的整数值,而local和gm则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
Run Code Online (Sandbox Code Playgroud)
如果仅此struct tm而已,程序如何从中得知时区信息?也就是说,如何知道时区HKT用于local,时区UTC用于gm?
如果还不够struct tm,请解释一下它如何存储时区信息。
顺便说一句,尽管演示代码是用C ++编写的,但我想这个问题在本质上也代表合法的C问题。
C标准在7.27.1时间部分中说:
该
tm结构至少应按任何顺序包含以下成员。成员的语义及其正常范围在注释中表达。318)Run Code Online (Sandbox Code Playgroud)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标准进行改进。
如果time_zone缩写不可用,则C ++ 20规范草案将其从“无字符”更改为引发异常:
http://eel.is/c++draft/time.format#3
除非明确要求,否则格式化计时器类型的结果将不包含时区缩写和时区偏移信息。如果信息可用,则转换说明
%Z和%z将格式化该信息(分别地)。[ 注意:如果该信息不可用,并且chrono-format-spec中 出现%Z或%z转换说明符,则抛出类型异常,如上所述。—尾注 ]format_error
除了上面的段落没有描述C之外strftime,而是一个format对std::chrono类型(而不是类型)进行操作的新函数tm。此外,还有一种新类型:std::chrono::zoned_time(http://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)