• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::fmt;
2 
3 /// Release date including year, month, and day.
4 // Internal storage is: y[31..9] | m[8..5] | d[5...0].
5 #[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
6 pub struct Date(u32);
7 
8 impl Date {
9     /// Reads the release date of the running compiler. If it cannot be
10     /// determined (see the [top-level documentation](crate)), returns `None`.
11     ///
12     /// # Example
13     ///
14     /// ```rust
15     /// use version_check::Date;
16     ///
17     /// match Date::read() {
18     ///     Some(d) => format!("The release date is: {}", d),
19     ///     None => format!("Failed to read the release date.")
20     /// };
21     /// ```
read() -> Option<Date>22     pub fn read() -> Option<Date> {
23         ::get_version_and_date()
24             .and_then(|(_, date)| date)
25             .and_then(|date| Date::parse(&date))
26     }
27 
28     /// Parse a release date of the form `%Y-%m-%d`. Returns `None` if `date` is
29     /// not in `%Y-%m-%d` format.
30     ///
31     /// # Example
32     ///
33     /// ```rust
34     /// use version_check::Date;
35     ///
36     /// let date = Date::parse("2016-04-20").unwrap();
37     ///
38     /// assert!(date.at_least("2016-01-10"));
39     /// assert!(date.at_most("2016-04-20"));
40     /// assert!(date.exactly("2016-04-20"));
41     ///
42     /// assert!(Date::parse("2021-12-31").unwrap().exactly("2021-12-31"));
43     ///
44     /// assert!(Date::parse("March 13, 2018").is_none());
45     /// assert!(Date::parse("1-2-3-4-5").is_none());
46     /// assert!(Date::parse("2020-300-23120").is_none());
47     /// assert!(Date::parse("2020-12-12 1").is_none());
48     /// assert!(Date::parse("2020-10").is_none());
49     /// assert!(Date::parse("2020").is_none());
50     /// ```
parse(date: &str) -> Option<Date>51     pub fn parse(date: &str) -> Option<Date> {
52         let mut ymd = [0u16; 3];
53         for (i, split) in date.split('-').map(|s| s.parse::<u16>()).enumerate() {
54             ymd[i] = match (i, split) {
55                 (3, _) | (_, Err(_)) => return None,
56                 (_, Ok(v)) => v,
57             };
58         }
59 
60         let (year, month, day) = (ymd[0], ymd[1], ymd[2]);
61         if year == 0 || month == 0 || month > 12 || day == 0 || day > 31 {
62             return None;
63         }
64 
65         Some(Date::from_ymd(year, month as u8, day as u8))
66     }
67 
68     /// Creates a `Date` from `(year, month, day)` date components.
69     ///
70     /// Does not check the validity of `year`, `month`, or `day`, but `year` is
71     /// truncated to 23 bits (% 8,388,608), `month` to 4 bits (% 16), and `day`
72     /// to 5 bits (% 32).
73     ///
74     /// # Example
75     ///
76     /// ```rust
77     /// use version_check::Date;
78     ///
79     /// assert!(Date::from_ymd(2021, 7, 30).exactly("2021-07-30"));
80     /// assert!(Date::from_ymd(2010, 3, 23).exactly("2010-03-23"));
81     /// assert!(Date::from_ymd(2090, 1, 31).exactly("2090-01-31"));
82     ///
83     /// // Truncation: 33 % 32 == 0x21 & 0x1F == 1.
84     /// assert!(Date::from_ymd(2090, 1, 33).exactly("2090-01-01"));
85     /// ```
from_ymd(year: u16, month: u8, day: u8) -> Date86     pub fn from_ymd(year: u16, month: u8, day: u8) -> Date {
87         let year = (year as u32) << 9;
88         let month = ((month as u32) & 0xF) << 5;
89         let day = (day as u32) & 0x1F;
90         Date(year | month | day)
91     }
92 
93     /// Return the original (YYYY, MM, DD).
to_ymd(&self) -> (u16, u8, u8)94     fn to_ymd(&self) -> (u16, u8, u8) {
95         let y = self.0 >> 9;
96         let m = (self.0 >> 5) & 0xF;
97         let d = self.0 & 0x1F;
98         (y as u16, m as u8, d as u8)
99     }
100 
101     /// Returns `true` if `self` occurs on or after `date`.
102     ///
103     /// If `date` occurs before `self`, or if `date` is not in `%Y-%m-%d`
104     /// format, returns `false`.
105     ///
106     /// # Example
107     ///
108     /// ```rust
109     /// use version_check::Date;
110     ///
111     /// let date = Date::parse("2020-01-01").unwrap();
112     ///
113     /// assert!(date.at_least("2019-12-31"));
114     /// assert!(date.at_least("2020-01-01"));
115     /// assert!(date.at_least("2014-04-31"));
116     ///
117     /// assert!(!date.at_least("2020-01-02"));
118     /// assert!(!date.at_least("2024-08-18"));
119     /// ```
at_least(&self, date: &str) -> bool120     pub fn at_least(&self, date: &str) -> bool {
121         Date::parse(date)
122             .map(|date| self >= &date)
123             .unwrap_or(false)
124     }
125 
126     /// Returns `true` if `self` occurs on or before `date`.
127     ///
128     /// If `date` occurs after `self`, or if `date` is not in `%Y-%m-%d`
129     /// format, returns `false`.
130     ///
131     /// # Example
132     ///
133     /// ```rust
134     /// use version_check::Date;
135     ///
136     /// let date = Date::parse("2020-01-01").unwrap();
137     ///
138     /// assert!(date.at_most("2020-01-01"));
139     /// assert!(date.at_most("2020-01-02"));
140     /// assert!(date.at_most("2024-08-18"));
141     ///
142     /// assert!(!date.at_most("2019-12-31"));
143     /// assert!(!date.at_most("2014-04-31"));
144     /// ```
at_most(&self, date: &str) -> bool145     pub fn at_most(&self, date: &str) -> bool {
146         Date::parse(date)
147             .map(|date| self <= &date)
148             .unwrap_or(false)
149     }
150 
151     /// Returns `true` if `self` occurs exactly on `date`.
152     ///
153     /// If `date` is not exactly `self`, or if `date` is not in `%Y-%m-%d`
154     /// format, returns `false`.
155     ///
156     /// # Example
157     ///
158     /// ```rust
159     /// use version_check::Date;
160     ///
161     /// let date = Date::parse("2020-01-01").unwrap();
162     ///
163     /// assert!(date.exactly("2020-01-01"));
164     ///
165     /// assert!(!date.exactly("2019-12-31"));
166     /// assert!(!date.exactly("2014-04-31"));
167     /// assert!(!date.exactly("2020-01-02"));
168     /// assert!(!date.exactly("2024-08-18"));
169     /// ```
exactly(&self, date: &str) -> bool170     pub fn exactly(&self, date: &str) -> bool {
171         Date::parse(date)
172             .map(|date| self == &date)
173             .unwrap_or(false)
174     }
175 }
176 
177 impl fmt::Display for Date {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result178     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
179         let (y, m, d) = self.to_ymd();
180         write!(f, "{}-{:02}-{:02}", y, m, d)
181     }
182 }
183 
184 #[cfg(test)]
185 mod tests {
186     use super::Date;
187 
188     macro_rules! reflexive_display {
189         ($string:expr) => (
190             assert_eq!(Date::parse($string).unwrap().to_string(), $string);
191         )
192     }
193 
194     #[test]
display()195     fn display() {
196         reflexive_display!("2019-05-08");
197         reflexive_display!("2000-01-01");
198         reflexive_display!("2000-12-31");
199         reflexive_display!("2090-12-31");
200         reflexive_display!("1999-02-19");
201         reflexive_display!("9999-12-31");
202     }
203 }
204