c++ - std::get_time doesn't always give the same result - Stack Overflow

What I want to doI am working on a small discord bot to handle friendly bets on ESport matches with my

What I want to do

I am working on a small discord bot to handle friendly bets on ESport matches with my friends. Lately I have been trying to add the date and hours of the matches to:

  1. Display only the coming matches
  2. Don't allow the addition of new matches with past dates
  3. Display as a string the date and hours of the matches

What I did

So I looked on google how I could do that and I ended up with that very simple class:

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;
};

The problem

For some reason, with the same string as input, I don't have the same result depending on if I am executing my unit tests or the bot itself.

The unit tests:

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");
}

All results are green so I works fine.

But if I execute my Discord Command with the date "01-01-2028 18:00" I have this error message (my error when giving a past date):

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

As you can see, it is not the date I gave in input.

Debugging

At first I though that I had a problem in between the command and the DateTime's contructor like something altering the input string. But when I check add a breakpoint in the constructor, the input string is correct. So I must admit that I don't really understand what is happening.

What I want to do

I am working on a small discord bot to handle friendly bets on ESport matches with my friends. Lately I have been trying to add the date and hours of the matches to:

  1. Display only the coming matches
  2. Don't allow the addition of new matches with past dates
  3. Display as a string the date and hours of the matches

What I did

So I looked on google how I could do that and I ended up with that very simple class:

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;
};

The problem

For some reason, with the same string as input, I don't have the same result depending on if I am executing my unit tests or the bot itself.

The unit tests:

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");
}

All results are green so I works fine.

But if I execute my Discord Command with the date "01-01-2028 18:00" I have this error message (my error when giving a past date):

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

As you can see, it is not the date I gave in input.

Debugging

At first I though that I had a problem in between the command and the DateTime's contructor like something altering the input string. But when I check add a breakpoint in the constructor, the input string is correct. So I must admit that I don't really understand what is happening.

Share Improve this question asked Mar 8 at 17:05 MaeglixMaeglix 551 silver badge6 bronze badges 4
  • What is your DATE_TIME_FORMAT? – ruakh Commented Mar 8 at 17:16
  • It is "%d-%m-%Y %H:%M". – Maeglix Commented Mar 8 at 17:17
  • 1 Well, the problem is in the code not shown. You say you have the problem when you use your class in "the bot itself", but you don't show how you use the class there. The error message mentions "given date" - who gives what date to whom how? Show a minimal reproducible example. – Igor Tandetnik Commented Mar 8 at 18:04
  • 1 Maybe (well likely) the discord server has a different locale active and that needs to be taken into account. It would help us much if you also could show the output for the code as it runs on the discord server) – Pepijn Kramer Commented Mar 8 at 18:10
Add a comment  | 

1 Answer 1

Reset to default 1

The C timing API can be error prone to use.

In this function you need to carefully initialize the std::tm before passing it into get_time:

explicit DateAndTime(const std::string& timeAsString)
{
    std::istringstream timeAsStream{ timeAsString };
    m_Time = {};           // Add this
    m_Time.tm_isdst = -1;  // Add this
    timeAsStream >> std::get_time(&m_Time, std::string{ DATE_TIME_FORMAT }.c_str());
    if (timeAsStream.fail())
    {
        throw InvalidDateFormat(timeAsString);
    }
}

You need to zero the whole thing, and then set the tm_isdst member to something negative to indicate that the daylight saving is unknown and the library should figure it out for itself.

You could also opt to abandon the C API and use <chrono> exclusively (if you have C++20). That would look like this:

class DateAndTime
{
public:
    explicit DateAndTime(const std::string& timeAsString)
    {
        std::istringstream timeAsStream{ timeAsString };
        timeAsStream >> std::chrono::parse(DATE_TIME_FORMAT, m_Time);
        if (timeAsStream.fail())
        {
            throw InvalidDateFormat(timeAsString);
        }
    }
    
    [[nodiscard]] std::string ToString() const noexcept
    {
        return std::vformat("{:" + DATE_TIME_FORMAT + '}', std::make_format_args(m_Time));
    }
    
    [[nodiscard]] bool IsInFuture() noexcept
    {
        const auto dateInSeconds = std::chrono::current_zone()->to_sys(m_Time);

        const auto now = std::chrono::system_clock::now();
        const auto nowAsSeconds = std::chrono::floor<std::chrono::seconds>(now);

        return dateInSeconds > nowAsSeconds;
    }

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

    std::chrono::local_seconds m_Time;
};

The <chrono> version is more explicit in the fact that m_Time is a local time in the computer's current local time zone. And if instead m_Time is UTC, or in some other time zone, it is trivial to change the code to reflect that.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744890730a4599395.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信