update test_time and implement more time module#7073
Conversation
📝 WalkthroughWalkthroughThe changes extend the Unix timestamp and time structure handling in the time module with platform-specific code paths and new helper functions. Function signatures for gmtime, localtime, and ctime now accept optional nullable time values. StructTimeData gains new constructors (new_inner, new_utc, new_local) for platform-specific instantiation, and strftime/asctime logic is refactored to branch on Unix versus non-Unix implementations. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Sorry, something went wrong.
933912a to
f1e686e
Compare
February 11, 2026 04:21
- Replace chrono-based gmtime/localtime/mktime/strftime/asctime/ctime with direct libc calls (gmtime_r, localtime_r, mktime, strftime) on unix, keeping chrono as fallback for non-unix platforms - Add checked_tm_from_struct_time for struct_time field validation - Accept None in gmtime/localtime/ctime (OptionalArg<Option<>>) - Fix StructTimeData field order: tm_zone before tm_gmtoff - Fix PyStructTime slot_new to accept optional dict argument
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@crates/vm/src/stdlib/time.rs`:
- Line 580: The error message uses the wrong function name: update the call to
checked_tm_from_struct_time in the ctime implementation so the third argument is
"ctime" (not "asctime"), ensuring checked_tm_from_struct_time(...) produces
correct error text for the ctime function; look for the call site in the ctime
function and replace the literal function-name argument accordingly.
- Around line 670-675: The current match on t.naive_or_local(vm) in the strftime
path swallows struct_time conversion errors and returns the raw format string
(using fmt_lossy), which hides user errors; instead propagate the conversion
error so callers get a proper exception like CPython does. Replace the Err(_) =>
return Ok(vm.ctx.new_str(...)) branch in the t.naive_or_local(vm) match with
propagation of the underlying error (or create and return an appropriate VM
error/Value) so that invalid struct_time inputs to the strftime implementation
raise rather than silently fall back.
- Around line 515-520: The non-Unix branch for localtime currently passes a
hardcoded 0 for tm_isdst (see secs.naive_or_local and
StructTimeData::new_local), which incorrectly claims DST is off; change this to
derive the DST flag from the chrono datetime if possible (compute whether the
local datetime is in DST and pass that value into StructTimeData::new_local as
tm_isdst) or, if accurate DST detection is not feasible, pass -1 to indicate
unknown DST instead of 0 so non-Unix platforms don't falsely report "not DST".
- Around line 836-841: new_local currently calls
chrono::Local.from_local_datetime(&tm).unwrap(), which can panic for DST-gap
times; change it to handle the MappedLocalTime result like the non-Unix mktime
path by using .single().ok_or_else(...) (or otherwise map the none case to a
PyErr) and propagate the error (or change new_local to return PyResult<Self>) so
you don't unwrap; update the callsite or signature accordingly and then pass the
resultant offset_seconds and tz_abbr into Self::new_inner as before.
🧹 Nitpick comments (2)
crates/vm/src/stdlib/time.rs (2)
947-964:i64→f64round-trip inpyobj_to_time_tcan lose precision.For the
Either::B(int)branch, the value is cast tof64(line 958), range-checked, then cast totime_t(line 963). On 64-bit platforms wheretime_tisi64, thef64round-trip silently loses precision for values with magnitude > 2^53. Consider handling the integer path directly:Proposed fix
pub(super) fn pyobj_to_time_t( value: Either<f64, i64>, vm: &VirtualMachine, ) -> PyResult<time_t> { - let secs = match value { + match value { Either::A(float) => { if !float.is_finite() { return Err(vm.new_value_error("Invalid value for timestamp")); } - float.floor() + let secs = float.floor(); + if secs < time_t::MIN as f64 || secs > time_t::MAX as f64 { + return Err( + vm.new_overflow_error("timestamp out of range for platform time_t"), + ); + } + Ok(secs as time_t) } - Either::B(int) => int as f64, - }; - if secs < time_t::MIN as f64 || secs > time_t::MAX as f64 { - return Err(vm.new_overflow_error("timestamp out of range for platform time_t")); + Either::B(int) => { + let ts: time_t = int + .try_into() + .map_err(|_| vm.new_overflow_error("timestamp out of range for platform time_t"))?; + Ok(ts) + } } - Ok(secs as time_t) }
1019-1078: Duplicatedlibc::tmconstruction betweenunix_mktimeandchecked_tm_from_struct_time.Both functions extract the same fields from
StructTimeDatainto alibc::tmwith nearly identical code (clone + try_into_value + error mapping). Consider extracting a shared helper that builds the baselibc::tm, then let each caller apply its own adjustments (validation, sentinel values, zone handling). As per coding guidelines: "When branches differ only in a value but share common logic, extract the differing value first, then call the common logic once to avoid duplicate code."
Sorry, something went wrong.
📦 Library DependenciesThe following Lib/ modules were modified. Here are their dependencies: [ ] lib: cpython/Lib/test/support dependencies:
dependent tests: (no tests depend on support) [ ] test: cpython/Lib/test/test_time.py (TODO: 24) dependencies: dependent tests: (133 tests)
Legend:
|
Sorry, something went wrong.
bc35aa8
into
RustPython:main
Feb 11, 2026
update test_time and implement more time module
Summary by CodeRabbit
Bug Fixes
Refactor