1#[cfg(feature = "alloc")]
10use alloc::alloc::{Layout, alloc, dealloc};
11use core::cmp::Ordering;
12use core::fmt::{self, Debug, Formatter};
13use core::hash::{Hash, Hasher};
14
15use crate::value::{TypeTag, Value};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum DateTimeKind {
20 Offset {
23 offset_minutes: i16,
25 },
26
27 LocalDateTime,
30
31 LocalDate,
34
35 LocalTime,
38}
39
40#[repr(C, align(8))]
42struct DateTimeHeader {
43 year: i32,
45 month: u8,
47 day: u8,
49 hour: u8,
51 minute: u8,
53 second: u8,
55 _pad: [u8; 3],
57 nanos: u32,
59 kind: DateTimeKind,
61}
62
63#[repr(transparent)]
68#[derive(Clone)]
69pub struct VDateTime(pub(crate) Value);
70
71impl VDateTime {
72 fn layout() -> Layout {
73 Layout::new::<DateTimeHeader>()
74 }
75
76 #[cfg(feature = "alloc")]
77 fn alloc() -> *mut DateTimeHeader {
78 unsafe { alloc(Self::layout()).cast::<DateTimeHeader>() }
79 }
80
81 #[cfg(feature = "alloc")]
82 fn dealloc(ptr: *mut DateTimeHeader) {
83 unsafe {
84 dealloc(ptr.cast::<u8>(), Self::layout());
85 }
86 }
87
88 fn header(&self) -> &DateTimeHeader {
89 unsafe { &*(self.0.heap_ptr() as *const DateTimeHeader) }
90 }
91
92 #[allow(dead_code)]
93 fn header_mut(&mut self) -> &mut DateTimeHeader {
94 unsafe { &mut *(self.0.heap_ptr_mut() as *mut DateTimeHeader) }
95 }
96
97 #[cfg(feature = "alloc")]
109 #[must_use]
110 #[allow(clippy::too_many_arguments)]
111 pub fn new_offset(
112 year: i32,
113 month: u8,
114 day: u8,
115 hour: u8,
116 minute: u8,
117 second: u8,
118 nanos: u32,
119 offset_minutes: i16,
120 ) -> Self {
121 unsafe {
122 let ptr = Self::alloc();
123 (*ptr).year = year;
124 (*ptr).month = month;
125 (*ptr).day = day;
126 (*ptr).hour = hour;
127 (*ptr).minute = minute;
128 (*ptr).second = second;
129 (*ptr)._pad = [0; 3];
130 (*ptr).nanos = nanos;
131 (*ptr).kind = DateTimeKind::Offset { offset_minutes };
132 VDateTime(Value::new_ptr(ptr.cast(), TypeTag::DateTime))
133 }
134 }
135
136 #[cfg(feature = "alloc")]
138 #[must_use]
139 pub fn new_local_datetime(
140 year: i32,
141 month: u8,
142 day: u8,
143 hour: u8,
144 minute: u8,
145 second: u8,
146 nanos: u32,
147 ) -> Self {
148 unsafe {
149 let ptr = Self::alloc();
150 (*ptr).year = year;
151 (*ptr).month = month;
152 (*ptr).day = day;
153 (*ptr).hour = hour;
154 (*ptr).minute = minute;
155 (*ptr).second = second;
156 (*ptr)._pad = [0; 3];
157 (*ptr).nanos = nanos;
158 (*ptr).kind = DateTimeKind::LocalDateTime;
159 VDateTime(Value::new_ptr(ptr.cast(), TypeTag::DateTime))
160 }
161 }
162
163 #[cfg(feature = "alloc")]
165 #[must_use]
166 pub fn new_local_date(year: i32, month: u8, day: u8) -> Self {
167 unsafe {
168 let ptr = Self::alloc();
169 (*ptr).year = year;
170 (*ptr).month = month;
171 (*ptr).day = day;
172 (*ptr).hour = 0;
173 (*ptr).minute = 0;
174 (*ptr).second = 0;
175 (*ptr)._pad = [0; 3];
176 (*ptr).nanos = 0;
177 (*ptr).kind = DateTimeKind::LocalDate;
178 VDateTime(Value::new_ptr(ptr.cast(), TypeTag::DateTime))
179 }
180 }
181
182 #[cfg(feature = "alloc")]
184 #[must_use]
185 pub fn new_local_time(hour: u8, minute: u8, second: u8, nanos: u32) -> Self {
186 unsafe {
187 let ptr = Self::alloc();
188 (*ptr).year = 0;
189 (*ptr).month = 0;
190 (*ptr).day = 0;
191 (*ptr).hour = hour;
192 (*ptr).minute = minute;
193 (*ptr).second = second;
194 (*ptr)._pad = [0; 3];
195 (*ptr).nanos = nanos;
196 (*ptr).kind = DateTimeKind::LocalTime;
197 VDateTime(Value::new_ptr(ptr.cast(), TypeTag::DateTime))
198 }
199 }
200
201 #[must_use]
203 pub fn kind(&self) -> DateTimeKind {
204 self.header().kind
205 }
206
207 #[must_use]
209 pub fn year(&self) -> i32 {
210 self.header().year
211 }
212
213 #[must_use]
215 pub fn month(&self) -> u8 {
216 self.header().month
217 }
218
219 #[must_use]
221 pub fn day(&self) -> u8 {
222 self.header().day
223 }
224
225 #[must_use]
227 pub fn hour(&self) -> u8 {
228 self.header().hour
229 }
230
231 #[must_use]
233 pub fn minute(&self) -> u8 {
234 self.header().minute
235 }
236
237 #[must_use]
239 pub fn second(&self) -> u8 {
240 self.header().second
241 }
242
243 #[must_use]
245 pub fn nanos(&self) -> u32 {
246 self.header().nanos
247 }
248
249 #[must_use]
251 pub fn offset_minutes(&self) -> Option<i16> {
252 match self.kind() {
253 DateTimeKind::Offset { offset_minutes } => Some(offset_minutes),
254 _ => None,
255 }
256 }
257
258 #[must_use]
260 pub fn has_date(&self) -> bool {
261 !matches!(self.kind(), DateTimeKind::LocalTime)
262 }
263
264 #[must_use]
266 pub fn has_time(&self) -> bool {
267 !matches!(self.kind(), DateTimeKind::LocalDate)
268 }
269
270 #[must_use]
272 pub fn has_offset(&self) -> bool {
273 matches!(self.kind(), DateTimeKind::Offset { .. })
274 }
275
276 pub(crate) fn clone_impl(&self) -> Value {
279 #[cfg(feature = "alloc")]
280 {
281 let h = self.header();
282 match h.kind {
283 DateTimeKind::Offset { offset_minutes } => {
284 Self::new_offset(
285 h.year,
286 h.month,
287 h.day,
288 h.hour,
289 h.minute,
290 h.second,
291 h.nanos,
292 offset_minutes,
293 )
294 .0
295 }
296 DateTimeKind::LocalDateTime => {
297 Self::new_local_datetime(
298 h.year, h.month, h.day, h.hour, h.minute, h.second, h.nanos,
299 )
300 .0
301 }
302 DateTimeKind::LocalDate => Self::new_local_date(h.year, h.month, h.day).0,
303 DateTimeKind::LocalTime => {
304 Self::new_local_time(h.hour, h.minute, h.second, h.nanos).0
305 }
306 }
307 }
308 #[cfg(not(feature = "alloc"))]
309 {
310 panic!("cannot clone VDateTime without alloc feature")
311 }
312 }
313
314 pub(crate) fn drop_impl(&mut self) {
315 #[cfg(feature = "alloc")]
316 unsafe {
317 Self::dealloc(self.0.heap_ptr_mut().cast());
318 }
319 }
320}
321
322impl PartialEq for VDateTime {
325 fn eq(&self, other: &Self) -> bool {
326 let (h1, h2) = (self.header(), other.header());
327 h1.kind == h2.kind
328 && h1.year == h2.year
329 && h1.month == h2.month
330 && h1.day == h2.day
331 && h1.hour == h2.hour
332 && h1.minute == h2.minute
333 && h1.second == h2.second
334 && h1.nanos == h2.nanos
335 }
336}
337
338impl Eq for VDateTime {}
339
340impl PartialOrd for VDateTime {
343 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
344 let (h1, h2) = (self.header(), other.header());
345
346 match (&h1.kind, &h2.kind) {
348 (
349 DateTimeKind::Offset { offset_minutes: o1 },
350 DateTimeKind::Offset { offset_minutes: o2 },
351 ) => {
352 let to_comparable = |h: &DateTimeHeader, offset: i16| -> (i64, u32) {
355 let days = h.year as i64 * 366 + h.month as i64 * 31 + h.day as i64;
356 let secs = days * 86400
357 + h.hour as i64 * 3600
358 + h.minute as i64 * 60
359 + h.second as i64
360 - offset as i64 * 60;
361 (secs, h.nanos)
362 };
363 let c1 = to_comparable(h1, *o1);
364 let c2 = to_comparable(h2, *o2);
365 c1.partial_cmp(&c2)
366 }
367 (DateTimeKind::LocalDateTime, DateTimeKind::LocalDateTime)
368 | (DateTimeKind::LocalDate, DateTimeKind::LocalDate) => {
369 (
371 h1.year, h1.month, h1.day, h1.hour, h1.minute, h1.second, h1.nanos,
372 )
373 .partial_cmp(&(
374 h2.year, h2.month, h2.day, h2.hour, h2.minute, h2.second, h2.nanos,
375 ))
376 }
377 (DateTimeKind::LocalTime, DateTimeKind::LocalTime) => {
378 (h1.hour, h1.minute, h1.second, h1.nanos)
379 .partial_cmp(&(h2.hour, h2.minute, h2.second, h2.nanos))
380 }
381 _ => None, }
383 }
384}
385
386impl Hash for VDateTime {
389 fn hash<H: Hasher>(&self, state: &mut H) {
390 let h = self.header();
391 h.kind.hash(state);
392 h.year.hash(state);
393 h.month.hash(state);
394 h.day.hash(state);
395 h.hour.hash(state);
396 h.minute.hash(state);
397 h.second.hash(state);
398 h.nanos.hash(state);
399 }
400}
401
402impl Debug for VDateTime {
405 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
406 let h = self.header();
407 match h.kind {
408 DateTimeKind::Offset { offset_minutes } => {
409 write!(
410 f,
411 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}",
412 h.year, h.month, h.day, h.hour, h.minute, h.second
413 )?;
414 if h.nanos > 0 {
415 write!(f, ".{:09}", h.nanos)?;
416 }
417 if offset_minutes == 0 {
418 write!(f, "Z")
419 } else {
420 let sign = if offset_minutes >= 0 { '+' } else { '-' };
421 let abs = offset_minutes.abs();
422 write!(f, "{}{:02}:{:02}", sign, abs / 60, abs % 60)
423 }
424 }
425 DateTimeKind::LocalDateTime => {
426 write!(
427 f,
428 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}",
429 h.year, h.month, h.day, h.hour, h.minute, h.second
430 )?;
431 if h.nanos > 0 {
432 write!(f, ".{:09}", h.nanos)?;
433 }
434 Ok(())
435 }
436 DateTimeKind::LocalDate => {
437 write!(f, "{:04}-{:02}-{:02}", h.year, h.month, h.day)
438 }
439 DateTimeKind::LocalTime => {
440 write!(f, "{:02}:{:02}:{:02}", h.hour, h.minute, h.second)?;
441 if h.nanos > 0 {
442 write!(f, ".{:09}", h.nanos)?;
443 }
444 Ok(())
445 }
446 }
447 }
448}
449
450#[cfg(feature = "alloc")]
453impl From<VDateTime> for Value {
454 fn from(dt: VDateTime) -> Self {
455 dt.0
456 }
457}