af_core/time/
date.rs

1// Copyright © 2020 Alexandra Frydl
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7use super::Zone;
8use crate::prelude::*;
9use chrono::{Datelike, TimeZone};
10
11#[derive(Clone, Copy, Eq, From, Into, Ord, PartialEq, PartialOrd)]
12pub struct Date(chrono::NaiveDate);
13
14impl Date {
15  /// Creates a date from a given year, month, and day number.
16  pub fn from_ymd(year: isize, month: usize, day: usize) -> Self {
17    Self(chrono::NaiveDate::from_ymd(year as i32, month as u32, day as u32))
18  }
19
20  /// Converts the date to a time in the given time zone with the given hour,
21  /// minute, and second.
22  pub fn and_hms(&self, hour: usize, minute: usize, second: usize, zone: Zone) -> Time {
23    let inner = match &zone {
24      Zone::Local => chrono::Local
25        .from_local_date(&self.0)
26        .and_hms_opt(hour as u32, minute as u32, second as u32)
27        .unwrap()
28        .with_timezone(&chrono::Utc),
29
30      Zone::Tz(tz) => tz
31        .from_local_date(&self.0)
32        .and_hms_opt(hour as u32, minute as u32, second as u32)
33        .unwrap()
34        .with_timezone(&chrono::Utc),
35    };
36
37    Time { inner, zone }
38  }
39
40  /// Returns the day of the month starting from `1`.
41  pub fn day(&self) -> usize {
42    self.0.day() as usize
43  }
44
45  /// Formats the date according to the given format string.
46  pub fn format<'a>(&self, fmt: &'a str) -> impl Display + 'a {
47    self.0.format(fmt)
48  }
49
50  /// Returns the month of the year starting from `1`.
51  pub fn month(&self) -> usize {
52    self.0.month() as usize
53  }
54
55  /// Returns the next day.
56  pub fn next(&self) -> Self {
57    Self(self.0.succ())
58  }
59
60  /// Returns the previous day.
61  pub fn prev(&self) -> Self {
62    Self(self.0.pred())
63  }
64
65  /// Convert the date to a time in the local time zone.
66  pub fn to_local_time(&self) -> Time {
67    self.to_time(super::LOCAL)
68  }
69
70  /// Converts the date to a time in the given time zone.
71  pub fn to_time(&self, zone: Zone) -> Time {
72    self.and_hms(0, 0, 0, zone)
73  }
74
75  /// Convert the date to a time in UTC.
76  pub fn to_utc_time(&self) -> Time {
77    self.to_time(super::UTC)
78  }
79
80  /// Returns the year number.
81  pub fn year(&self) -> isize {
82    self.0.year() as isize
83  }
84
85  /// Returns the year, month of the year, and day of the month.
86  ///
87  /// Equivalent to `(date.year(), date.month(), date.day())`.
88  pub fn ymd(&self) -> (isize, usize, usize) {
89    (self.year(), self.month(), self.day())
90  }
91}
92
93// Implement formatting.
94
95impl Debug for Date {
96  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97    write!(f, "\"{}\"", self.format("%F"))
98  }
99}
100
101impl Display for Date {
102  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103    write!(f, "{}", self.format("%v"))
104  }
105}
106
107// Implement conversion to and from postgres.
108
109cfg_if! {
110  if #[cfg(feature = "postgres")] {
111    use postgres_types as pg;
112
113    impl<'a> pg::FromSql<'a> for Date {
114      fn from_sql(ty: &pg::Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + Sync + Send>>{
115        Ok(Self(pg::FromSql::from_sql(ty, raw)?))
116      }
117
118      fn accepts(ty: &pg::Type) -> bool {
119        <chrono::NaiveDate as pg::FromSql>::accepts(ty)
120      }
121    }
122
123    impl pg::ToSql for Date {
124      fn to_sql(&self, ty: &pg::Type, out: &mut bytes::BytesMut) -> Result<pg::IsNull, Box<dyn Error + Sync + Send>>
125      where
126        Self: Sized,
127      {
128        self.0.to_sql(ty, out)
129      }
130
131      fn accepts(ty: &pg::Type) -> bool
132      where
133        Self: Sized,
134      {
135        <chrono::NaiveDate as pg::ToSql>::accepts(ty)
136      }
137
138      fn to_sql_checked(&self, ty: &pg::Type, out: &mut bytes::BytesMut) -> Result<pg::IsNull, Box<dyn Error + Sync + Send>> {
139        self.0.to_sql_checked(ty, out)
140      }
141    }
142  }
143}