datetime.rs
Overview
The [datetime.rs](/projects/287/67764) file provides low-level Rust abstractions and serialization utilities for handling Python datetime objects within a Rust-based serialization framework. It primarily deals with the representation and serialization of Python `date`, `time`, and `datetime` objects, interfacing directly with Python's C API through FFI (Foreign Function Interface).
This file implements:
Safe wrappers around raw Python datetime pointers.
Methods to extract datetime components such as year, month, day, hour, minute, second, and microsecond.
Custom serialization logic compatible with Serde for efficient serialization of datetime objects.
Handling of time zones and offsets, including support for common timezone libraries (e.g., pytz, pendulum, dateutil).
Utility macros to format datetime components as zero-padded strings.
The main goal is to enable efficient and correct serialization of Python datetime objects into string representations while respecting options such as omission of microseconds and correct timezone offset retrieval.
Detailed Explanation of Components
Macros
write_double_digit!
macro_rules! write_double_digit {
($buf:ident, $value:ident) => {
if $value < 10 {
$buf.put_u8(b'0');
}
$buf.put_slice(itoa::Buffer::new().format($value).as_bytes());
};
}
Purpose: Writes a two-digit zero-padded number (e.g.,
03,12) into a buffer.Parameters:
$buf: A mutable buffer implementingbytes::BufMutwhere bytes will be written.$value: The numeric value to write (expected to be less than 100).
Behavior: If the value is less than 10, prepends a '0' character before the value.
**Usage example:**
write_double_digit!(buf, 7); // writes "07"
write_double_digit!(buf, 23); // writes "23"
write_microsecond!
macro_rules! write_microsecond {
($buf:ident, $microsecond:ident) => {
if $microsecond != 0 {
let mut buf = itoa::Buffer::new();
let formatted = buf.format($microsecond);
$buf.put_slice(&[b'.', b'0', b'0', b'0', b'0', b'0', b'0'][..(7 - formatted.len())]);
$buf.put_slice(formatted.as_bytes());
}
};
}
Purpose: Writes the fractional seconds part (microseconds) in
.ffffffformat, zero-padded to 6 digits.Parameters:
$buf: The buffer to write to.$microsecond: The microsecond component (0 to 999,999).
Behavior: Writes a '.' followed by a zero-padded microsecond value only if microsecond is non-zero.
**Example:**
write_microsecond!(buf, 123456); // writes ".123456"
write_microsecond!(buf, 0); // writes nothing
Structs and Enums
Date
#[repr(transparent)]
pub(crate) struct Date {
ptr: *mut pyo3_ffi::PyObject,
}
Description: A wrapper around a raw pointer to a Python
dateobject.Main responsibilities:
Extract year, month, and day fields from the Python object.
Serialize into ISO 8601 date string (
YYYY-MM-DD).
**Key methods:**
new(ptr: *mut pyo3_ffi::PyObject) -> SelfCreates a new
Dateinstance from a raw Python object pointer.
write_buf<B>(&self, buf: &mut B)whereB: bytes::BufMutWrites the date string representation to the provided buffer.
Pads year with leading zeros if less than 1000 to ensure 4 digits.
Implements
Serializetrait to serialize date strings using Serde.
**Usage example:**
let date = Date::new(py_date_ptr);
let mut buf = SmallFixedBuffer::new();
date.write_buf(&mut buf);
println!("{}", std::str::from_utf8(buf.as_slice()).unwrap()); // e.g. "2024-04-27"
TimeError
pub(crate) enum TimeError {
HasTimezone,
}
Description: Error type returned when attempting to serialize a
timeobject that has timezone information (which the serializer does not support).
Time
pub(crate) struct Time {
ptr: *mut pyo3_ffi::PyObject,
opts: Opt,
}
Description: Wrapper around a Python
timeobject pointer with serialization options.Responsibilities:
Serialize a Python
timeobject (HH:MM:SS[.ffffff]) to string.Checks and errors if the time has timezone information.
Honors option to omit microseconds.
**Key methods:**
new(ptr: *mut pyo3_ffi::PyObject, opts: Opt) -> Selfwrite_buf<B>(&self, buf: &mut B) -> Result<(), TimeError>:Writes the time string to the buffer.
Returns
TimeError::HasTimezoneif time has tzinfo.
Implements
Serializetrait, returns a serialization error if time has timezone.
**Usage example:**
let time = Time::new(py_time_ptr, opts);
let mut buf = SmallFixedBuffer::new();
time.write_buf(&mut buf)?; // May error if tzinfo present
println!("{}", std::str::from_utf8(buf.as_slice()).unwrap()); // e.g. "14:30:00.123456"
DateTime
pub(crate) struct DateTime {
ptr: *mut pyo3_ffi::PyObject,
opts: Opt,
}
Description: Wrapper around a Python
datetimeobject pointer with serialization options.Responsibilities:
Provide accessors for datetime components.
Handle timezone-aware offsets using several timezone libraries’ conventions.
Serialize datetime in ISO 8601 format including timezone offset.
**Key traits and methods:**
Implements the
DateTimeLiketrait, providing:Accessor methods for year, month, day, hour, minute, second, microsecond.
nanosecond()returns microseconds × 1000.has_tz()checks if datetime has timezone info.offset()returns the timezone offset, optimized for zoneinfo or falls back to a slower method that supports other timezone libraries (pendulum, pytz, dateutil).
new(ptr: *mut pyo3_ffi::PyObject, opts: Opt) -> SelfImplements
Serializetrait that serializes the datetime object to a string representation.
**Important Implementation Details:**
Uses unsafe FFI calls to Python C API to get datetime fields.
slow_offset()method handles complex timezone offset retrieval for third-party timezone libraries by checking for presence of methods like.convert(),.normalize(),.dst(), and.utcoffset().Efficiently writes zero-padded datetime components to buffer.
Trait Implementations
DateTimeLike (for DateTime)
This trait defines an interface for extracting datetime-like fields and offset information.
Accessors (
year(),month(),day(),hour(),minute(),second(),microsecond()) use macros to wrap unsafe FFI calls.offset()attempts to retrieve the timezone offset, optimized forzoneinfotype or fallback to slower method.slow_offset()implements complex logic to support multiple timezone libraries by inspecting Python object attributes and calling relevant methods.
Interaction with Other System Components
Uses
pyo3_ffifor FFI bindings to Python C API.Depends on
crate::opt::Optfor serialization options such asOMIT_MICROSECONDS.Uses
crate::serialize::buffer::SmallFixedBufferfor efficient fixed-size buffer serialization.Error handling via
crate::serialize::error::SerializeError.Implements
serde::ser::Serializefor integration with Serde serialization framework.Uses
crate::serialize::per_type::datetimelike::{DateTimeLike, Offset, DateTimeError}trait and types for datetime abstraction.Relies on string constants like
CONVERT_METHOD_STR,UTCOFFSET_METHOD_STR, etc., fromcrate::typerefto interact with Python timezone objects.The file is likely part of a larger Python serialization/deserialization crate that handles multiple Python types and converts them to Rust/JSON representations.
Example Usage
Assuming a Python `datetime` object pointer `py_dt_ptr` and serialization options `opts`:
use datetime::DateTime;
use serde_json::to_string;
let dt = DateTime::new(py_dt_ptr, opts);
// Serialize datetime object to JSON string (as ISO 8601 string)
let json_str = to_string(&dt).expect("Serialization failed");
println!("Serialized datetime: {}", json_str);
Visual Diagram
classDiagram
class Date {
-ptr: *mut PyObject
+new(ptr: *mut PyObject) Date
+write_buf<B: BufMut>(&mut buf)
+serialize<S: Serializer>(serializer: S) -> Result
}
class Time {
-ptr: *mut PyObject
-opts: Opt
+new(ptr: *mut PyObject, opts: Opt) Time
+write_buf<B: BufMut>(&mut buf) -> Result<(), TimeError>
+serialize<S: Serializer>(serializer: S) -> Result
}
class DateTime {
-ptr: *mut PyObject
-opts: Opt
+new(ptr: *mut PyObject, opts: Opt) DateTime
+year() -> i32
+month() -> u8
+day() -> u8
+hour() -> u8
+minute() -> u8
+second() -> u8
+microsecond() -> u32
+nanosecond() -> u32
+has_tz() -> bool
+offset() -> Result<Offset, DateTimeError>
+slow_offset() -> Result<Offset, DateTimeError>
+serialize<S: Serializer>(serializer: S) -> Result
}
DateTime ..|> DateTimeLike
Date ..|> Serialize
Time ..|> Serialize
DateTime ..|> Serialize
Summary
The [datetime.rs](/projects/287/67764) file is a critical module for serializing Python datetime-related objects in Rust, providing:
Safe abstraction over Python datetime pointers.
Efficient string serialization with zero-padded formatting.
Support for timezone-aware datetime objects across various timezone libraries.
Integration with Serde for seamless serialization workflows.
Its design blends low-level FFI interactions with Rust’s type safety and performance, enabling robust serialization in Python-Rust interop contexts.