繁体   English   中英

C++ 与 python:提取 Unix 时间时无法识别夏令时?

[英]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 时间,我将testdate1testdate2输入到 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) 的testdate1testdate2 ,代码似乎运行良好。 例如,通过设置testdate1 = 20060211来简单地将月份设置为二月,而其他所有内容都保持不变。 根据需要,这给出了unixtime(testdate1,testdate2) = 1139612404 ,对应于 CET 中的 hh:mm:ss = 00:00:04。

我的印象是setenv("TZ","Europe/Berlin", 1)在适用时应该考虑 DST,但也许我弄错了。 TZ 可以解释testdate1testdate2以说明 DST 吗?

有趣的是,我有一个 python 代码,它通过os.environ['TZ'] = 'Europe/Berlin'更改本地时间来执行相同的任务。 在这里我没有问题,因为它似乎计算正确的 Unix 时间,而不管 DST/非 DST。

localtimetimeinfo->tm_isdst设置为当前时间 - 而不是您解析的日期。

不要拨打localtime timeinfo->tm_isdst设置为-1

tm_isdst字段中指定的值通知mktime()对于 tm 结构中提供的时间,夏令时 (DST) 是否有效:正值表示 DST 有效; 零表示 DST 无效; 负值意味着mktime()应该(使用时区信息和系统数据库)尝试确定 DST 在指定时间是否有效

请参阅https://en.cppreference.com/w/cpp/chrono/c/mktime中的代码示例

马克西姆的答案是正确的,我已经投了赞成票。 但我也认为展示如何使用较新的<chrono>工具在 C++20 中完成此操作可能会有所帮助。 这还没有在任何地方实现,但它在 Visual Studio 中,很快就会出现在其他地方。

我想在这里说明两个要点:

  1. <chrono>对于这样的转换很方便,即使输入和 output 都不涉及std::chrono chrono 类型。 可以将积分输入转换为计时,进行转换,然后将计时结果转换回积分。

  2. 使用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类型的daymonthyear ,最后将这三个单独的字段组合成一个std::chrono::year_month_day将其作为一个值返回。 这个返回类型是一个简单的{year, month, day}数据结构——就像一个tuple ,但具有日历意义。

/语法只是一个方便的工厂 function 用于构造year_month_day 并且可以使用以下三个顺序中的任何一个来完成此构造: y/m/dd/m/ym/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_timesys_time都是std::chrono::time_point类型。 local_time是“某个本地时间”,不一定是您计算机的本地时间。 您可以将本地时间与时区相关联,以指定该时间的位置。

sys_time是一个基于system_clocktime_point点。 这跟踪 UTC(Unix 时间)。

表达式local_days{ymd} + hmsymdhms转换为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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM