Skip to main content

hvz/
time.rs

1use std::ops::{Add, AddAssign, Deref};
2
3use chrono::{DateTime, Duration, NaiveDateTime, TimeDelta, Utc};
4use chrono_tz::Tz;
5use diesel::backend::Backend;
6use diesel::deserialize::{self, FromSql, FromSqlRow};
7use diesel::expression::AsExpression;
8use diesel::mysql::Mysql;
9use diesel::serialize::{self, Output, ToSql};
10use diesel::sql_types::Datetime;
11use serde::{Deserialize, Serialize};
12
13/// `CustomDateTime` is a wrapper around `NaiveDateTime` that provides additional functionality.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromSqlRow, AsExpression)]
15#[diesel(sql_type = Datetime)]
16pub struct CustomDateTime(NaiveDateTime);
17
18/// `CustomDateTime` implementing certain useful methods.
19impl CustomDateTime {
20    /// Returns the inner `NaiveDateTime`.
21    pub fn inner(&self) -> NaiveDateTime {
22        self.0
23    }
24
25    /// Returns the current date and time.
26    pub fn now() -> Self {
27        CustomDateTime(chrono::Local::now().naive_local())
28    }
29}
30
31/// `From<NaiveDateTime>` implementation for `CustomDateTime`.
32impl From<NaiveDateTime> for CustomDateTime {
33    /// Converts a `NaiveDateTime` to a `CustomDateTime`.
34    fn from(dt: NaiveDateTime) -> Self {
35        CustomDateTime(dt)
36    }
37}
38
39/// `FromSql` implementation for `CustomDateTime`.
40impl FromSql<Datetime, Mysql> for CustomDateTime {
41    /// Converts a MySQL datetime to a `CustomDateTime`.
42    fn from_sql<'a>(bytes: <Mysql as Backend>::RawValue<'a>) -> deserialize::Result<Self> {
43        let utc_dt = <NaiveDateTime as FromSql<Datetime, Mysql>>::from_sql(bytes)?;
44        let barcelona_tz: Tz = "Europe/Madrid".parse().unwrap();
45        let utc_datetime: DateTime<Utc> = DateTime::from_naive_utc_and_offset(utc_dt, Utc);
46        let barcelona_datetime = utc_datetime.with_timezone(&barcelona_tz);
47        Ok(CustomDateTime(barcelona_datetime.naive_local()))
48    }
49}
50
51/// `ToSql` implementation for `CustomDateTime`.
52impl<DB> ToSql<Datetime, DB> for CustomDateTime
53where
54    DB: Backend,
55    NaiveDateTime: ToSql<Datetime, DB>,
56{
57    /// Converts a `CustomDateTime` to a MySQL datetime.
58    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> serialize::Result {
59        // We take the `NaiveDateTime` inside our struct and serialize that
60        self.0.to_sql(out)
61    }
62}
63
64/// `Deref` implementation for `CustomDateTime`.
65impl Deref for CustomDateTime {
66    /// Target type for dereferencing.
67    type Target = NaiveDateTime;
68
69    /// Dereferences the `CustomDateTime` to its underlying `NaiveDateTime`.
70    fn deref(&self) -> &Self::Target {
71        &self.0
72    }
73}
74
75/// `AddAssign` implementation for `CustomDateTime`.
76impl AddAssign<TimeDelta> for CustomDateTime {
77    /// Adds a `TimeDelta` to the `CustomDateTime`.
78    fn add_assign(&mut self, other: TimeDelta) {
79        self.0 += other;
80    }
81}
82
83/// Add implementation for `CustomDateTime`.
84impl Add<Duration> for CustomDateTime {
85    /// Outputs a new `CustomDateTime` by adding a Duration to the current one.
86    type Output = Self;
87
88    /// Adds a `Duration` to the `CustomDateTime`.
89    fn add(mut self, other: Duration) -> Self {
90        self.0 += other;
91        self
92    }
93}
94
95/// Serialize implementation for `CustomDateTime`.
96impl Serialize for CustomDateTime {
97    /// Serializes the `CustomDateTime` into a JSON string.
98    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
99    where
100        S: serde::Serializer,
101    {
102        self.0.serialize(serializer)
103    }
104}
105
106/// `Deserialize` implementation for `CustomDateTime`.
107impl<'de> Deserialize<'de> for CustomDateTime {
108    /// Deserializes a JSON string into a `CustomDateTime`.
109    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
110    where
111        D: serde::Deserializer<'de>,
112    {
113        let naive_dt = NaiveDateTime::deserialize(deserializer)?;
114        let barcelona_tz: Tz = "Europe/Madrid".parse().unwrap();
115        let utc_datetime: DateTime<Utc> = DateTime::from_naive_utc_and_offset(naive_dt, Utc);
116        let barcelona_datetime = utc_datetime.with_timezone(&barcelona_tz);
117        Ok(CustomDateTime(barcelona_datetime.naive_local()))
118    }
119}