summaryrefslogtreecommitdiff
path: root/backend/src/models/date_time_utc.rs
blob: 6c5dc33819da89d0c494a7dfa3046240c58ff587 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use chrono::{DateTime, Datelike, Utc};
use diesel::{
    backend::Backend,
    deserialize::{self, FromSql, FromSqlRow},
    expression::AsExpression,
    serialize::{self, ToSql},
    sql_types::Text,
    sqlite::Sqlite,
};
use serde::{Deserialize, Serialize};

#[derive(
    Eq,
    PartialEq,
    Ord,
    PartialOrd,
    Serialize,
    Deserialize,
    Copy,
    Clone,
    Debug,
    AsExpression,
    FromSqlRow,
)]
#[diesel(sql_type=Text)]
pub struct DateTimeUtc(DateTime<Utc>);

impl DateTimeUtc {
    pub fn day_diff(&self, other: &DateTimeUtc) -> u32 {
        self.0
            .num_days_from_ce()
            .abs_diff(other.0.num_days_from_ce())
    }

    pub fn new(date_time_utc: DateTime<Utc>) -> Self {
        Self(date_time_utc)
    }

    pub fn now() -> Self {
        Self(Utc::now())
    }
}

impl ToSql<Text, Sqlite> for DateTimeUtc {
    fn to_sql<'b>(
        &'b self,
        out: &mut diesel::serialize::Output<'b, '_, Sqlite>,
    ) -> serialize::Result {
        out.set_value(self.0.to_rfc3339());
        Ok(diesel::serialize::IsNull::No)
    }
}

impl FromSql<Text, Sqlite> for DateTimeUtc {
    fn from_sql(bytes: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<DateTimeUtc> {
        let str: String = FromSql::<Text, Sqlite>::from_sql(bytes)?;
        let date_time = str.as_str().parse::<DateTime<Utc>>()?;
        Ok(DateTimeUtc(date_time))
    }
}

#[cfg(test)]
mod test {
    use chrono::TimeZone;

    use super::*;

    #[test]
    fn given_midnight_when_day_diff_next_midnight_less_1s_then_return_0() {
        let start = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap().to_utc());
        let end = DateTimeUtc::new(
            Utc.with_ymd_and_hms(2000, 1, 1, 23, 59, 59)
                .unwrap()
                .to_utc(),
        );

        let day_diff = start.day_diff(&end);
        let neg_day_diff = end.day_diff(&start);

        assert_eq!(day_diff, 0);
        assert_eq!(day_diff, neg_day_diff);
    }

    #[test]
    fn given_midnight_when_day_diff_next_midnight_then_return_1() {
        let start = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 1, 0, 0, 0).unwrap().to_utc());
        let end = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 2, 0, 0, 0).unwrap().to_utc());

        let day_diff = start.day_diff(&end);
        let neg_day_diff = end.day_diff(&start);

        assert_eq!(day_diff, 1);
        assert_eq!(day_diff, neg_day_diff);
    }

    #[test]
    fn given_midnight_less_1s_when_day_diff_midnight_then_return_1() {
        let start = DateTimeUtc::new(
            Utc.with_ymd_and_hms(2000, 1, 1, 23, 59, 59)
                .unwrap()
                .to_utc(),
        );
        let end = DateTimeUtc::new(Utc.with_ymd_and_hms(2000, 1, 2, 0, 0, 0).unwrap().to_utc());

        let day_diff = start.day_diff(&end);
        let neg_day_diff = end.day_diff(&start);

        assert_eq!(day_diff, 1);
        assert_eq!(day_diff, neg_day_diff);
    }
}