Skip to content

Commit 578e7f2

Browse files
authored
Expose NTP64 (#650)
* Expose NTP64 * Remove unused import
1 parent 956c6a1 commit 578e7f2

3 files changed

Lines changed: 133 additions & 7 deletions

File tree

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ pub(crate) mod zenoh {
7373
sample::{Locality, Sample, SampleKind, SourceInfo},
7474
scouting::{scout, Hello, Scout},
7575
session::{open, EntityGlobalId, Session, SessionInfo},
76-
time::{Timestamp, TimestampId},
76+
time::{Timestamp, TimestampId, NTP64},
7777
ZError,
7878
};
7979

src/time.rs

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@ use std::{
1717
};
1818

1919
use pyo3::{
20+
exceptions::PyTypeError,
2021
prelude::*,
21-
types::{PyBytes, PyType},
22+
types::{PyBytes, PyDateTime, PyType},
2223
};
2324

2425
use crate::{
2526
macros::{downcast_or_new, wrapper},
26-
utils::IntoPyResult,
27+
utils::{IntoPyErr, IntoPyResult},
2728
};
2829

2930
wrapper!(zenoh::time::TimestampId: Copy, Clone, PartialEq, PartialOrd);
@@ -66,17 +67,30 @@ wrapper!(zenoh::time::Timestamp: Clone, PartialEq, PartialOrd, Hash);
6667
impl Timestamp {
6768
#[new]
6869
fn new(
69-
time: SystemTime,
70+
time: Bound<PyAny>,
7071
#[pyo3(from_py_with = TimestampId::from_py)] id: TimestampId,
7172
) -> PyResult<Self> {
72-
let timestamp = time.duration_since(SystemTime::UNIX_EPOCH).into_pyres()?;
73-
Ok(Self(zenoh::time::Timestamp::new(timestamp.into(), id.0)))
73+
let ntp = if let Ok(time) = time.downcast::<PyDateTime>() {
74+
time.extract::<SystemTime>()?
75+
.duration_since(SystemTime::UNIX_EPOCH)
76+
.into_pyres()?
77+
.into()
78+
} else if let Ok(ntp) = time.extract::<NTP64>() {
79+
ntp.0
80+
} else {
81+
return Err(PyTypeError::new_err("expected a `datetime` or a `NTP64`"));
82+
};
83+
Ok(Self(zenoh::time::Timestamp::new(ntp, id.0)))
7484
}
7585

7686
fn get_time(&self) -> SystemTime {
7787
self.0.get_time().to_system_time()
7888
}
7989

90+
fn get_time_as_ntp64(&self) -> NTP64 {
91+
(*self.0.get_time()).into()
92+
}
93+
8094
fn get_id(&self) -> TimestampId {
8195
(*self.0.get_id()).into()
8296
}
@@ -123,3 +137,69 @@ impl Timestamp {
123137
format!("{}", self.0)
124138
}
125139
}
140+
141+
wrapper!(zenoh::time::NTP64: Clone, PartialEq, PartialOrd, Hash);
142+
143+
#[pymethods]
144+
impl NTP64 {
145+
#[new]
146+
fn new(seconds: u64, nanoseconds: u32) -> Self {
147+
Self(Duration::new(seconds, nanoseconds).into())
148+
}
149+
150+
fn as_secs_f64(&self) -> f64 {
151+
self.0.as_secs_f64()
152+
}
153+
154+
fn as_secs(&self) -> u32 {
155+
self.0.as_secs()
156+
}
157+
158+
fn as_nanos(&self) -> u64 {
159+
self.0.as_nanos()
160+
}
161+
162+
fn subsec_nanos(&self) -> u32 {
163+
self.0.subsec_nanos()
164+
}
165+
166+
fn to_datetime(&self) -> SystemTime {
167+
SystemTime::UNIX_EPOCH + self.0.to_duration()
168+
}
169+
170+
fn to_string_rfc3339_lossy(&self) -> String {
171+
self.0.to_string_rfc3339_lossy()
172+
}
173+
174+
#[classmethod]
175+
fn parse_rfc3339(_cls: &Bound<PyType>, s: &str) -> PyResult<Self> {
176+
zenoh::time::NTP64::parse_rfc3339(s)
177+
.map(Self)
178+
.map_err(|err| err.cause.into_pyerr())
179+
}
180+
181+
fn __richcmp__(&self, other: &Self, op: pyo3::pyclass::CompareOp) -> bool {
182+
match op {
183+
pyo3::pyclass::CompareOp::Lt => self < other,
184+
pyo3::pyclass::CompareOp::Le => self <= other,
185+
pyo3::pyclass::CompareOp::Eq => self == other,
186+
pyo3::pyclass::CompareOp::Ne => self != other,
187+
pyo3::pyclass::CompareOp::Gt => self > other,
188+
pyo3::pyclass::CompareOp::Ge => self >= other,
189+
}
190+
}
191+
192+
fn __hash__(&self) -> u64 {
193+
let mut hasher = std::collections::hash_map::DefaultHasher::new();
194+
self.0.hash(&mut hasher);
195+
hasher.finish()
196+
}
197+
198+
fn __repr__(&self) -> String {
199+
format!("{:?}", self.0)
200+
}
201+
202+
fn __str__(&self) -> String {
203+
format!("{}", self.0)
204+
}
205+
}

zenoh/__init__.pyi

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,49 @@ class LivelinessToken:
574574
def undeclare(self):
575575
"""Undeclare the :class:`LivelinessToken`."""
576576

577+
@final
578+
class NTP64:
579+
"""A NTP 64-bits format as specified in RFC-5909 <https://tools.ietf.org/html/rfc5905#section-6>
580+
581+
The 1st 32-bits part is the number of second since the EPOCH of the physical clock,
582+
and the 2nd 32-bits part is the fraction of second."""
583+
584+
def __new__(cls, seconds: int, nanos: int) -> Self: ...
585+
def as_secs_f64(self) -> float:
586+
"""Returns this NTP64 as a f64 in seconds.
587+
588+
The integer part of the f64 is the NTP64's Seconds part.
589+
The decimal part of the f64 is the result of a division of NTP64's Fraction part divided by 2^32.
590+
Considering the probable large number of Seconds (for a time relative to UNIX_EPOCH), the precision of the resulting f64 might be in the order of microseconds.
591+
Therefore, it should not be used for comparison. Directly comparing [NTP64] objects is preferable.
592+
"""
593+
594+
def as_secs(self) -> int:
595+
"""Returns the 32-bits seconds part."""
596+
597+
def as_nanos(self) -> int:
598+
"""Returns the total duration converted to nanoseconds."""
599+
600+
def subsec_nanos(self) -> int:
601+
"""Returns the 32-bits fraction of second part converted to nanoseconds."""
602+
603+
def to_datetime(self) -> datetime:
604+
"""Returns this NTP64 as a datetime."""
605+
606+
def to_string_rfc3339_lossy(self) -> str:
607+
"""Convert to a RFC3339 time representation with nanoseconds precision.
608+
609+
e.g.: `"2024-07-01T13:51:12.129693000Z"``"""
610+
611+
@classmethod
612+
def parse_rfc3339(cls, s: str) -> Self:
613+
"""Parse a RFC3339 time representation into a NTP64."""
614+
615+
def __eq__(self, other: Any) -> bool: ...
616+
def __ge__(self, other) -> bool: ...
617+
def __hash__(self) -> int: ...
618+
def __str__(self) -> str: ...
619+
577620
@final
578621
class Parameters:
579622
"""A collection of key-value parameters used in query operations.
@@ -1723,10 +1766,13 @@ class Timestamp:
17231766
For detailed information about Timestamp, see: https://docs.rs/zenoh/latest/zenoh/time/struct.Timestamp.html
17241767
"""
17251768

1726-
def __new__(cls, time: datetime, id: _IntoTimestampId) -> Self: ...
1769+
def __new__(cls, time: datetime | NTP64, id: _IntoTimestampId) -> Self: ...
17271770
def get_time(self) -> datetime:
17281771
"""Returns the time component of the timestamp as a datetime object."""
17291772

1773+
def get_time_as_ntp64(self) -> NTP64:
1774+
"""Returns the time component of the timestamp as a datetime object."""
1775+
17301776
def get_id(self) -> TimestampId:
17311777
"""Returns the unique identifier component of the timestamp as a :class:`TimestampId`."""
17321778

0 commit comments

Comments
 (0)