mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Align native datetime conversion with Python 3.15 (#9133)
Prefer index conversion for timestamps, accept float-convertible values, validate ISO timezone components, and apply final local fold detection consistently. Implementation, source comparison, and bounded validation were assisted by OpenAI Codex. Assisted-by: OpenAI Codex:model version unavailable
This commit is contained in:
1 parent
4d54126c1f
commit
9c832ee26c
1 file changed
+49
-41
@@ -1527,7 +1527,8 @@ mod _datetime {
|
||||
vals: &mut [i32; 4],
|
||||
tzoffset: &mut i32,
|
||||
tzmicrosecond: &mut i32,
|
||||
) -> i32 {
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<i32> {
|
||||
let p_end = start + len;
|
||||
let mut tzinfo_pos = start;
|
||||
loop {
|
||||
@@ -1542,18 +1543,18 @@ mod _datetime {
|
||||
}
|
||||
let rv = parse_hh_mm_ss_ff(s, start, tzinfo_pos, vals);
|
||||
if rv < 0 {
|
||||
return rv;
|
||||
return Ok(rv);
|
||||
} else if tzinfo_pos == p_end {
|
||||
return if rv == 1 { -5 } else { 0 };
|
||||
return Ok(if rv == 1 { -5 } else { 0 });
|
||||
}
|
||||
if byte_at(s, tzinfo_pos) == b'Z' {
|
||||
*tzoffset = 0;
|
||||
*tzmicrosecond = 0;
|
||||
return if byte_at(s, tzinfo_pos + 1) != 0 {
|
||||
return Ok(if byte_at(s, tzinfo_pos + 1) != 0 {
|
||||
-5
|
||||
} else {
|
||||
1
|
||||
};
|
||||
});
|
||||
}
|
||||
let tzsign = if byte_at(s, tzinfo_pos) == b'-' {
|
||||
-1
|
||||
@@ -1562,9 +1563,10 @@ mod _datetime {
|
||||
};
|
||||
let mut tz = [0; 4];
|
||||
let rv = parse_hh_mm_ss_ff(s, tzinfo_pos + 1, p_end, &mut tz);
|
||||
check_time_args(tz[0], tz[1], tz[2], tz[3], 0, vm)?;
|
||||
*tzoffset = tzsign * (tz[0] * 3600 + tz[1] * 60 + tz[2]);
|
||||
*tzmicrosecond = tzsign * tz[3];
|
||||
if rv != 0 { -5 } else { 1 }
|
||||
Ok(if rv != 0 { -5 } else { 1 })
|
||||
}
|
||||
|
||||
fn tzinfo_from_isoformat_results(
|
||||
@@ -1748,18 +1750,15 @@ mod _datetime {
|
||||
|
||||
/// `_PyTime_ObjectToTime_t` with floor rounding.
|
||||
fn object_to_time_t(obj: &PyObject, vm: &VirtualMachine) -> PyResult<i64> {
|
||||
if let Some(float) = obj.downcast_ref::<PyFloat>() {
|
||||
let d = float.to_f64();
|
||||
if d.is_nan() {
|
||||
return Err(vm.new_value_error("Invalid value NaN (not a number)"));
|
||||
}
|
||||
let d = d.floor();
|
||||
if !(-9.223_372_036_854_776e18..9.223_372_036_854_776e18).contains(&d) {
|
||||
return Err(time_t_overflow(vm));
|
||||
}
|
||||
return Ok(d as i64);
|
||||
if obj.number().is_index() {
|
||||
return long_to_time_t(obj, vm);
|
||||
}
|
||||
long_to_time_t(obj, vm)
|
||||
let d = timestamp_as_double(obj, vm)?;
|
||||
let d = d.floor();
|
||||
if !(-9.223_372_036_854_776e18..9.223_372_036_854_776e18).contains(&d) {
|
||||
return Err(time_t_overflow(vm));
|
||||
}
|
||||
Ok(d as i64)
|
||||
}
|
||||
|
||||
fn time_t_overflow(vm: &VirtualMachine) -> crate::vm::builtins::PyBaseExceptionRef {
|
||||
@@ -1772,29 +1771,37 @@ mod _datetime {
|
||||
int.as_bigint().to_i64().ok_or_else(|| time_t_overflow(vm))
|
||||
}
|
||||
|
||||
fn timestamp_as_double(obj: &PyObject, vm: &VirtualMachine) -> PyResult<f64> {
|
||||
let d = match obj.downcast_ref::<PyFloat>() {
|
||||
Some(float) => float.to_f64(),
|
||||
None => obj.try_float(vm)?.to_f64(),
|
||||
};
|
||||
if d.is_nan() {
|
||||
return Err(vm.new_value_error("Invalid value NaN (not a number)"));
|
||||
}
|
||||
Ok(d)
|
||||
}
|
||||
|
||||
/// `_PyTime_ObjectToTimeval` with half-even rounding: whole seconds and microseconds.
|
||||
fn object_to_timeval(obj: &PyObject, vm: &VirtualMachine) -> PyResult<(i64, i32)> {
|
||||
if let Some(float) = obj.downcast_ref::<PyFloat>() {
|
||||
let d = float.to_f64();
|
||||
if d.is_nan() {
|
||||
return Err(vm.new_value_error("Invalid value NaN (not a number)"));
|
||||
}
|
||||
let mut intpart = d.trunc();
|
||||
let mut floatpart = (d - intpart) * 1e6;
|
||||
floatpart = round_half_even(floatpart);
|
||||
if floatpart >= 1e6 {
|
||||
floatpart -= 1e6;
|
||||
intpart += 1.0;
|
||||
} else if floatpart < 0.0 {
|
||||
floatpart += 1e6;
|
||||
intpart -= 1.0;
|
||||
}
|
||||
if !(-9.223_372_036_854_776e18..9.223_372_036_854_776e18).contains(&intpart) {
|
||||
return Err(time_t_overflow(vm));
|
||||
}
|
||||
return Ok((intpart as i64, floatpart as i32));
|
||||
if obj.number().is_index() {
|
||||
return Ok((long_to_time_t(obj, vm)?, 0));
|
||||
}
|
||||
Ok((long_to_time_t(obj, vm)?, 0))
|
||||
let d = timestamp_as_double(obj, vm)?;
|
||||
let mut intpart = d.trunc();
|
||||
let mut floatpart = (d - intpart) * 1e6;
|
||||
floatpart = round_half_even(floatpart);
|
||||
if floatpart >= 1e6 {
|
||||
floatpart -= 1e6;
|
||||
intpart += 1.0;
|
||||
} else if floatpart < 0.0 {
|
||||
floatpart += 1e6;
|
||||
intpart -= 1.0;
|
||||
}
|
||||
if !(-9.223_372_036_854_776e18..9.223_372_036_854_776e18).contains(&intpart) {
|
||||
return Err(time_t_overflow(vm));
|
||||
}
|
||||
Ok((intpart as i64, floatpart as i32))
|
||||
}
|
||||
|
||||
fn round_half_even(x: f64) -> f64 {
|
||||
@@ -3024,7 +3031,8 @@ mod _datetime {
|
||||
let len = bytes.len() - start;
|
||||
let mut vals = [0; 4];
|
||||
let (mut tzoffset, mut tzusec) = (0, 0);
|
||||
let rv = parse_isoformat_time(bytes, start, len, &mut vals, &mut tzoffset, &mut tzusec);
|
||||
let rv =
|
||||
parse_isoformat_time(bytes, start, len, &mut vals, &mut tzoffset, &mut tzusec, vm)?;
|
||||
if rv < 0 {
|
||||
return Err(invalid_isoformat(&tstr, vm)?);
|
||||
}
|
||||
@@ -3492,8 +3500,7 @@ mod _datetime {
|
||||
let tm = platform_time(timet, local_tm, vm)?;
|
||||
let second = tm.second.min(59);
|
||||
let mut fold = 0;
|
||||
let skip_probe = cfg!(windows) && timet - MAX_FOLD_SECONDS <= 0;
|
||||
if tzinfo.is_none() && local_tm && !skip_probe {
|
||||
if tzinfo.is_none() && local_tm {
|
||||
let result_seconds =
|
||||
utc_to_seconds(tm.year, tm.month, tm.day, tm.hour, tm.minute, second, vm)?;
|
||||
let probe_seconds = local(EPOCH_SECONDS + timet - MAX_FOLD_SECONDS, vm)?;
|
||||
@@ -3896,7 +3903,8 @@ mod _datetime {
|
||||
}
|
||||
};
|
||||
len = len.saturating_sub(p);
|
||||
rv = parse_isoformat_time(bytes, p, len, &mut vals, &mut tzoffset, &mut tzusec);
|
||||
rv =
|
||||
parse_isoformat_time(bytes, p, len, &mut vals, &mut tzoffset, &mut tzusec, vm)?;
|
||||
}
|
||||
if rv < 0 {
|
||||
return Err(invalid_isoformat(&dtstr, vm)?);
|
||||
|
||||
Reference in new issue
Block a user