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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
//! Contains a basic [`Date`] type used for differentiating nightly versions of rust.
//!
//! Intentionally ignores timezone information, making it simpler than the [`time` crate]
//!
//! [`time` crate]: https://github.com/time-rs/time
use core::fmt::{self, Display};
/// Indicates the date.
///
/// Used for the nightly versions of rust.
///
/// The timezone is not explicitly specified here,
/// and matches whatever one the rust team uses for nightly releases.
///
/// Can be created by the [`date!`](crate::date!) macro.
/// For example `date!(2014-12-31)`
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Date {
/// The year
pub year: u16,
/// The month
pub month: u8,
/// The day of the month.
pub day: u8,
}
impl Date {
/// Create a date, using YYYY-MM-DD format (ISO 8601).
///
/// For example, `Date::from_iso(2018-
///
/// If possible, perform some basic validation.
#[inline]
#[cfg_attr(has_track_caller, track_caller)]
pub const fn new(year: u16, month: u8, day: u8) -> Self {
#[cfg(has_const_panic)]
{
assert!(year >= 1, "Invalid year");
assert!(month >= 1 && month <= 12, "Invalid month");
assert!(day >= 1 && day <= 31, "Invalid day of month");
}
Date { year, month, day }
}
/// Check if this date is equal to or after the specified start.
///
/// Equivalent to `self >= start`,
/// but available as a `const` function.
///
/// ## Example
/// ```
/// # use rustversion_detect::date;;
///
/// assert!(date!(2024-11-16).is_since(date!(2024-7-28)));
/// ```
#[inline]
pub const fn is_since(&self, start: Date) -> bool {
self.year > start.year
|| (self.year == start.year
&& (self.month > start.month
|| (self.month == start.month && self.day >= start.day)))
}
/// Check if this date is before the specified end.
///
/// Equivalent to `self < end`,
/// but available as a `const` function.
///
/// ## Example
/// ```
/// # use rustversion_detect::date;
///
/// assert!(date!(2018-12-14).is_before(date!(2022-8-16)));
/// assert!(date!(2024-11-14).is_before(date!(2024-12-7)));
/// assert!(date!(2024-11-14).is_before(date!(2024-11-17)));
/// ```
#[inline]
pub const fn is_before(&self, end: Date) -> bool {
!self.is_since(end)
}
}
/// Declare a [`Date`] using the YYYY-MM-DD format (ISO 8601).
#[macro_export]
macro_rules! date {
($year:literal - $month:literal - $day:literal) => {{
// NOTE: The Date::new function sometimes perfroms validation
// It only validates if `const_panic` is stablized.
const DTE: $crate::date::Date = $crate::date::Date::new($year, $month, $day);
DTE
}};
}
/// Displays the date consistent with the ISO 8601 standard.
impl Display for Date {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(
formatter,
"{:04}-{:02}-{:02}",
self.year, self.month, self.day,
)
}
}
#[cfg(test)]
mod test {
use crate::Date;
// (before, after)
const TEST_DATES: &[(Date, Date)] = &[
(date!(2018 - 12 - 14), date!(2022 - 8 - 16)),
(date!(2024 - 11 - 14), date!(2024 - 12 - 7)),
(date!(2024 - 11 - 14), date!(2024 - 11 - 17)),
];
#[test]
fn test_before_after() {
for &(before, after) in TEST_DATES {
assert!(before.is_before(after), "{} & {}", before, after);
assert!(after.is_since(before), "{} & {}", before, after);
// check equal dates
for date in [before, after] {
assert!(date.is_since(date), "{}", date);
assert!(!date.is_before(date), "{}", date);
}
}
}
#[test]
#[cfg_attr(has_const_panic, should_panic(expected = "Invalid year"))]
fn test_invalid_year() {
Date::new(0, 7, 18);
}
#[test]
#[cfg_attr(has_const_panic, should_panic(expected = "Invalid month"))]
fn test_invalid_month() {
Date::new(2014, 13, 18);
}
#[test]
#[cfg_attr(has_const_panic, should_panic(expected = "Invalid day of month"))]
fn test_invalid_date() {
Date::new(2014, 7, 36);
}
}