Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions stl/inc/xloctime
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ protected:
break;

case 'c':
_First = _Getfmt(_First, _Last, _Iosbase, _State, _Pt, "%b %d %H : %M : %S %Y");
_First = _Getfmt(_First, _Last, _Iosbase, _State, _Pt, "%a %b %e %T %Y");
break;

case 'C':
Expand Down Expand Up @@ -547,7 +547,7 @@ protected:
break;

case 'x':
_First = _Getfmt(_First, _Last, _Iosbase, _State, _Pt, "%d / %m / %y");
_First = _Getfmt(_First, _Last, _Iosbase, _State, _Pt, "%m / %d / %y");
break;

case 'y':
Expand Down
35 changes: 31 additions & 4 deletions tests/std/tests/Dev11_0836436_get_time/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ using namespace std;
// DevDiv-836436 "<iomanip>: get_time()'s AM/PM parsing is broken"
// DevDiv-872926 "<locale>: time_get::get parsing format string gets tm::tm_hour wrong [libcxx]"
// VSO-1259138/GH-2618 "<xloctime>: get_time does not return correct year in tm.tm_year if year is 1"
// GH-6129 "<xloctime>: time_get::do_get uses the wrong format for %c and %x"

tm helper(const char* const s, const char* const fmt) {
tm t{};
Expand Down Expand Up @@ -60,9 +61,9 @@ tuple<int, int, int> read_date(const char* const s) {
const auto t = helper(s, "%x");

// %x The date, using the locale's date format.
// "%d / %m / %y"
// %d The day of the month [01,31]; leading zeros are permitted but not required.
// "%m / %d / %y"
// %m The month number [01,12]; leading zeros are permitted but not required.
// %d The day of the month [01,31]; leading zeros are permitted but not required.
// %y The year within century. When a century is not otherwise specified,
// values in the range [69,99] shall refer to years 1969 to 1999 inclusive, and
// values in the range [00,68] shall refer to years 2000 to 2068 inclusive;
Expand Down Expand Up @@ -112,6 +113,7 @@ void test_gh_2618();
void test_gh_2848();
void test_gh_4820();
void test_gh_4882();
void test_gh_6129();

int main() {
assert(read_hour("12 AM") == 0);
Expand Down Expand Up @@ -142,9 +144,9 @@ int main() {
assert(read_hour("11 PM") == 23);
assert(read_hour("11 pm") == 23);

assert(read_date("22 / 4 / 77") == make_tuple(22, /*NOTE DIFFERENCE:*/ 3, 77));
assert(read_date("04/22/77") == make_tuple(22, /*NOTE DIFFERENCE:*/ 3, 77));

assert(read_date("22 / 4 / 11") == make_tuple(22, /*NOTE DIFFERENCE:*/ 3, /*NOTE DIFFERENCE:*/ 111));
assert(read_date("04/22/11") == make_tuple(22, /*NOTE DIFFERENCE:*/ 3, /*NOTE DIFFERENCE:*/ 111));

assert(read_time("15 : 47 : 58") == make_tuple(15, 47, 58));
Comment on lines -145 to 149
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd also update the test to put the whitespace back in, to make sure that the parser accepts it. Sorry I neglected to mention it earlier. But otherwise LGTM, thanks!


Expand All @@ -160,6 +162,7 @@ int main() {
test_gh_2848();
test_gh_4820();
test_gh_4882();
test_gh_6129();
}

typedef istreambuf_iterator<char> Iter;
Expand Down Expand Up @@ -910,6 +913,30 @@ void test_gh_4820() {
}
}

void test_gh_6129() {
// GH-6129 "<xloctime>: time_get::do_get uses the wrong format for %c and %x"

// %c in the C locale uses "%a %b %e %T %Y"
{
const auto t = helper("Thu Jun 6 09:49:10 2009", "%c");
assert(t.tm_wday == 4);
assert(t.tm_mon == 5);
assert(t.tm_mday == 6);
assert(t.tm_hour == 9);
assert(t.tm_min == 49);
assert(t.tm_sec == 10);
assert(t.tm_year == 109);
}

// %x in the C locale uses "%m / %d / %y"
{
const auto t = helper("03/15/09", "%x");
assert(t.tm_mon == 2);
assert(t.tm_mday == 15);
assert(t.tm_year == 109);
}
}

void test_gh_4882() {
// GH-4882 <iomanip>: std::put_time should not crash on invalid/out-of-range tm struct values
const auto fieldValidation = [](int tm::* const field, const int value, const string& fmt) {
Expand Down
96 changes: 48 additions & 48 deletions tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -639,10 +639,10 @@ void parse_calendar_types_basic() {
// basic %D test
test_parse("07/04/76 17", "%D %C", ymd);
assert(ymd == July / 4d / 1776y);
// locale's date representation %x (== "%d / %m / %y")
test_parse("04/07/76 17", "%x %C", ymd);
// locale's date representation %x (== "%m / %d / %y")
test_parse("07/04/76 17", "%x %C", ymd);
assert(ymd == 4d / July / 1776y);
test_parse("10/12/15 18", "%x %C", ymd);
test_parse("12/10/15 18", "%x %C", ymd);
assert(ymd == 10d / December / 1815y);

// day-of-year tests, leap and non-leap years
Expand Down Expand Up @@ -972,27 +972,27 @@ void parse_timepoints() {
file_time<seconds> ft;
local_seconds lt;

test_parse("oct 29 19:01:42 2020", "%c", st);
test_parse("oct 29 19:01:42 2020", "%c", ut);
test_parse("oct 29 19:01:42 2020", "%c", ft);
test_parse("oct 29 19:01:42 2020", "%c", lt);
test_parse("thu oct 29 19:01:42 2020", "%c", st);
test_parse("thu oct 29 19:01:42 2020", "%c", ut);
test_parse("thu oct 29 19:01:42 2020", "%c", ft);
test_parse("thu oct 29 19:01:42 2020", "%c", lt);

assert(st == ref);
assert(ut == clock_cast<utc_clock>(ref));
assert(ft == clock_cast<file_clock>(ref));
assert(lt.time_since_epoch() == ref.time_since_epoch());

minutes offset;
test_parse("oct 29 19:01:42 2020 0430", "%c %z", st, nullptr, &offset);
test_parse("thu oct 29 19:01:42 2020 0430", "%c %z", st, nullptr, &offset);
assert(st == ref - offset && offset == 4h + 30min);

test_parse("oct 29 19:01:42 2020 0430", "%c %z", ut, nullptr, &offset);
test_parse("thu oct 29 19:01:42 2020 0430", "%c %z", ut, nullptr, &offset);
assert(ut == clock_cast<utc_clock>(ref) - offset && offset == 4h + 30min);

test_parse("oct 29 19:01:42 2020 0430", "%c %z", ft, nullptr, &offset);
test_parse("thu oct 29 19:01:42 2020 0430", "%c %z", ft, nullptr, &offset);
assert(ft == clock_cast<file_clock>(ref) - offset && offset == 4h + 30min);

test_parse("oct 29 19:01:42 2020 0430", "%c %z", lt, nullptr, &offset);
test_parse("thu oct 29 19:01:42 2020 0430", "%c %z", lt, nullptr, &offset);
assert(lt.time_since_epoch() == ref.time_since_epoch() && offset == 4h + 30min);

// N4878 [time.clock.tai]/1:
Expand All @@ -1004,13 +1004,13 @@ void parse_timepoints() {

ref = sys_days{1957y / December / 31d} + days{1} - 10s;
tai_seconds tt;
test_parse("jan 1 00:00:00 1958", "%c", tt);
test_parse("wed jan 1 00:00:00 1958", "%c", tt);
assert(tt == clock_cast<tai_clock>(ref));

ref = sys_days{2000y / January / 1d};
test_parse("jan 1 00:00:32 2000", "%c", tt);
test_parse("sat jan 1 00:00:32 2000", "%c", tt);
assert(tt == clock_cast<tai_clock>(ref));
test_parse("jan 1 00:00:32 2000 0430", "%c %z", tt, nullptr, &offset);
test_parse("sat jan 1 00:00:32 2000 0430", "%c %z", tt, nullptr, &offset);
assert(tt == clock_cast<tai_clock>(ref) - offset && offset == 4h + 30min);

// N4878 [time.clock.gps]/1:
Expand All @@ -1022,11 +1022,11 @@ void parse_timepoints() {

gps_seconds gt;
ref = sys_days{1980y / January / 6d};
test_parse("jan 6 00:00:00 1980 0430", "%c %z", gt, nullptr, &offset);
test_parse("sun jan 6 00:00:00 1980 0430", "%c %z", gt, nullptr, &offset);
assert(gt == clock_cast<gps_clock>(ref) - offset && offset == 4h + 30min);
test_parse("jan 6 00:00:00 1980", "%c", gt);
test_parse("sun jan 6 00:00:00 1980", "%c", gt);
assert(gt == clock_cast<gps_clock>(ref));
test_parse("jan 6 00:00:19 1980", "%c", tt);
test_parse("sun jan 6 00:00:19 1980", "%c", tt);
assert(gt == clock_cast<gps_clock>(tt));

seconds time;
Expand Down Expand Up @@ -1074,75 +1074,75 @@ void parse_timepoints() {
}

utc_seconds ut_ref = utc_clock::from_sys(sys_days{1d / July / 1972y}) - 1s; // leap second insertion
test_parse("june 30 23:59:60 1972", "%c", ut);
test_parse("fri june 30 23:59:60 1972", "%c", ut);
assert(ut == ut_ref);

// Test a later leap second, where the accumulated offset is greater than 1s.
ut_ref = utc_clock::from_sys(sys_days{1d / July / 1992y}) - 1s;
test_parse("june 30 23:59:60 1992", "%c", ut);
test_parse("tue june 30 23:59:60 1992", "%c", ut);
assert(ut == ut_ref);

// not leap-second aware
fail_parse("june 30 23:59:60 1972", "%c", st);
fail_parse("june 30 23:59:60 1972", "%c", tt);
fail_parse("june 30 23:59:60 1972", "%c", gt);
fail_parse("june 30 23:59:60 1972", "%c", ft);
fail_parse("fri june 30 23:59:60 1972", "%c", st);
fail_parse("fri june 30 23:59:60 1972", "%c", tt);
fail_parse("fri june 30 23:59:60 1972", "%c", gt);
fail_parse("fri june 30 23:59:60 1972", "%c", ft);

fail_parse("june 30 23:59:60 1973", "%c", ut); // not a leap second insertion
fail_parse("sat june 30 23:59:60 1973", "%c", ut); // not a leap second insertion

// the last leap second insertion that file_clock is not aware of
test_parse("dec 31 23:59:59 2016", "%c", ut);
test_parse("dec 31 23:59:59 2016", "%c", ft);
test_parse("sat dec 31 23:59:59 2016", "%c", ut);
test_parse("sat dec 31 23:59:59 2016", "%c", ft);
assert(ft == clock_cast<file_clock>(ut));

test_parse("dec 31 23:59:60 2016", "%c", ut);
fail_parse("dec 31 23:59:60 2016", "%c", ft);
test_parse("sat dec 31 23:59:60 2016", "%c", ut);
fail_parse("sat dec 31 23:59:60 2016", "%c", ft);

test_parse("jan 01 00:00:00 2017", "%c", ut);
test_parse("jan 01 00:00:00 2017", "%c", ft);
test_parse("sun jan 01 00:00:00 2017", "%c", ut);
test_parse("sun jan 01 00:00:00 2017", "%c", ft);
assert(ft == clock_cast<file_clock>(ut));

ref = sys_days{1d / January / 2020y} - 1s; // negative leap second, UTC time doesn't exist
fail_parse("dec 31 23:59:59 2019", "%c", ut);
fail_parse("dec 31 23:59:59 2019", "%c", ft);
fail_parse("tue dec 31 23:59:59 2019", "%c", ut);
fail_parse("tue dec 31 23:59:59 2019", "%c", ft);

test_parse("dec 31 23:59:59 2019", "%c", st);
test_parse("tue dec 31 23:59:59 2019", "%c", st);
assert(st == ref);

test_parse("dec 31 23:59:59 2019", "%c", lt); // Not UTC, might be valid depending on the time zone.
test_parse("tue dec 31 23:59:59 2019", "%c", lt); // Not UTC, might be valid depending on the time zone.
assert(lt.time_since_epoch() == ref.time_since_epoch());

// Initially, TAI - UTC == 37s.
test_parse("dec 31 23:59:59 2019", "%c", tt);
test_parse("dec 31 23:59:22 2019", "%c", ut);
test_parse("tue dec 31 23:59:59 2019", "%c", tt);
test_parse("tue dec 31 23:59:22 2019", "%c", ut);
assert(tt == clock_cast<tai_clock>(ut));

test_parse("jan 01 00:00:35 2020", "%c", tt);
test_parse("dec 31 23:59:58 2019", "%c", ut);
test_parse("wed jan 01 00:00:35 2020", "%c", tt);
test_parse("tue dec 31 23:59:58 2019", "%c", ut);
assert(tt == clock_cast<tai_clock>(ut));

test_parse("jan 01 00:00:36 2020", "%c", tt);
test_parse("jan 01 00:00:00 2020", "%c", ut);
test_parse("wed jan 01 00:00:36 2020", "%c", tt);
test_parse("wed jan 01 00:00:00 2020", "%c", ut);
assert(tt == clock_cast<tai_clock>(ut));

// Initially, GPS - UTC == 18s
test_parse("dec 31 23:59:59 2019", "%c", gt);
test_parse("dec 31 23:59:41 2019", "%c", ut);
test_parse("tue dec 31 23:59:59 2019", "%c", gt);
test_parse("tue dec 31 23:59:41 2019", "%c", ut);
assert(gt == clock_cast<gps_clock>(ut));

test_parse("jan 01 00:00:16 2020", "%c", gt);
test_parse("dec 31 23:59:58 2019", "%c", ut);
test_parse("wed jan 01 00:00:16 2020", "%c", gt);
test_parse("tue dec 31 23:59:58 2019", "%c", ut);
assert(gt == clock_cast<gps_clock>(ut));

test_parse("jan 01 00:00:17 2020", "%c", gt);
test_parse("jan 01 00:00:00 2020", "%c", ut);
test_parse("wed jan 01 00:00:17 2020", "%c", gt);
test_parse("wed jan 01 00:00:00 2020", "%c", ut);
assert(gt == clock_cast<gps_clock>(ut));


ut_ref = utc_clock::from_sys(sys_days{1d / January / 2022y}) - 1s; // leap second insertion
test_parse("dec 31 23:59:60 2021", "%c", ut);
test_parse("fri dec 31 23:59:60 2021", "%c", ut);
assert(ut == ut_ref);
test_parse("dec 31 23:59:60 2021", "%c", ft);
test_parse("fri dec 31 23:59:60 2021", "%c", ft);
assert(ft == clock_cast<file_clock>(ut_ref));


Expand Down