messagepack_core/
timestamp.rs

1//! MessagePack timestamp extension values.
2
3use crate::extension::{ExtensionRef, FixedExtension};
4
5pub(crate) const TIMESTAMP_EXTENSION_TYPE: i8 = -1;
6
7/// The error type returned when a checked extension conversion fails
8pub enum TryFromTimeStampError {
9    /// The format is not a valid timestamp type
10    InvalidType,
11    /// The data length is not valid for timestamp format
12    InvalidDataLength,
13}
14
15/// Represents timestamp 32 extension type.
16/// This stores 32bit unsigned seconds
17#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
18pub struct Timestamp32 {
19    secs: u32,
20}
21
22impl Timestamp32 {
23    /// Create a 32‑bit seconds timestamp.
24    pub fn new(seconds: u32) -> Self {
25        Self { secs: seconds }
26    }
27
28    /// Get seconds since the UNIX epoch.
29    pub fn seconds(&self) -> u32 {
30        self.secs
31    }
32
33    pub(crate) fn to_buf(self) -> [u8; 4] {
34        self.secs.to_be_bytes()
35    }
36
37    pub(crate) fn from_buf(buf: [u8; 4]) -> Self {
38        Self {
39            secs: u32::from_be_bytes(buf),
40        }
41    }
42}
43
44impl TryFrom<ExtensionRef<'_>> for Timestamp32 {
45    type Error = TryFromTimeStampError;
46
47    fn try_from(value: ExtensionRef<'_>) -> Result<Self, Self::Error> {
48        if value.r#type != TIMESTAMP_EXTENSION_TYPE {
49            return Err(TryFromTimeStampError::InvalidType);
50        }
51
52        let data = value.data;
53        let mut buf = [0u8; 4];
54        if data.len() != buf.len() {
55            return Err(TryFromTimeStampError::InvalidDataLength);
56        }
57
58        buf.copy_from_slice(data);
59        Ok(Self::from_buf(buf))
60    }
61}
62
63impl TryFrom<FixedExtension<4>> for Timestamp32 {
64    type Error = TryFromTimeStampError;
65
66    fn try_from(value: FixedExtension<4>) -> Result<Self, Self::Error> {
67        value.as_ref().try_into()
68    }
69}
70
71impl From<Timestamp32> for FixedExtension<4> {
72    fn from(value: Timestamp32) -> Self {
73        let buf = value.to_buf();
74        FixedExtension::new_fixed(TIMESTAMP_EXTENSION_TYPE, buf.len(), buf)
75    }
76}
77
78/// Represents timestamp 64 extension type.
79/// This stores 34bit unsigned seconds and 30bit nanoseconds
80#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
81pub struct Timestamp64 {
82    data: [u8; 8],
83}
84
85/// `seconds` or `nanos` cannot be represented
86#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
87pub struct Timestamp64Error {
88    /// Requested seconds that exceeded the 34‑bit range.
89    pub seconds: u64,
90    /// Requested nanoseconds that exceeded the 30‑bit range.
91    pub nanos: u32,
92}
93
94impl Timestamp64 {
95    /// Create a 64‑bit timestamp storing 34‑bit seconds and 30‑bit nanoseconds.
96    pub fn new(seconds: u64, nanos: u32) -> Result<Self, Timestamp64Error> {
97        const SECONDS_MAX_LIMIT: u64 = 1 << 34;
98
99        if seconds >= SECONDS_MAX_LIMIT {
100            return Err(Timestamp64Error { seconds, nanos });
101        }
102
103        const NANOS_MAX_LIMIT: u32 = 1 << 30;
104        if nanos >= NANOS_MAX_LIMIT {
105            return Err(Timestamp64Error { seconds, nanos });
106        }
107
108        let mut buf = [0u8; 8];
109        buf[..].copy_from_slice(&seconds.to_be_bytes());
110
111        let nano = (nanos << 2).to_be_bytes();
112        buf[..3].copy_from_slice(&nano[..3]);
113        buf[3] |= nano[3];
114
115        Ok(Self::from_buf(buf))
116    }
117
118    /// Get the nanoseconds component.
119    pub fn nanos(&self) -> u32 {
120        let mut buf = [0u8; 4];
121        buf.copy_from_slice(&self.data[..4]);
122        let nanosec = u32::from_be_bytes(buf);
123        nanosec >> 2
124    }
125
126    /// Get the seconds component.
127    pub fn seconds(&self) -> u64 {
128        // 34bit mask
129        const MASK: u64 = (1 << 34) - 1;
130        let mut buf = [0u8; 8];
131        buf.copy_from_slice(&self.data[..]);
132        let seconds = u64::from_be_bytes(buf);
133
134        seconds & MASK
135    }
136
137    pub(crate) fn to_buf(self) -> [u8; 8] {
138        self.data
139    }
140
141    pub(crate) fn from_buf(buf: [u8; 8]) -> Self {
142        Self { data: buf }
143    }
144}
145
146impl TryFrom<ExtensionRef<'_>> for Timestamp64 {
147    type Error = TryFromTimeStampError;
148
149    fn try_from(value: ExtensionRef<'_>) -> Result<Self, Self::Error> {
150        if value.r#type != TIMESTAMP_EXTENSION_TYPE {
151            return Err(TryFromTimeStampError::InvalidType);
152        }
153
154        let data = value.data;
155        let mut buf = [0u8; 8];
156        if data.len() != buf.len() {
157            return Err(TryFromTimeStampError::InvalidDataLength);
158        }
159
160        buf.copy_from_slice(data);
161        Ok(Self::from_buf(buf))
162    }
163}
164
165impl TryFrom<FixedExtension<8>> for Timestamp64 {
166    type Error = TryFromTimeStampError;
167
168    fn try_from(value: FixedExtension<8>) -> Result<Self, Self::Error> {
169        value.as_ref().try_into()
170    }
171}
172
173impl From<Timestamp64> for FixedExtension<8> {
174    fn from(value: Timestamp64) -> Self {
175        let buf = value.to_buf();
176        FixedExtension::new_fixed(TIMESTAMP_EXTENSION_TYPE, buf.len(), buf)
177    }
178}
179
180/// Represents timestamp 96 extension type.
181/// This stores 64bit signed seconds and 32bit nanoseconds
182#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
183pub struct Timestamp96 {
184    nanos: u32,
185    secs: i64,
186}
187
188impl Timestamp96 {
189    /// Create a 96‑bit timestamp storing signed seconds and nanoseconds.
190    pub fn new(seconds: i64, nanoseconds: u32) -> Self {
191        Self {
192            nanos: nanoseconds,
193            secs: seconds,
194        }
195    }
196
197    /// Get the nanoseconds component.
198    pub fn nanos(&self) -> u32 {
199        self.nanos
200    }
201
202    /// Get the seconds component.
203    pub fn seconds(&self) -> i64 {
204        self.secs
205    }
206
207    pub(crate) fn to_buf(self) -> [u8; 12] {
208        let mut buf = [0u8; 12];
209        buf[..4].copy_from_slice(&self.nanos.to_be_bytes());
210        buf[4..].copy_from_slice(&self.secs.to_be_bytes());
211
212        buf
213    }
214
215    pub(crate) fn from_buf(buf: [u8; 12]) -> Self {
216        let mut nano = [0u8; 4];
217        nano.copy_from_slice(&buf[..4]);
218
219        let mut second = [0u8; 8];
220        second.copy_from_slice(&buf[4..]);
221
222        Self {
223            nanos: u32::from_be_bytes(nano),
224            secs: i64::from_be_bytes(second),
225        }
226    }
227}
228
229impl TryFrom<ExtensionRef<'_>> for Timestamp96 {
230    type Error = TryFromTimeStampError;
231
232    fn try_from(value: ExtensionRef<'_>) -> Result<Self, Self::Error> {
233        if value.r#type != TIMESTAMP_EXTENSION_TYPE {
234            return Err(TryFromTimeStampError::InvalidType);
235        }
236
237        let data = value.data;
238        let mut buf = [0u8; 12];
239        if data.len() != buf.len() {
240            return Err(TryFromTimeStampError::InvalidDataLength);
241        }
242
243        buf.copy_from_slice(data);
244        Ok(Self::from_buf(buf))
245    }
246}
247
248impl TryFrom<FixedExtension<12>> for Timestamp96 {
249    type Error = TryFromTimeStampError;
250
251    fn try_from(value: FixedExtension<12>) -> Result<Self, Self::Error> {
252        value.as_ref().try_into()
253    }
254}
255
256impl From<Timestamp96> for FixedExtension<12> {
257    fn from(value: Timestamp96) -> Self {
258        let buf = value.to_buf();
259        FixedExtension::new_fixed(TIMESTAMP_EXTENSION_TYPE, buf.len(), buf)
260    }
261}