Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fail time parsing if input is insufficient to supply all fields #2523

Merged
merged 5 commits into from
Mar 28, 2022

Conversation

ecatmur
Copy link
Contributor

@ecatmur ecatmur commented Feb 7, 2022

(and literals) of the format spec, even if it is sufficient to fully
specify the parsable argument.

For example "23:59" is not a valid input for "%H:%M:%S" (or for "%T").

https://eel.is/c++draft/time.parse#17.sentence-1

If the from_stream overload fails to parse everything specified by
the format string, or if insufficient information is parsed to specify a
complete duration, time point, or calendrical data structure,
setstate(ios_base::failbit) is called on the basic_istream.

(and literals) of the format spec, even if it is sufficient to fully
specify the parsable argument.

For example "23:59" is not a valid input for "%H:%M:%S" (or for "%T").

https://eel.is/c++draft/time.parse#17.sentence-1

> If the from_stream overload fails to parse everything specified by
> the format string, or if insufficient information is parsed to specify a
> complete duration, time point, or calendrical data structure,
> setstate(ios_base::failbit) is called on the basic_istream.
@ecatmur ecatmur requested a review from a team as a code owner February 7, 2022 00:54
@ghost
Copy link

ghost commented Feb 7, 2022

CLA assistant check
All CLA requirements met.

@StephanTLavavej StephanTLavavej added bug Something isn't working chrono C++20 chrono labels Feb 7, 2022
@MattStephanson
Copy link
Contributor

MattStephanson commented Feb 8, 2022

@HowardHinnant (or any interested party), if you have time, could you comment on an underlying issue here?

MSVC STL follows libc++ and libstdc++ in setting eofbit in the following situation (godbolt):

#include <cassert>
#include <iomanip>
#include <sstream>

int main() {
    std::tm t;
    std::istringstream iss("2022");
    iss >> std::get_time(&t, "%Y");
    assert(iss.rdstate() == std::ios_base::eofbit);
    return 0;
}

Personally I find the Standard wording here a bit unclear ([locale.time.get.virtuals]/12: "When s == end evaluates to true after reading a character the function evaluates err |= ios_­base​::​eofbit."), but these three implementations agree. (Edit: note that an earlier part of p12 refers to strptime, which specifies that, by default, the "maximum number of characters scanned" for %Y is 4.)

MSVC STL carries over this behavior for std::chrono::parse, because [time] doesn't mention eofbit anywhere. In contrast, your Date library does not set eofbit after an equivalent parse (godbolt):

#include <cassert>
#include <date/date.h>
#include <sstream>

int main() {
    date::year y;
    std::istringstream iss("2022");
    iss >> date::parse("%Y", y);
    assert(iss.rdstate() == std::ios_base::goodbit);
    return 0;
}

Do you have any opinion about what the behavior here should be or if an LWG issue is needed? Thanks.

FWIW, I like Date's behavior, even though it's inconsistent with get_time, because it's more consistent with the philosophy (as I understand it) that eofbit isn't set until we actually try to read after reaching the end. All the numeric chrono parse fields have a known width, either default or user-specified, so it makes sense to me that it should act more like string s; istringstream{"abcd"} >> setw(4) >> s;, which doesn't set eofbit.

@HowardHinnant
Copy link

HowardHinnant commented Feb 8, 2022 via email

@StephanTLavavej
Copy link
Member

@MattStephanson @HowardHinnant Thanks for the info - that does seem worthy of an LWG issue, especially if we want microsoft/STL, libstdc++, and libc++ to all change to follow Date's behavior.

stl/inc/chrono Outdated Show resolved Hide resolved
ecatmur and others added 2 commits February 13, 2022 23:38
Co-authored-by: Matt Stephanson <68978048+MattStephanson@users.noreply.github.com>
Test for whitespace after other fields (explicit and shorthand) to
ensure that we don't return early on EOF.

Check exact rdstate flags to ensure that we set eofbit in addition to
failbit.
Copy link
Contributor

@MattStephanson MattStephanson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me! I have just one small style nitpick.

Copy link
Member

@StephanTLavavej StephanTLavavej left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this looks great! Just a couple of minor style questions/issues.

stl/inc/chrono Show resolved Hide resolved
Break out of loop after setting failbit | eofbit

Test against goodbit instead of 0
Copy link
Member

@barcharcraz barcharcraz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks correct to me.

@StephanTLavavej
Copy link
Member

I'm mirroring this to the MSVC-internal repo - please notify me if any further changes are pushed.

@StephanTLavavej StephanTLavavej merged commit c42ad5c into microsoft:main Mar 28, 2022
@StephanTLavavej
Copy link
Member

Thanks for fixing this bug in one of C++20's biggest features, and congratulations on your first microsoft/STL commit! 😻 🎉 🚀

This is expected to ship in VS 2022 17.3 Preview 1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working chrono C++20 chrono
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants