lib/models/
datetime.rs

1//! Defines the [`DateTimeUtc`] struct.
2
3use std::ops::{Deref, DerefMut};
4use std::time::UNIX_EPOCH;
5
6use chrono::{DateTime, Utc};
7use serde::Serialize;
8
9/// A newtype around [`chrono`]'s [`DateTime<Utc>`] to allow implementation of the [`Default`] trait.
10///
11/// Why do we need a Default implementation?
12///
13/// When a template is registered, it's validated to make sure it contains no syntax errors
14/// or variables that reference non-existent fields. In order to achieve the latter, a dummy
15/// [`Entry`][entry] struct---its Default implementation---is passed to validate the template's
16/// variables.
17///
18/// See [`Renderer`][renderer] and [`dummy`][dummy] for more information.
19///
20/// [dummy]: crate::models::dummy
21/// [entry]: crate::models::entry::Entry
22/// [renderer]: crate::render::renderer::Renderer
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
24pub struct DateTimeUtc(DateTime<Utc>);
25
26impl Default for DateTimeUtc {
27    fn default() -> Self {
28        Self(DateTime::<Utc>::from(UNIX_EPOCH))
29    }
30}
31
32impl Deref for DateTimeUtc {
33    type Target = DateTime<Utc>;
34
35    fn deref(&self) -> &DateTime<Utc> {
36        &self.0
37    }
38}
39
40impl DerefMut for DateTimeUtc {
41    fn deref_mut(&mut self) -> &mut DateTime<Utc> {
42        &mut self.0
43    }
44}
45
46/// Converts a `Core Data` timestamp (f64) to `DateTime`.
47///
48/// A `Core Data` timestamp is the number of seconds (or nanoseconds) since midnight, January 1,
49/// 2001, GMT. The difference between a `Core Data` timestamp and a Unix timestamp (seconds since
50/// 1/1/1970) is 978307200 seconds.
51///
52/// <https://www.epochconverter.com/coredata>
53#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
54impl From<f64> for DateTimeUtc {
55    fn from(f: f64) -> Self {
56        // Add the `Core Data` timestamp offset
57        let timestamp = f + 978_307_200_f64;
58
59        let seconds = timestamp.trunc() as i64;
60        let nanoseconds = timestamp.fract() * 1_000_000_000.0;
61        // Unwrap should be safe here as the timestamps are coming from the OS.
62        let datetime = DateTime::from_timestamp(seconds, nanoseconds as u32).unwrap();
63
64        DateTimeUtc(datetime)
65    }
66}