【问题标题】:strftime returns incorrect day of the week on iOSstrftime 在 iOS 上返回不正确的星期几
【发布时间】:2017-09-13 11:27:44
【问题描述】:

我有以下代码:

#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 上,它正确输出“星期一”(星期几)。当我在 iOS 上编译和运行相同的代码时,它返回“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);
}

【问题讨论】:

  • 来自 man7.org,“使用了环境变量 TZ 和 LC_TIME。”您可能希望在运行程序时检查每个系统上的这两个值。
  • @WeatherVane 我相信这也是一个有效的 C 问题。请不要删除标签。
  • @Olaf 你真的需要我将其重新格式化为 C 以表明它也是一个有效的 C 问题吗?
  • @aardvarkk ... 是吗?在目前的形式中,问题中没有任何与 C 相关的内容。
  • 而且strptime 不是标准函数。 ideone 当然不是一个权威性的参考。

标签: c++ ctime


【解决方案1】:

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

【讨论】:

  • 在这种情况下,tm_wday 实际上设置为 0。我尝试手动将其设置为 1,但 strptime 将其重置为 0(星期日,而不是星期一)。所以我不认为这是问题所在,尽管将结构初始化为零显然是我的最佳实践。不过,这并不能解决问题。
  • 我认为@yellowantphil 可能是对的?可能是时区问题。我提供的日期被解释为 time(可能是 UTC)。因为我的 UTC 偏移量是负数,所以它会将工作日计算为前一天?这就是我的跑步理论...
  • 我相信它可能与tm_isdst(与时区有关)有关。
  • 我不太确定这个答案 - 我会在删除之前让它静置一段时间,但我想有人会想出更好的答案。
  • 哦,mktime 确实需要零初始化值,否则它会将日期转换为非常不稳定的值!所以基本上我必须对从strptime 返回的值进行零初始化调用mktime,以将其转换为本地时区值,以便检索正确的wday 值。
【解决方案2】:

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

  1. strptime 在解析日期时似乎意外地使本地时区生效(但仅用于设置星期几!)。似乎当它解析“2017-04-17”时,至少在一周中的某一天,它把它当作像午夜 UTC 的东西——这意味着在一个负的 UTC 偏移中,那个“时间”的星期几是一天前。

    基本上strptime 总是给我tm_wday 比正确值小一。当解析“2017-04-17”(星期一)和 1(星期一)为“2017-04-18”(星期二)时,它将返回 0(星期日)为 tm_wday

    非常奇怪的是它不会填写其他时区数据,如tm_isdsttm_gmtoff。它会使那些保持不变——只需为 tm_wday 选择一个错误的值。

    我可以通过在从strptime 返回的生成的tm 结构上调用mktime 来解决此问题。我没有使用从mktime 返回的time_t,但是在tm 结构上调用mktime 的行为正确设置了tm_wday 的值,除了正确设置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);

【讨论】:

    【解决方案3】:

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

    如果您可以使用 C++11 或更高版本,Howard Hinnant's &lt;chrono&gt;-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" 也可用作 "%Y-%m-%d" 的简写,作为 POSIX strptime 规范的扩展。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-17
      • 2016-11-05
      • 2011-05-24
      相关资源
      最近更新 更多