You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

std::get_time执行结果不一致问题排查求助

问题背景

需求说明

我正在开发一个小型Discord Bot,用于和朋友一起处理电竞比赛的友好赌注。近期尝试为比赛添加日期时间功能,以实现:

  • 仅显示即将到来的比赛
  • 禁止添加日期已过期的比赛
  • 将比赛日期时间转换为字符串展示

实现代码

我实现了如下DateAndTime类:

class DateAndTime
{
public:
    explicit DateAndTime(const std::string& timeAsString)
    {
        std::istringstream timeAsStream{ timeAsString };
        timeAsStream >> std::get_time(&m_Time, std::string{ DATE_TIME_FORMAT }.c_str());
        if (timeAsStream.fail())
        {
            throw InvalidDateFormat(timeAsString);
        }
    }
    
    [[nodiscard]] std::string ToString() const noexcept
    {
        std::stringstream resultAsStream;
        resultAsStream << std::put_time(&m_Time, std::string{DATE_TIME_FORMAT}.c_str());

        return resultAsStream.str();
    }
    
    [[nodiscard]] bool IsInFuture() noexcept
    {
        const std::time_t dateInSeconds = std::mktime(&m_Time);

        const std::chrono::time_point now = std::chrono::system_clock::now();
        const std::time_t nowAsSeconds = std::chrono::system_clock::to_time_t(now);

        return dateInSeconds > nowAsSeconds;
    }

private:
    static constexpr std::string_view DATE_TIME_FORMAT = "%d-%m-%Y %H:%M";

    std::tm m_Time;
};

问题现象

同一输入字符串在单元测试和Bot运行时得到不同结果:
单元测试代码:

TEST(DateAndTime_Tests, ToString)
{
    const DateAndTime test{ "10-01-1995 18:00" };
    EXPECT_EQ(test.ToString(), "10-01-1995 18:00");

    const DateAndTime test2{ "10-01-1995 18:00:00" };
    EXPECT_EQ(test2.ToString(), "10-01-1995 18:00");

    const DateAndTime test3{ "01-01-2028 18:00" };
    EXPECT_EQ(test3.ToString(), "01-01-2028 18:00");
}

所有单元测试均通过,表现正常。

但在Discord命令中输入日期"01-01-2028 18:00"时,却触发了过期日期错误:

User error: The given Date [12-10-2000 16:42] is in the past.

可见解析出的日期与输入完全不符。

调试情况

起初怀疑命令到DateAndTime构造函数之间的输入字符串被篡改,但在构造函数中打断点后确认输入字符串是正确的,目前无法定位问题原因。


问题分析与解决方案

根本原因

问题出在std::tm类型成员m_Time的未初始化上:

  • 类成员m_Time默认未初始化,其内部字段(如tm_isdst、tm_yday等)会是随机垃圾值
  • std::get_time只会填充格式字符串中匹配的字段(日、月、年、时、分),不会处理其他未匹配字段
  • 调用std::mktime转换时间时,这些垃圾值会导致转换结果完全错误,不同运行环境的初始垃圾值不同,就出现了单元测试正常但Bot运行异常的情况

修复方案

在构造函数中显式初始化m_Time,确保所有字段都有合理初始值,有两种常见方式:

方式1:初始化为全0

explicit DateAndTime(const std::string& timeAsString)
{
    // 用memset将tm结构体所有字段置0
    std::memset(&m_Time, 0, sizeof(m_Time));
    
    std::istringstream timeAsStream{ timeAsString };
    timeAsStream >> std::get_time(&m_Time, std::string{ DATE_TIME_FORMAT }.c_str());
    if (timeAsStream.fail())
    {
        throw InvalidDateFormat(timeAsString);
    }
}

方式2:基于当前时间初始化

如果需要保留时区、夏令时等系统默认设置,可以基于当前时间初始化:

explicit DateAndTime(const std::string& timeAsString)
{
    std::time_t now = std::time(nullptr);
    // 复制当前时间的tm结构体,再覆盖需要的字段
    m_Time = *std::localtime(&now);
    
    std::istringstream timeAsStream{ timeAsString };
    timeAsStream >> std::get_time(&m_Time, std::string{ DATE_TIME_FORMAT }.c_str());
    if (timeAsStream.fail())
    {
        throw InvalidDateFormat(timeAsString);
    }
}

额外建议

  • 在IsInFuture方法中检查std::mktime的返回值,mktime转换失败会返回-1,可以提前抛出错误避免逻辑异常
  • 补充单元测试覆盖IsInFuture方法,测试未来、过去、当前时间的场景,提前发现环境相关问题

内容的提问来源于stack exchange,提问作者Maeglix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 01:04:52