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

chronat: Add year and year_month_day formatting #1840

Merged
merged 7 commits into from
Apr 15, 2021

Conversation

eldakesh-ms
Copy link
Contributor

This one is a biggie. The main things changed are the way that
specifiers are handled and delegated.

The general idea behind formatting time is that you can take segments of
a type and format them individually. For example, you can take the year
out of year_month_day and do the exact same operations you can do with a
normal year. The _Is_type_valid function recursively checks if a
parent type can be formatted by its children. The other functions didn't
really need much more finessing, the tm structure already has all the
fields we need to hold all the time info (simultaniously) and the
formatters work off that.

The big change here is in moving some "basic" formatters into our own
function and not relying on get_time to format them. The main reason
is that get_time does not play with invalid ranges, at all. A day of
40 is always illegal, but we need to be able to format it, especially
in the face of operator <<. We could have kept what we had before, but
then it becomes a clear problem that we cannot use %F for a
year_month_day that has an invalid day, so I am seperating all the
integral formatters out into that function. Again, because of the nested
nature of times, we recurse in this function.

Note that function currently uses format_to in probably a very
inneficient way. I am all ears on how to improve that.

This one is a biggie. The main things changed are the way that
specifiers are handled and delegated.

The general idea behind formatting time is that you can take segments of
a type and format them individually. For example, you can take the year
out of year_month_day and do the exact same operations you can do with a
normal year. The `_Is_type_valid` function recursively checks if a
parent type can be formatted by its children. The other functions didn't
really need much more finessing, the `tm` structure already has all the
fields we need to hold all the time info (simultaniously) and the
formatters work off that.

The big change here is in moving some "basic" formatters into our own
function and not relying on `get_time` to format them. The main reason
is that `get_time` does not play with invalid ranges, at all. A day of
`40` is always illegal, but we need to be able to format it, especially
in the face of `operator <<`. We could have kept what we had before, but
then it becomes a clear problem that we cannot use `%F` for a
`year_month_day` that has an invalid day, so I am seperating all the
integral formatters out into that function. Again, because of the nested
nature of times, we recurse in this function.

Note that function currently uses `format_to` in probably a very
inneficient way. I am all ears on how to improve that.
@eldakesh-ms eldakesh-ms requested a review from a team as a code owner April 15, 2021 16:59
@mnatsuhara mnatsuhara self-assigned this Apr 15, 2021
@mnatsuhara mnatsuhara added the chrono C++20 chrono label Apr 15, 2021
Copy link
Contributor

@mnatsuhara mnatsuhara left a comment

Choose a reason for hiding this comment

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

This is looking great! I have a few questions about what specifiers are valid for year_month_day, but otherwise they're just nitpicks.

Comment on lines +5665 to +5671
case 'a':
case 'A':
case 'b':
case 'B':
case 'h':
case 'z':
case 'Z':
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you want to just populate this with all of the type specifiers that will end up here? Or were you planning to add to this as we added formatters/operator<<s for each type? I think we'll also need to include %c, %x, %X looking at Table 101.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I think we need to rename this function. It was conceived as a way to figure out specifiers can throw, so we can explicit about ok() checking. %c uses locale, but I don't think it can ever be out of bounds. Maybe _Type_needs_bounds_checking?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think %c will actually require more extensive bounds-checking because put_time [says that %c uses all fields of tm so there is even more opportunity for out-of-bounds issues?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I want to say that %c is used for a point in time, measured from some base. You can't have an invalid seconds, because you derive the seconds from the amount of time elapsed.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm trying to work out what type would be used to format %c and I think it would probably be time_points right? (e.g., sys_time, etc.). If we would just use time_point::time_since_epoch() then I think you're right.

Comment on lines +5611 to +5612
return _Type == 'D' || _Type == 'F' || _Is_valid_type<_CHRONO year>(_Type)
|| _Is_valid_type<_CHRONO month>(_Type) || _Is_valid_type<_CHRONO day>(_Type);
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering if the specifiers that are valid for weekday are also valid here since a specific year, month, and weekday would give us a particular weekday. Similarly, would 'U' and 'V' be valid specifiers since we could get a particular week from a year_month_day?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We do not have a weekday. I based which specifiers are legal from put_time, and since it isn't trivial to compute the weekday from just a year/month/day, I think it doesn't apply. I could be very wrong though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After discussing on discord, I agree it makes sense to be able to derive some more information from a year_month_day than I originally implemented.

Co-authored-by: mnatsuhara <46756417+mnatsuhara@users.noreply.github.com>
@CaseyCarter CaseyCarter changed the title <chronat>: Add year and year_month_day formatting chronat: Add year and year_month_day formatting Apr 15, 2021
Comment on lines 353 to 358
assert(format(STR("{}"), year_month_day{year{1900}, month{1}, day{1}}) == STR("1900-01-01"));
stream_helper(STR("1900-01-01"), year_month_day{year{1900}, month{1}, day{1}});
stream_helper(STR("1900-00-01 is not a valid date"), invalid);

assert(format(STR("{:%Y %b %d}"), year_month_day{year{1900}, month{1}, day{1}}) == STR("1900 Jan 01"));
assert(format(STR("{:%F %D}"), invalid) == STR("1900-00-01 00/01/00"));
Copy link
Member

Choose a reason for hiding this comment

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

An issue with using 1900-01-01 as an example is that it won't detect swapping the month and the day. I recommend using test data where the values are distinct (when possible).

Similarly, invalid printing 00/01/00 wouldn't detect egregious swapping.

@eldakesh-ms eldakesh-ms merged commit 0aeb5ea into microsoft:chronat2 Apr 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
chrono C++20 chrono
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants