16acc7838Sopenharmony_ciuse std::fmt; 26acc7838Sopenharmony_ci 36acc7838Sopenharmony_ci/// Release date including year, month, and day. 46acc7838Sopenharmony_ci// Internal storage is: y[31..9] | m[8..5] | d[5...0]. 56acc7838Sopenharmony_ci#[derive(Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)] 66acc7838Sopenharmony_cipub struct Date(u32); 76acc7838Sopenharmony_ci 86acc7838Sopenharmony_ciimpl Date { 96acc7838Sopenharmony_ci /// Reads the release date of the running compiler. If it cannot be 106acc7838Sopenharmony_ci /// determined (see the [top-level documentation](crate)), returns `None`. 116acc7838Sopenharmony_ci /// 126acc7838Sopenharmony_ci /// # Example 136acc7838Sopenharmony_ci /// 146acc7838Sopenharmony_ci /// ```rust 156acc7838Sopenharmony_ci /// use version_check::Date; 166acc7838Sopenharmony_ci /// 176acc7838Sopenharmony_ci /// match Date::read() { 186acc7838Sopenharmony_ci /// Some(d) => format!("The release date is: {}", d), 196acc7838Sopenharmony_ci /// None => format!("Failed to read the release date.") 206acc7838Sopenharmony_ci /// }; 216acc7838Sopenharmony_ci /// ``` 226acc7838Sopenharmony_ci pub fn read() -> Option<Date> { 236acc7838Sopenharmony_ci ::get_version_and_date() 246acc7838Sopenharmony_ci .and_then(|(_, date)| date) 256acc7838Sopenharmony_ci .and_then(|date| Date::parse(&date)) 266acc7838Sopenharmony_ci } 276acc7838Sopenharmony_ci 286acc7838Sopenharmony_ci /// Parse a release date of the form `%Y-%m-%d`. Returns `None` if `date` is 296acc7838Sopenharmony_ci /// not in `%Y-%m-%d` format. 306acc7838Sopenharmony_ci /// 316acc7838Sopenharmony_ci /// # Example 326acc7838Sopenharmony_ci /// 336acc7838Sopenharmony_ci /// ```rust 346acc7838Sopenharmony_ci /// use version_check::Date; 356acc7838Sopenharmony_ci /// 366acc7838Sopenharmony_ci /// let date = Date::parse("2016-04-20").unwrap(); 376acc7838Sopenharmony_ci /// 386acc7838Sopenharmony_ci /// assert!(date.at_least("2016-01-10")); 396acc7838Sopenharmony_ci /// assert!(date.at_most("2016-04-20")); 406acc7838Sopenharmony_ci /// assert!(date.exactly("2016-04-20")); 416acc7838Sopenharmony_ci /// 426acc7838Sopenharmony_ci /// assert!(Date::parse("2021-12-31").unwrap().exactly("2021-12-31")); 436acc7838Sopenharmony_ci /// 446acc7838Sopenharmony_ci /// assert!(Date::parse("March 13, 2018").is_none()); 456acc7838Sopenharmony_ci /// assert!(Date::parse("1-2-3-4-5").is_none()); 466acc7838Sopenharmony_ci /// assert!(Date::parse("2020-300-23120").is_none()); 476acc7838Sopenharmony_ci /// assert!(Date::parse("2020-12-12 1").is_none()); 486acc7838Sopenharmony_ci /// assert!(Date::parse("2020-10").is_none()); 496acc7838Sopenharmony_ci /// assert!(Date::parse("2020").is_none()); 506acc7838Sopenharmony_ci /// ``` 516acc7838Sopenharmony_ci pub fn parse(date: &str) -> Option<Date> { 526acc7838Sopenharmony_ci let mut ymd = [0u16; 3]; 536acc7838Sopenharmony_ci for (i, split) in date.split('-').map(|s| s.parse::<u16>()).enumerate() { 546acc7838Sopenharmony_ci ymd[i] = match (i, split) { 556acc7838Sopenharmony_ci (3, _) | (_, Err(_)) => return None, 566acc7838Sopenharmony_ci (_, Ok(v)) => v, 576acc7838Sopenharmony_ci }; 586acc7838Sopenharmony_ci } 596acc7838Sopenharmony_ci 606acc7838Sopenharmony_ci let (year, month, day) = (ymd[0], ymd[1], ymd[2]); 616acc7838Sopenharmony_ci if year == 0 || month == 0 || month > 12 || day == 0 || day > 31 { 626acc7838Sopenharmony_ci return None; 636acc7838Sopenharmony_ci } 646acc7838Sopenharmony_ci 656acc7838Sopenharmony_ci Some(Date::from_ymd(year, month as u8, day as u8)) 666acc7838Sopenharmony_ci } 676acc7838Sopenharmony_ci 686acc7838Sopenharmony_ci /// Creates a `Date` from `(year, month, day)` date components. 696acc7838Sopenharmony_ci /// 706acc7838Sopenharmony_ci /// Does not check the validity of `year`, `month`, or `day`, but `year` is 716acc7838Sopenharmony_ci /// truncated to 23 bits (% 8,388,608), `month` to 4 bits (% 16), and `day` 726acc7838Sopenharmony_ci /// to 5 bits (% 32). 736acc7838Sopenharmony_ci /// 746acc7838Sopenharmony_ci /// # Example 756acc7838Sopenharmony_ci /// 766acc7838Sopenharmony_ci /// ```rust 776acc7838Sopenharmony_ci /// use version_check::Date; 786acc7838Sopenharmony_ci /// 796acc7838Sopenharmony_ci /// assert!(Date::from_ymd(2021, 7, 30).exactly("2021-07-30")); 806acc7838Sopenharmony_ci /// assert!(Date::from_ymd(2010, 3, 23).exactly("2010-03-23")); 816acc7838Sopenharmony_ci /// assert!(Date::from_ymd(2090, 1, 31).exactly("2090-01-31")); 826acc7838Sopenharmony_ci /// 836acc7838Sopenharmony_ci /// // Truncation: 33 % 32 == 0x21 & 0x1F == 1. 846acc7838Sopenharmony_ci /// assert!(Date::from_ymd(2090, 1, 33).exactly("2090-01-01")); 856acc7838Sopenharmony_ci /// ``` 866acc7838Sopenharmony_ci pub fn from_ymd(year: u16, month: u8, day: u8) -> Date { 876acc7838Sopenharmony_ci let year = (year as u32) << 9; 886acc7838Sopenharmony_ci let month = ((month as u32) & 0xF) << 5; 896acc7838Sopenharmony_ci let day = (day as u32) & 0x1F; 906acc7838Sopenharmony_ci Date(year | month | day) 916acc7838Sopenharmony_ci } 926acc7838Sopenharmony_ci 936acc7838Sopenharmony_ci /// Return the original (YYYY, MM, DD). 946acc7838Sopenharmony_ci fn to_ymd(&self) -> (u16, u8, u8) { 956acc7838Sopenharmony_ci let y = self.0 >> 9; 966acc7838Sopenharmony_ci let m = (self.0 >> 5) & 0xF; 976acc7838Sopenharmony_ci let d = self.0 & 0x1F; 986acc7838Sopenharmony_ci (y as u16, m as u8, d as u8) 996acc7838Sopenharmony_ci } 1006acc7838Sopenharmony_ci 1016acc7838Sopenharmony_ci /// Returns `true` if `self` occurs on or after `date`. 1026acc7838Sopenharmony_ci /// 1036acc7838Sopenharmony_ci /// If `date` occurs before `self`, or if `date` is not in `%Y-%m-%d` 1046acc7838Sopenharmony_ci /// format, returns `false`. 1056acc7838Sopenharmony_ci /// 1066acc7838Sopenharmony_ci /// # Example 1076acc7838Sopenharmony_ci /// 1086acc7838Sopenharmony_ci /// ```rust 1096acc7838Sopenharmony_ci /// use version_check::Date; 1106acc7838Sopenharmony_ci /// 1116acc7838Sopenharmony_ci /// let date = Date::parse("2020-01-01").unwrap(); 1126acc7838Sopenharmony_ci /// 1136acc7838Sopenharmony_ci /// assert!(date.at_least("2019-12-31")); 1146acc7838Sopenharmony_ci /// assert!(date.at_least("2020-01-01")); 1156acc7838Sopenharmony_ci /// assert!(date.at_least("2014-04-31")); 1166acc7838Sopenharmony_ci /// 1176acc7838Sopenharmony_ci /// assert!(!date.at_least("2020-01-02")); 1186acc7838Sopenharmony_ci /// assert!(!date.at_least("2024-08-18")); 1196acc7838Sopenharmony_ci /// ``` 1206acc7838Sopenharmony_ci pub fn at_least(&self, date: &str) -> bool { 1216acc7838Sopenharmony_ci Date::parse(date) 1226acc7838Sopenharmony_ci .map(|date| self >= &date) 1236acc7838Sopenharmony_ci .unwrap_or(false) 1246acc7838Sopenharmony_ci } 1256acc7838Sopenharmony_ci 1266acc7838Sopenharmony_ci /// Returns `true` if `self` occurs on or before `date`. 1276acc7838Sopenharmony_ci /// 1286acc7838Sopenharmony_ci /// If `date` occurs after `self`, or if `date` is not in `%Y-%m-%d` 1296acc7838Sopenharmony_ci /// format, returns `false`. 1306acc7838Sopenharmony_ci /// 1316acc7838Sopenharmony_ci /// # Example 1326acc7838Sopenharmony_ci /// 1336acc7838Sopenharmony_ci /// ```rust 1346acc7838Sopenharmony_ci /// use version_check::Date; 1356acc7838Sopenharmony_ci /// 1366acc7838Sopenharmony_ci /// let date = Date::parse("2020-01-01").unwrap(); 1376acc7838Sopenharmony_ci /// 1386acc7838Sopenharmony_ci /// assert!(date.at_most("2020-01-01")); 1396acc7838Sopenharmony_ci /// assert!(date.at_most("2020-01-02")); 1406acc7838Sopenharmony_ci /// assert!(date.at_most("2024-08-18")); 1416acc7838Sopenharmony_ci /// 1426acc7838Sopenharmony_ci /// assert!(!date.at_most("2019-12-31")); 1436acc7838Sopenharmony_ci /// assert!(!date.at_most("2014-04-31")); 1446acc7838Sopenharmony_ci /// ``` 1456acc7838Sopenharmony_ci pub fn at_most(&self, date: &str) -> bool { 1466acc7838Sopenharmony_ci Date::parse(date) 1476acc7838Sopenharmony_ci .map(|date| self <= &date) 1486acc7838Sopenharmony_ci .unwrap_or(false) 1496acc7838Sopenharmony_ci } 1506acc7838Sopenharmony_ci 1516acc7838Sopenharmony_ci /// Returns `true` if `self` occurs exactly on `date`. 1526acc7838Sopenharmony_ci /// 1536acc7838Sopenharmony_ci /// If `date` is not exactly `self`, or if `date` is not in `%Y-%m-%d` 1546acc7838Sopenharmony_ci /// format, returns `false`. 1556acc7838Sopenharmony_ci /// 1566acc7838Sopenharmony_ci /// # Example 1576acc7838Sopenharmony_ci /// 1586acc7838Sopenharmony_ci /// ```rust 1596acc7838Sopenharmony_ci /// use version_check::Date; 1606acc7838Sopenharmony_ci /// 1616acc7838Sopenharmony_ci /// let date = Date::parse("2020-01-01").unwrap(); 1626acc7838Sopenharmony_ci /// 1636acc7838Sopenharmony_ci /// assert!(date.exactly("2020-01-01")); 1646acc7838Sopenharmony_ci /// 1656acc7838Sopenharmony_ci /// assert!(!date.exactly("2019-12-31")); 1666acc7838Sopenharmony_ci /// assert!(!date.exactly("2014-04-31")); 1676acc7838Sopenharmony_ci /// assert!(!date.exactly("2020-01-02")); 1686acc7838Sopenharmony_ci /// assert!(!date.exactly("2024-08-18")); 1696acc7838Sopenharmony_ci /// ``` 1706acc7838Sopenharmony_ci pub fn exactly(&self, date: &str) -> bool { 1716acc7838Sopenharmony_ci Date::parse(date) 1726acc7838Sopenharmony_ci .map(|date| self == &date) 1736acc7838Sopenharmony_ci .unwrap_or(false) 1746acc7838Sopenharmony_ci } 1756acc7838Sopenharmony_ci} 1766acc7838Sopenharmony_ci 1776acc7838Sopenharmony_ciimpl fmt::Display for Date { 1786acc7838Sopenharmony_ci fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 1796acc7838Sopenharmony_ci let (y, m, d) = self.to_ymd(); 1806acc7838Sopenharmony_ci write!(f, "{}-{:02}-{:02}", y, m, d) 1816acc7838Sopenharmony_ci } 1826acc7838Sopenharmony_ci} 1836acc7838Sopenharmony_ci 1846acc7838Sopenharmony_ci#[cfg(test)] 1856acc7838Sopenharmony_cimod tests { 1866acc7838Sopenharmony_ci use super::Date; 1876acc7838Sopenharmony_ci 1886acc7838Sopenharmony_ci macro_rules! reflexive_display { 1896acc7838Sopenharmony_ci ($string:expr) => ( 1906acc7838Sopenharmony_ci assert_eq!(Date::parse($string).unwrap().to_string(), $string); 1916acc7838Sopenharmony_ci ) 1926acc7838Sopenharmony_ci } 1936acc7838Sopenharmony_ci 1946acc7838Sopenharmony_ci #[test] 1956acc7838Sopenharmony_ci fn display() { 1966acc7838Sopenharmony_ci reflexive_display!("2019-05-08"); 1976acc7838Sopenharmony_ci reflexive_display!("2000-01-01"); 1986acc7838Sopenharmony_ci reflexive_display!("2000-12-31"); 1996acc7838Sopenharmony_ci reflexive_display!("2090-12-31"); 2006acc7838Sopenharmony_ci reflexive_display!("1999-02-19"); 2016acc7838Sopenharmony_ci reflexive_display!("9999-12-31"); 2026acc7838Sopenharmony_ci } 2036acc7838Sopenharmony_ci} 204