gpt4 book ai didi

C++:Windows Vista 上 MinGW 下的 mktime 错误?

转载 作者:行者123 更新时间:2023-11-28 01:04:39 27 4
gpt4 key购买 nike

因此,我试图将格式为“2000-01-01”的日期转换为表示自某个任意来源(例如 1900/01/01)以来的天数的整数,以便我可以将它们视为整数索引。为此,我编写了一个转换函数,它在 Windows XP 下的 MinGW 上运行良好,但在 Vista 下运行不佳。我添加了一些日志记录代码:

int dateStrToInt(string date) {
int ymd[3];
tm tm1, tm0;
istringstream iss(date);
string s;
for (int i = 3; i; --i) {
getline(iss, s, '-');
ymd[3-i] = str2<int>(s);
}
cout << ymd[0] << ' ' << ymd[1] << ' ' << ymd[2] << ' ' << endl;
tm1.tm_year = ymd[0] - 1900;
tm1.tm_mon = ymd[1] - 1;
tm1.tm_mday = ymd[2];
time_t t1 = mktime(&tm1);
tm0.tm_year = 0;
tm0.tm_mon = 0;
tm0.tm_mday = 0;
time_t t0 = mktime(&tm0);

//cout << "times: " << mktime(&origin) << ' ' << mktime(&time) << endl;
cout << "times: " << t0 << ' ' << t1 << endl;

cout << "difftime: " << difftime(t1, t0) << endl;

return difftime(mktime(&tm1), mktime(&tm0)) / (60*60*24);
}

int i = dateStrToInt("2000-01-01");

我从中得到的输出是

2000 1 1
times: -1 -1
difftime: 0

这显然是错误的。我该怎么办?

编辑:正如下面的答案所说,1970 年之前的年份似乎存在问题。为了避免这种情况,我已经手动编写了自己的日计数函数:

int dateStrToInt(string date) {
int ymd[3];
istringstream iss(date);
string s;
for (int i = 0; i < 3; ++i) {
getline(iss, s, '-');
ymd[i] = str2<int>(s);
}
const static int cum_m_days[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
int year = ymd[0]+10000, month = ymd[1], day = ymd[2];
int days = year*365 + cum_m_days[month-1] + day;
// handle leap years
if (month <= 2)
--year;
days = days + (year/4) - (year/100) + (year/400);
return days;
}

最佳答案

将所有其他 struct tm 字段保留为默认值(在本例中为随机)不一定是个好主意。

该标准并没有明确说明在调用 mktime 之前需要设置哪些字段,但它确实说明它设置了 tm_wdaytm_yday基于其他字段,并且那些其他字段不限于有效。

标准展示的一件事是示例代码,它设置了所有字段,除了上面提到的那两个,所以这就是我的目标。

尝试更改计算时间的段:

tm1.tm_year = ymd[0] - 1900;
tm1.tm_mon = ymd[1] - 1;
tm1.tm_mday = ymd[2];
time_t t1 = mktime(&tm1);

tm0.tm_year = 0;
tm0.tm_mon = 0;
tm0.tm_mday = 0;
time_t t0 = mktime(&tm0);

类似于:

// Quick and dirty way to get decent values for all fields.

time_t filled_in;
time (&filled_in);
memcpy (&tm1, localtime ( &filled_in ), sizeof (tm1));
memcpy (&tm0, &tm1, sizeof (tm0));

// Now do the modifications to relevant fields, and calculations.

tm1.tm_year = ymd[0] - 1900;
tm1.tm_mon = ymd[1] - 1;
tm1.tm_mday = ymd[2];
time_t t1 = mktime(&tm1);

tm0.tm_year = 0;
tm0.tm_mon = 0;
tm0.tm_mday = 0;
time_t t0 = mktime(&tm0);

此外,在 XP 下使用 CygWin 进行的一些实验导致 mktime 似乎总是为 struct tm 结构返回 -1,其中 tm_year 是少于两个。这是否是一个真正的错误值得怀疑,因为我经常发现实现并不总是支持纪元(1970 年 1 月 1 日)之前的日期。

某些 UNIX 确实允许您指定小于 70 的 tm_year 值,并且它们通常可以使用 time_t 的这些“负”值来访问 1970 年以前的年份。

但是,由于标准并没有真正涉及到这一点,所以它留给了实现。在 7.23.1/4 中可以找到继承到 C++ 的 C99 标准(可能还有更早的迭代)的相关位:

The range and precision of times representable in clock_t and time_t are implementation-defined.

最安全的做法是使用纪元开始后的日期作为基线日期。这显示在以下代码中:

#include <iostream>
#include <sstream>
#include <string>
#include <ctime>
#include <cstring>
#include <cstdlib>

int dateStrToInt(std::string date) {
int ymd[3];
tm tm1, tm0;
std::istringstream iss(date);
std::string s;

// Test code.
ymd[0] = 2000; ymd[1] = 1; ymd[2] = 1;

std::cout << ymd[0] << ' ' << ymd[1] << ' ' << ymd[2] << ' ' << std::endl;

time_t filled_in;
time (&filled_in);
std::memcpy (&tm0, localtime ( &filled_in ), sizeof (tm0));
std::memcpy (&tm1, &tm0, sizeof (tm1));

tm1.tm_year = ymd[0] - 1900;
tm1.tm_mon = ymd[1] - 1;
tm1.tm_mday = ymd[2];
time_t t1 = mktime(&tm1);

tm0.tm_year = 1970 - 1900; // Use epoch as base date.
tm0.tm_mon = 0;
tm0.tm_mday = 1;

time_t t0 = mktime(&tm0);

std::cout << "times: " << t0 << ' ' << t1 << std::endl;
std::cout << "difftime: " << difftime(t1, t0) << std::endl;
return difftime(mktime(&tm1), mktime(&tm0)) / (60*60*24);
}

int main (void) {
int i = dateStrToInt("2000-01-01");
double d = i; d /= 365.25;
std::cout << i << " days, about " << d << " years." << std::endl;
return 0;
}

这会输出预期的结果:

2000 1 1
times: 31331 946716131
difftime: 9.46685e+08
10957 days, about 29.9986 years.

作为附录,POSIX 有 this说:


自纪元以来 4.14 秒

一个近似于自纪元以来经过的秒数的值。协调世界时名称(以秒 (tm_sec)、分钟 (tm_min)、小时 (tm_hour)、自当年 1 月 1 日以来的天数 (tm_yday) 和减去 1900 的日历年 (tm_year) 指定)与根据以下表达式,时间表示为自纪元以来的秒数。

如果年份<1970 或值为负,则关系未定义。如果年份 >=1970 且值为非负数,则根据 C 语言表达式,该值与协调世界时名称相关,其中 tm_sec、tm_min、tm_hour、tm_yday 和 tm_year 均为整数类型:

tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
(tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400

一天中的实际时间与自纪元以来秒数的当前值之间的关系未指定。

自 Epoch 以来秒值的任何更改如何与当前实际时间对齐以符合所需的关系是实现定义的。以纪元以来的秒数表示,每一天都应准确计算 86400 秒。

注意:表达式的最后三项在从大纪元以来第一个闰年开始的闰年之后的每一年中添加一天。第一项从 1973 年开始每 4 年增加一天,第二项从 2001 年开始每 100 年减少一天,第三项从 2001 年开始每 400 年增加一天。公式中的除法是整数除法;也就是说,余数被丢弃,只留下整数商。


换句话说(请参阅“如果年份是 <1970 年或值为负数,则关系未定义”),使用 1970 年之前的日期需要您自担风险。

关于C++:Windows Vista 上 MinGW 下的 mktime 错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6937606/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com