-
Notifications
You must be signed in to change notification settings - Fork 1
fix: parsing end dates on systems with 32-bit time_t #24
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,22 @@ | |
|
|
||
| namespace eppoclient { | ||
|
|
||
| std::chrono::system_clock::time_point parseISOTimestamp(const std::string& timestamp) { | ||
| std::chrono::system_clock::time_point parseISOTimestampForConfigEndTime( | ||
| const std::string& timestamp) { | ||
| // Check for the sentinel value that represents "no end date" | ||
| if (timestamp == "9999-12-31T00:00:00.000Z") { | ||
| return std::chrono::system_clock::time_point::max(); | ||
| } | ||
| return parseISOTimestamp(timestamp, std::chrono::system_clock::time_point::max()); | ||
|
Comment on lines
+10
to
+14
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this still feels arbitrary, I'll echo the concern @dd-oleksii had. What if someone sets (explicitly) that an assignment ends in the year 5000? Or, more realistically, 2039. I think it might be better to make sure The platform I've tested on that had the problem was Windows 11 x64:
Also, I have another idea:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hah, I found this:
I expected the 64 bit version would have astronomical upper time limit, because why not, but 9999 really is out of range on Windows 🙃
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, this is a better approach. I'll update! |
||
| } | ||
|
|
||
| std::chrono::system_clock::time_point parseISOTimestampForConfigStartTime( | ||
| const std::string& timestamp) { | ||
| return parseISOTimestamp(timestamp, std::chrono::system_clock::time_point()); | ||
| } | ||
|
|
||
| std::chrono::system_clock::time_point parseISOTimestamp( | ||
| const std::string& timestamp, std::chrono::system_clock::time_point errorValue) { | ||
| std::tm tm = {}; | ||
| char dot = '\0'; | ||
| int milliseconds = 0; | ||
|
|
@@ -52,16 +67,33 @@ std::chrono::system_clock::time_point parseISOTimestamp(const std::string& times | |
| #endif | ||
|
|
||
| if (time_c == -1) { | ||
| return std::chrono::system_clock::time_point(); | ||
| // timegm failed - could be out of range for time_t | ||
| // Return the supplied error value | ||
| return errorValue; | ||
| } | ||
| auto tp = std::chrono::system_clock::from_time_t(time_c); | ||
| tp += std::chrono::milliseconds(milliseconds); | ||
| return tp; | ||
| } | ||
|
|
||
| std::string formatISOTimestampForConfigEndTime(const std::chrono::system_clock::time_point& tp) { | ||
| // Special handling for max time_point (year 9999 dates) | ||
| if (tp == std::chrono::system_clock::time_point::max()) { | ||
| return "9999-12-31T00:00:00.000Z"; | ||
| } | ||
|
Comment on lines
+80
to
+83
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Q: does this date stay on the client, or goes back to the backend (for consumption by potentially other clients with different date parsing capabilities)? If it stays on the client, we could just let
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does stay on the client. That's a good idea. |
||
| return formatISOTimestamp(tp); | ||
| } | ||
|
|
||
| std::string formatISOTimestamp(const std::chrono::system_clock::time_point& tp) { | ||
| auto tt = std::chrono::system_clock::to_time_t(tp); | ||
| std::tm tm = *std::gmtime(&tt); | ||
| std::tm* tm_ptr = std::gmtime(&tt); | ||
|
|
||
| // Handle gmtime failure (can happen on 32-bit systems with out-of-range dates) | ||
| if (!tm_ptr) { | ||
| return "1970-01-01T00:00:00.000Z"; // Return epoch as fallback | ||
| } | ||
|
Comment on lines
+91
to
+94
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Epoch is different on Posix vs Windows. It could be fine to use the Posix epoch since it's unlikely that clients will need to represents dates outside of it, but in general maybe it'd be better to not return any string if Otherwise, with out-of-range dates, was the caller intending to represent a date in the far future, or far past? This formatting function is used to set "updatedAt" in BanditConfiguration. Setting it as epoch means this will always be the "oldest" bandit config possible, is this correct? |
||
|
|
||
| std::tm tm = *tm_ptr; | ||
|
|
||
| // Add milliseconds | ||
| auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch()) % 1000; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this represents my case, this might be a windows vs linux problem rather than 32 vs 64. I've had this problem with x64 Windows that has 64-bit time_t - the problem is caused by the use
_mkgmtime. Would be good to test on windows too, but I understand it could be a logistical problem