[英]C++ vs. python: daylight saving time not recognized when extracting Unix time?
我正在尝试计算由两个整数表示的给定日期和时间的 Unix 时间,例如
testdate1 = 20060711
(2006 年 7 月 11 日)
testdate2 = 4
(00:00:04,午夜后 4 秒)
在我当地时区以外的时区。 为了计算 Unix 时间,我将testdate1
、 testdate2
输入到 function 我改编自Convert date to unix time stamp226CE809EACF903926CE809EACF9039
int unixtime (int testdate1, int testdate2) {
time_t rawtime;
struct tm * timeinfo;
//time1, ..., time6 are external functions that extract the
//year, month, day, hour, minute, seconds digits from testdate1, testdate2
int year=time1(testdate1);
int month=time2(testdate1);
int day=time3(testdate1);
int hour=time4(testdate2);
int minute=time5(testdate2);
int second=time6(testdate2);
time ( &rawtime );
timeinfo = localtime ( &rawtime );
timeinfo->tm_year = year - 1900;
timeinfo->tm_mon = month - 1;
timeinfo->tm_mday = day;
timeinfo->tm_hour = hour;
timeinfo->tm_min = minute;
timeinfo->tm_sec = second;
int date;
date = mktime(timeinfo);
return date;
}
我从主代码中调用
using namespace std;
int main(int argc, char* argv[])
{
int testdate1 = 20060711;
int testdate2 = 4;
//switch to CET time zone
setenv("TZ","Europe/Berlin", 1);
tzset();
cout << testdate1 << "\t" << testdate2 << "\t" << unixtime(testdate1,testdate2) << "\n";
return 0;
}
对于给定的示例,我得到unixtime(testdate1,testdate2) = 1152572404
,根据
https://www.epochconverter.com/timezones?q=1152572404&tz=Europe%2FBerlin
是中欧标准时间上午 1:00:04,但我希望这是中欧标准时间 0:00:04。
如果我选择不遵守夏令时 (DST) 的testdate1
、 testdate2
,代码似乎运行良好。 例如,通过设置testdate1 = 20060211
来简单地将月份设置为二月,而其他所有内容都保持不变。 根据需要,这给出了unixtime(testdate1,testdate2) = 1139612404
,对应于 CET 中的 hh:mm:ss = 00:00:04。
我的印象是setenv("TZ","Europe/Berlin", 1)
在适用时应该考虑 DST,但也许我弄错了。 TZ 可以解释testdate1
, testdate2
以说明 DST 吗?
有趣的是,我有一个 python 代码,它通过os.environ['TZ'] = 'Europe/Berlin'
更改本地时间来执行相同的任务。 在这里我没有问题,因为它似乎计算正确的 Unix 时间,而不管 DST/非 DST。
localtime
将timeinfo->tm_isdst
设置为当前时间 - 而不是您解析的日期。
不要拨打localtime
。 将timeinfo->tm_isdst
设置为-1
:
tm_isdst
字段中指定的值通知mktime()
对于 tm 结构中提供的时间,夏令时 (DST) 是否有效:正值表示 DST 有效; 零表示 DST 无效; 负值意味着mktime()
应该(使用时区信息和系统数据库)尝试确定 DST 在指定时间是否有效。
马克西姆的答案是正确的,我已经投了赞成票。 但我也认为展示如何使用较新的<chrono>
工具在 C++20 中完成此操作可能会有所帮助。 这还没有在任何地方实现,但它在 Visual Studio 中,很快就会出现在其他地方。
我想在这里说明两个要点:
<chrono>
对于这样的转换很方便,即使输入和 output 都不涉及std::chrono
chrono 类型。 可以将积分输入转换为计时,进行转换,然后将计时结果转换回积分。
使用TZ
环境变量存在线程安全漏洞,因为这是一种全局变量。 如果另一个线程也在进行某种类型的时间计算,如果计算机的时区意外地从它下面改变,它可能无法得到正确的答案。 <chrono>
解决方案是线程安全的。 它不涉及全局变量或环境变量。
第一项工作是解包积分数据。 在这里,我将展示如何做到这一点,并一步将其转换为chrono
类型:
std::chrono::year_month_day
get_ymd(int ymd)
{
using namespace std::chrono;
day d(ymd % 100);
ymd /= 100;
month m(ymd % 100);
ymd /= 100;
year y{ymd};
return y/m/d;
}
get_ymd
采用“ testdate1
”,提取日、月和年的各个整数字段,然后将每个整数字段转换为std::chrono
类型的day
、 month
和year
,最后将这三个单独的字段组合成一个std::chrono::year_month_day
将其作为一个值返回。 这个返回类型是一个简单的{year, month, day}
数据结构——就像一个tuple
,但具有日历意义。
/
语法只是一个方便的工厂 function 用于构造year_month_day
。 并且可以使用以下三个顺序中的任何一个来完成此构造: y/m/d
、 d/m/y
和m/d/y
。 此语法与auto
结合使用时,还意味着您通常不必拼出冗长的名称year_month_day
:
auto
get_ymd(int ymd)
{
// ...
return y/m/d;
}
get_hms
解包小时、分钟和秒字段,并将其作为std::chrono::seconds
返回:
std::chrono::seconds
get_hms(int hms)
{
using namespace std::chrono;
seconds s{hms % 100};
hms /= 100;
minutes m{hms % 100};
hms /= 100;
hours h{hms};
return h + m + s;
}
该代码与get_ymd
的代码非常相似,只是返回的是小时、分钟和秒的总和。 chrono
库为您完成在执行求和时将小时和分钟转换为秒的工作。
接下来是 function 用于进行转换,并将结果作为int
返回。
int
unixtime(int testdate1, int testdate2)
{
using namespace std::chrono;
auto ymd = get_ymd(testdate1);
auto hms = get_hms(testdate2);
auto ut = locate_zone("Europe/Berlin")->to_sys(local_days{ymd} + hms);
return ut.time_since_epoch().count();
}
调用std::chrono::locate_zone
以获取指向名为 "Europe/Berlin" 的std::chrono::time_zone
的指针。 std::lib 管理这个 object 的生命周期,所以你不必担心它。 它是一个const
singleton,按需创建。 它对您的计算机认为其“本地时区”的时区没有影响。
std::chrono::time_zone
有一个名为to_sys
的成员 function ,它采用local_time
并将其转换为sys_time
,使用该时区的正确 UTC 偏移量(在适用时考虑夏令时规则)。
local_time
和sys_time
都是std::chrono::time_point
类型。 local_time
是“某个本地时间”,不一定是您计算机的本地时间。 您可以将本地时间与时区相关联,以指定该时间的位置。
sys_time
是一个基于system_clock
的time_point
点。 这跟踪 UTC(Unix 时间)。
表达式local_days{ymd} + hms
将ymd
和hms
转换为local_time
,精度为seconds
。 local_days
只是另一个local_time
time_point
,但精度为days
。
ut
的类型是time_point<system_clock, seconds>
,它有一个方便的类型别名,称为sys_seconds
,尽管auto
使该名称在此代码中不必要。
要将sys_seconds
解包为整数类型, .time_since_epoch()
成员 function 导致duration
seconds
,然后调用 .count .count()
成员 function 以从该duration
中提取整数值。
当int
为 32 位时,这个 function 容易受到2038 年溢出问题的影响。 要解决这个问题,只需将unixtime
的返回类型更改为返回 64 位整数类型(或返回auto
)。 没有其他需要更改,因为std::chrono::seconds
已经要求大于 32 位并且在 68 年不会溢出。 事实上std::chrono::seconds
在实践中通常由带符号的 64 位整数类型表示,使其范围大于宇宙的年龄(即使科学家偏离了一个数量级)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.