Fix negative subsec for time_point - #3261
Conversation
| val -= std::chrono::seconds(1); | ||
| } | ||
|
|
||
|
|
| EXPECT_EQ("59.000", fmt::format("{:%S}", epoch - 4 * d)); | ||
| EXPECT_EQ("59.250", fmt::format("{:%S}", epoch - 3 * d)); | ||
| EXPECT_EQ("59.500", fmt::format("{:%S}", epoch - 2 * d)); | ||
| EXPECT_EQ("59.750", fmt::format("{:%S}", epoch - 1 * d)); |
There was a problem hiding this comment.
I'm not sure if we need all four of these checks because they are effectively testing the same thing. One is probably enough.
| EXPECT_EQ("00.250", fmt::format("{:%S}", epoch + 1 * d)); | ||
| EXPECT_EQ("00.500", fmt::format("{:%S}", epoch + 2 * d)); | ||
| EXPECT_EQ("00.750", fmt::format("{:%S}", epoch + 3 * d)); | ||
| EXPECT_EQ("01.000", fmt::format("{:%S}", epoch + 4 * d)); |
vitaut
left a comment
There was a problem hiding this comment.
Thanks for the PR! Overall looks good, just some minor comments inline.
|
Thank you! |
|
This change triggers a ubsan error on the following case: #include <fmt/chrono.h>
int main() {
auto d = std::chrono::system_clock::duration(-9223372036854767584);
fmt::print("{:}", std::chrono::system_clock::time_point(d));
}Stack trace: |
|
I attempted to resolve this ub by first converting the #include <fmt/chrono.h>
int main() {
auto val = -9223372036854767584 / 1'000'000 - 1; // -9223372036855
auto tp =
std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>(
std::chrono::seconds(val));
auto time = std::chrono::system_clock::to_time_t(tp);
fmt::print("{} {}", val, time);
// expected: -9223372036855 -9223372036855
// actual: -9223372036855 9223372036854
}The message from the sanitizer is as follows: I don't know why the library would multiply the seconds by 1,000,000 and then convert it to |
|
An alternative solution is to subtract 1 from the resulting #include <fmt/chrono.h>
int main() {
auto val = -9223372036854; // -9223372036854767584 / 1'000'000
auto tp =
std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>(
std::chrono::seconds(val));
// This results in integer overflow since to_time_t does some multiplications.
auto t1 = std::chrono::system_clock::to_time_t(tp - std::chrono::seconds(1));
// This assumes time_t to represent seconds and is signed, which is not
// specified by the standard, but true for almost all platforms.
auto t2 = std::chrono::system_clock::to_time_t(tp) - 1;
fmt::print("t1: {}, t2: {}\n", t1, t2);
// t1: 9223372036854, t2: -9223372036855
} |
|
I turned the UB into an error in 240b728. If you have a better idea, a PR would be welcome. |
Fix #3117
The following piece of code (https://godbolt.org/z/4qr5389ns) used to print
59.000 00.750 00.500 00.250 00.000 00.250 00.500 00.750 01.000, which is wrong. After this PR, it now correctly prints59.000 59.250 59.500 59.750 00.000 00.250 00.500 00.750 01.000