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        FixedExtension::new_fixed(TIMESTAMP_EXTENSION_TYPE, value.to_buf())
74    }
75}
76
77/// Represents timestamp 64 extension type.
78/// This stores 34bit unsigned seconds and 30bit nanoseconds
79#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
80pub struct Timestamp64 {
81    data: [u8; 8],
82}
83
84/// `seconds` or `nanos` cannot be represented
85#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
86pub struct Timestamp64Error {
87    /// Requested seconds that exceeded the 34‑bit range.
88    pub seconds: u64,
89    /// Requested nanoseconds that exceeded the 30‑bit range.
90    pub nanos: u32,
91}
92
93impl Timestamp64 {
94    /// Create a 64‑bit timestamp storing 34‑bit seconds and 30‑bit nanoseconds.
95    pub fn new(seconds: u64, nanos: u32) -> Result<Self, Timestamp64Error> {
96        const SECONDS_MAX_LIMIT: u64 = 1 << 34;
97
98        if seconds >= SECONDS_MAX_LIMIT {
99            return Err(Timestamp64Error { seconds, nanos });
100        }
101
102        const NANOS_MAX_LIMIT: u32 = 1 << 30;
103        if nanos >= NANOS_MAX_LIMIT {
104            return Err(Timestamp64Error { seconds, nanos });
105        }
106
107        let mut buf = [0u8; 8];
108        buf[..].copy_from_slice(&seconds.to_be_bytes());
109
110        let nano = (nanos << 2).to_be_bytes();
111        buf[..3].copy_from_slice(&nano[..3]);
112        buf[3] |= nano[3];
113
114        Ok(Self::from_buf(buf))
115    }
116
117    /// Get the nanoseconds component.
118    pub fn nanos(&self) -> u32 {
119        let mut buf = [0u8; 4];
120        buf.copy_from_slice(&self.data[..4]);
121        let nanosec = u32::from_be_bytes(buf);
122        nanosec >> 2
123    }
124
125    /// Get the seconds component.
126    pub fn seconds(&self) -> u64 {
127        // 34bit mask
128        const MASK: u64 = (1 << 34) - 1;
129        let mut buf = [0u8; 8];
130        buf.copy_from_slice(&self.data[..]);
131        let seconds = u64::from_be_bytes(buf);
132
133        seconds & MASK
134    }
135
136    pub(crate) fn to_buf(self) -> [u8; 8] {
137        self.data
138    }
139
140    pub(crate) fn from_buf(buf: [u8; 8]) -> Self {
141        Self { data: buf }
142    }
143}
144
145impl TryFrom<ExtensionRef<'_>> for Timestamp64 {
146    type Error = TryFromTimeStampError;
147
148    fn try_from(value: ExtensionRef<'_>) -> Result<Self, Self::Error> {
149        if value.r#type != TIMESTAMP_EXTENSION_TYPE {
150            return Err(TryFromTimeStampError::InvalidType);
151        }
152
153        let data = value.data;
154        let mut buf = [0u8; 8];
155        if data.len() != buf.len() {
156            return Err(TryFromTimeStampError::InvalidDataLength);
157        }
158
159        buf.copy_from_slice(data);
160        Ok(Self::from_buf(buf))
161    }
162}
163
164impl TryFrom<FixedExtension<8>> for Timestamp64 {
165    type Error = TryFromTimeStampError;
166
167    fn try_from(value: FixedExtension<8>) -> Result<Self, Self::Error> {
168        value.as_ref().try_into()
169    }
170}
171
172impl From<Timestamp64> for FixedExtension<8> {
173    fn from(value: Timestamp64) -> Self {
174        FixedExtension::new_fixed(TIMESTAMP_EXTENSION_TYPE, value.to_buf())
175    }
176}
177
178/// Represents timestamp 96 extension type.
179/// This stores 64bit signed seconds and 32bit nanoseconds
180#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
181pub struct Timestamp96 {
182    nanos: u32,
183    secs: i64,
184}
185
186impl Timestamp96 {
187    /// Create a 96‑bit timestamp storing signed seconds and nanoseconds.
188    pub fn new(seconds: i64, nanoseconds: u32) -> Self {
189        Self {
190            nanos: nanoseconds,
191            secs: seconds,
192        }
193    }
194
195    /// Get the nanoseconds component.
196    pub fn nanos(&self) -> u32 {
197        self.nanos
198    }
199
200    /// Get the seconds component.
201    pub fn seconds(&self) -> i64 {
202        self.secs
203    }
204
205    pub(crate) fn to_buf(self) -> [u8; 12] {
206        let mut buf = [0u8; 12];
207        buf[..4].copy_from_slice(&self.nanos.to_be_bytes());
208        buf[4..].copy_from_slice(&self.secs.to_be_bytes());
209
210        buf
211    }
212
213    pub(crate) fn from_buf(buf: [u8; 12]) -> Self {
214        let mut nano = [0u8; 4];
215        nano.copy_from_slice(&buf[..4]);
216
217        let mut second = [0u8; 8];
218        second.copy_from_slice(&buf[4..]);
219
220        Self {
221            nanos: u32::from_be_bytes(nano),
222            secs: i64::from_be_bytes(second),
223        }
224    }
225}
226
227impl TryFrom<ExtensionRef<'_>> for Timestamp96 {
228    type Error = TryFromTimeStampError;
229
230    fn try_from(value: ExtensionRef<'_>) -> Result<Self, Self::Error> {
231        if value.r#type != TIMESTAMP_EXTENSION_TYPE {
232            return Err(TryFromTimeStampError::InvalidType);
233        }
234
235        let data = value.data;
236        let mut buf = [0u8; 12];
237        if data.len() != buf.len() {
238            return Err(TryFromTimeStampError::InvalidDataLength);
239        }
240
241        buf.copy_from_slice(data);
242        Ok(Self::from_buf(buf))
243    }
244}
245
246impl TryFrom<FixedExtension<12>> for Timestamp96 {
247    type Error = TryFromTimeStampError;
248
249    fn try_from(value: FixedExtension<12>) -> Result<Self, Self::Error> {
250        value.as_ref().try_into()
251    }
252}
253
254impl From<Timestamp96> for FixedExtension<12> {
255    fn from(value: Timestamp96) -> Self {
256        FixedExtension::new_fixed(TIMESTAMP_EXTENSION_TYPE, value.to_buf())
257    }
258}