strftime returns iOS 星期几不正确

strftime returns incorrect day of the week on iOS

我有以下代码:

#include <iostream>
#include <ctime>
using namespace std; 

int main() {
    tm _tm;
    strptime("2017-04-17", "%Y-%m-%d", &_tm);

    char buf[16];
    strftime(buf, sizeof(buf), "%A", &_tm);

    cout << buf << endl;
}

Ideone,它正确输出 "Monday"(今天是星期几)。当我在 iOS 上编译和 运行 相同的代码时,它 returns "Sunday"。给了什么?!

编辑: 对于所有无法理解这为什么也是 C 问题的人,这里是 C 代码。问题依旧:

#include <stdio.h>
#include <time.h>

int main(void) {
    struct tm _tm;
    strptime("2017-04-17", "%Y-%m-%d", &_tm);

    char buf[16];
    strftime(buf, sizeof(buf), "%A", &_tm);

    printf(buf);
}

strptime() 仅更新提供的 struct tm 中格式字符串中指定的字段。其他字段保持不变(在您的情况下未初始化)。

有两个问题导致问题中的行为。

  1. strptime 似乎在解析日期时意外地使本地时区生效(但仅用于设置星期几!)。似乎当它解析“2017-04-17”时,至少在星期几,它把它当作午夜 UTC 来处理——这意味着在负 UTC 中抵消了星期几 "time" 早一天。

    基本上 strptime 总是给我 tm_wday 比正确值少一。解析“2017-04-17”(星期一)时,return 0(星期日)表示 tm_wday,“2017-04-18”表示 1(星期一) "(星期二)。

    非常奇怪的是它不会填写其他时区数据,例如tm_isdsttm_gmtoff。它会让那些保持不变 - 只需为 tm_wday.

    选择一个错误的值

    我能够通过在生成的 tm 结构 return 上调用 mktime 来解决这个问题 strptime。我没有使用 mktime 中的 time_t returned,但是在 tm 结构上调用 mktime 的行为正确设置了 [=13 的值=]、以及 正确设置 tm_isdsttm_gmtoff 等的值。

  2. 我没有对 tm 结构进行零初始化。这只有在我开始调用 mktime 后才真正发挥作用。如果没有正确的初始化,mktime 会因为 tm_gmtoff 的垃圾值而破坏日期。在正确初始化之后,调用 mktime 给出了一个格式正确的日期,其中设置了正确的 tm_wday

所以一个有效的例子是这样的:

tm _tm = {};
strptime("2017-04-17", "%Y-%m-%d", &_tm); // Incorrect tm_wday after this call
mktime(&_tm); // Correct tm_wday after this call

char buf[16];
strftime(buf, sizeof(buf), "%A", &_tm);

不是答案,但也许是解决方案:

如果您可以使用 C++11 或更高版本,Howard Hinnant's <chrono>-based, free, open-source, header-only date/time library 可以在两个平台上为您提供相同的答案。

#include "date.h"
#include <iostream>
#include <sstream>

int
main()
{
    using namespace std;
    istringstream in{"2017-04-17"};
    date::year_month_day ymd;
    in >> parse("%Y-%m-%d", ymd);
    cout << format("%A\n", ymd);
}

便携式输出:

Monday

此库将解析和格式化解释为隐含的 UTC,没有时区或您计算机当前本地时区的恶作剧。如果您需要该功能,它存在于 separate library built on top of this one.

"%F" 也作为 shorthand 用于 "%Y-%m-%d",作为 POSIX strptime 规范的扩展。

in >> parse("%F", ymd);

这是一个功能非常全面的日期时间库,可以处理粗略到一年的日历特征,以及精确到纳秒级的时间戳。它全部构建在 C++ <chrono> 库上,因此是完全类型安全的。随后它比 C/POSIX API 更容易使用,在编译时检测到许多逻辑错误。