-
-
Save jhpratt/76a2db05de5266be79b85416a7f43fd0 to your computer and use it in GitHub Desktop.
time macros
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(const_panic)] | |
pub use time::{util, Date, Time, UtcOffset}; | |
#[doc(hidden)] | |
#[macro_export] | |
macro_rules! attach_sign { | |
(+ $($val:tt)*) => ($($val)*); | |
(- $($val:tt)*) => (-$($val)*); | |
} | |
#[doc(hidden)] | |
#[macro_export] | |
macro_rules! range_error { | |
($name:literal, -$min:literal, $max:literal $(, $($extra:tt)*)?) => { | |
::core::panic!(::core::concat!( | |
$name, " must be in range -", $min, "..=", $max, | |
$(" ", $($extra)*)? | |
)); | |
}; | |
($name:literal, $min:literal, $max:literal $(, $($extra:tt)*)?) => { | |
::core::panic!(::core::concat!( | |
$name, " must be in range ", $min, "..=", $max, | |
$(" ", $($extra)*)? | |
)); | |
}; | |
} | |
#[doc(hidden)] | |
#[macro_export] | |
macro_rules! validate { | |
($name:literal, $value:expr, -$min:literal, $max:literal $(, $($extra:tt)*)?) => {{ | |
#[allow(unused_comparisons)] | |
if $value < -$min || $value > $max { | |
$crate::range_error!($name, -$min, $max $(, $($extra)*)?); | |
} | |
$value | |
}}; | |
($name:literal, $value:expr, $min:literal, $max:literal $(, $($extra:tt)*)?) => {{ | |
#[allow(unused_comparisons)] | |
if $value < $min || $value > $max { | |
$crate::range_error!($name, $min, $max $(, $($extra)*)?); | |
} | |
$value | |
}}; | |
} | |
#[rustfmt::skip] | |
#[doc(hidden)] | |
#[macro_export] | |
macro_rules! validate_and_convert_hour { | |
($hour:tt) => ($crate::validate!("hour", $hour, 0, 23, "(was ", $hour, ")")); | |
(12 am) => (0); | |
(12 pm) => (12); | |
($hour:tt am) => ($crate::validate!( | |
"hour", $hour, 1, 12, | |
"when am is specified", " (was ", $hour, ")" | |
)); | |
($hour:tt pm) => ($crate::validate!( | |
"hour", $hour, 1, 12, | |
"when pm is specified", " (was ", $hour, ")" | |
)); | |
} | |
#[rustfmt::skip] | |
#[doc(hidden)] | |
#[macro_export] | |
macro_rules! value_or_zero { | |
() => (0); | |
($value:tt) => ($value); | |
} | |
#[macro_export] | |
macro_rules! offset { | |
(UTC) => ($crate::UtcOffset::UTC); | |
($sign:tt $hours:tt $(: $minutes:tt $(: $seconds:tt)?)?) => {{ | |
#[forbid(const_err)] | |
#[allow(clippy::zero_prefixed_literal)] | |
const OFFSET: $crate::UtcOffset= $crate::UtcOffset::__from_hms_unchecked( | |
$crate::validate!( | |
"hour", $crate::attach_sign!($sign $hours), -23, 23, | |
"(was ", ::core::stringify!($sign), $hours, ")" | |
), | |
$crate::attach_sign!($sign $crate::validate!( | |
"minute", $crate::value_or_zero!($($minutes)?), 0, 59, | |
"(was ", $crate::value_or_zero!($($minutes)?), ")" | |
)), | |
$crate::attach_sign!($sign $crate::validate!( | |
"second", $crate::value_or_zero!($($($seconds)?)?), 0, 59, | |
"(was ", $crate::value_or_zero!($($($seconds)?)?), ")" | |
)), | |
); | |
OFFSET | |
}}; | |
} | |
#[rustfmt::skip] | |
#[doc(hidden)] | |
#[macro_export] | |
macro_rules! time_internal { | |
($hour:expr, $minute:tt, $second_subsecond:expr) => {{ | |
#[forbid(const_err)] | |
#[allow(clippy::zero_prefixed_literal)] | |
const TIME: $crate::Time = $crate::Time::__from_hms_nanos_unchecked( | |
$hour, | |
$crate::validate!("minute", $minute, 0, 59, "(was ", $minute, ")"), | |
$crate::validate!("second", $second_subsecond as ::core::primitive::f64, 0., 60.) as _, | |
($second_subsecond as ::core::primitive::f64 % 1. * 1_000_000_000.) as _, | |
); | |
TIME | |
}}; | |
} | |
#[macro_export] | |
macro_rules! time { | |
($hour:tt : $minute:tt $(: $second_subsecond:tt)? am) => ($crate::time_internal!( | |
$crate::validate_and_convert_hour!($hour am), | |
$minute, | |
$crate::value_or_zero!($($second_subsecond)?) | |
)); | |
($hour:tt : $minute:tt $(: $second_subsecond:tt)? pm) => ($crate::time_internal!( | |
$crate::validate_and_convert_hour!($hour pm), | |
$minute, | |
$crate::value_or_zero!($($second_subsecond)?) | |
)); | |
($hour:tt : $minute:tt $(: $second_subsecond:tt)?) => ($crate::time_internal!( | |
$crate::validate_and_convert_hour!($hour), | |
$minute, | |
$crate::value_or_zero!($($second_subsecond)?) | |
)); | |
} | |
#[cfg(feature = "large-dates")] | |
#[doc(hidden)] | |
#[macro_export] | |
macro_rules! validate_year { | |
($sign:tt $year:tt) => ($crate::validate!( | |
"year", $crate::attach_sign!($sign $year), -999_999, 999_999, | |
"(was ", ::core::stringify!($sign), $year, ")" | |
)); | |
} | |
#[cfg(not(feature = "large-dates"))] | |
#[doc(hidden)] | |
#[macro_export] | |
macro_rules! validate_year { | |
($sign:tt $year:tt) => ($crate::validate!( | |
"year", $crate::attach_sign!($sign $year), -9999, 9999, | |
"(was ", ::core::stringify!($sign), $year, ")" | |
)); | |
} | |
#[macro_export] | |
macro_rules! date { | |
($year:tt - $ordinal:tt) => {{ | |
const _: () = if $year > 9999 { | |
::core::panic!("years with more than four digits must have an explicit sign"); | |
}; | |
$crate::date!(+ $year - $ordinal) | |
}}; | |
($sign:tt $year:tt - $ordinal:tt) => {{ | |
#[forbid(const_err)] | |
#[allow(clippy::zero_prefixed_literal)] | |
const DATE: $crate::Date = { | |
let year: i32 = $crate::validate_year!($sign $year); | |
if $ordinal > $crate::util::days_in_year(year) { | |
if $crate::util::is_leap_year(year) { | |
$crate::range_error!( | |
"ordinal", 1, 366, | |
"(", ::core::stringify!($sign), $year, " is a leap year)", | |
" (was ", $ordinal, ")" | |
); | |
} else { | |
$crate::range_error!( | |
"ordinal", 1, 365, | |
"(", ::core::stringify!($sign), $year, " is not a leap year)", | |
" (was ", $ordinal, ")" | |
); | |
} | |
} | |
$crate::Date::__from_ordinal_date_unchecked(year, $ordinal) | |
}; | |
DATE | |
}}; | |
($year:tt - $month:tt - $day:tt) => {{ | |
const _: () = if $year > 9999 { | |
::core::panic!("years with more than four digits must have an explicit sign"); | |
}; | |
$crate::date!(+ $year - $month - $day) | |
}}; | |
($sign:tt $year:tt - $month:tt - $day:tt) => {{ | |
#[forbid(const_err)] | |
#[allow(clippy::zero_prefixed_literal)] | |
const DATE: $crate::Date = { | |
let year: i32 = $crate::validate_year!($sign $year); | |
if $month == 0 || $month > 12 { | |
$crate::range_error!("month", 1, 12, "(was ", $month, ")"); | |
} | |
if $day == 0 || $day > 29 && $month == 2 && $crate::util::is_leap_year(year) { | |
$crate::range_error!("day", 1, 29, "for given month", " (was ", $day, ")"); | |
} | |
if $day == 0 || $day > 28 && $month == 2 { | |
$crate::range_error!("day", 1, 28, "for given month", " (was ", $day, ")"); | |
} | |
if $day == 0 || $day > 31 && matches!($month, 1 | 3 | 5 | 7 | 8 | 10 | 12) { | |
$crate::range_error!("day", 1, 31, "for given month", " (was ", $day, ")"); | |
} | |
if $day == 0 || $day > 30 && matches!($month, 4 | 6 | 9 | 11) { | |
$crate::range_error!("day", 1, 30, "for given month", " (was ", $day, ")"); | |
} | |
let days_cumulative_common_leap = [ | |
[0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], | |
[0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335], | |
]; | |
$crate::Date::__from_ordinal_date_unchecked( | |
year, | |
days_cumulative_common_leap[$crate::util::is_leap_year(year) as usize][$month - 1] | |
+ $day | |
) | |
}; | |
DATE | |
}}; | |
} | |
#[macro_export] | |
macro_rules! datetime { | |
($year:tt - $month:tt - $day:tt $($rest:tt)*) => { | |
$crate::datetime!(@time $crate::date!($year - $month - $day); $($rest)*) | |
}; | |
($sign:tt $year:tt - $month:tt - $day:tt $($rest:tt)*) => { | |
$crate::datetime!(@time $crate::date!($sign $year - $month - $day); $($rest)*) | |
}; | |
($year:tt - $ordinal:tt $($rest:tt)*) => { | |
$crate::datetime!(@time $crate::date!($year - $ordinal); $($rest)*) | |
}; | |
($sign:tt $year:tt - $ordinal:tt $($rest:tt)*) => { | |
$crate::datetime!(@time $crate::date!($year - $ordinal); $($rest)*) | |
}; | |
(@time $date:expr; $hour:tt : $minute:tt $(: $second_subsecond:tt)?am $($rest:tt)*) => { | |
$crate::datetime!( | |
@offset $date; $crate::time!($hour : $minute $(: $second_subsecond)? am); $($rest)* | |
) | |
}; | |
(@time $date:expr; $hour:tt : $minute:tt $(: $second_subsecond:tt)?pm $($rest:tt)*) => { | |
$crate::datetime!( | |
@offset $date; $crate::time!($hour : $minute $(: $second_subsecond)? pm); $($rest)* | |
) | |
}; | |
(@time $date:expr; $hour:tt : $minute:tt : $second_subsecond:tt $($rest:tt)*) => { | |
$crate::datetime!( | |
@offset $date; $crate::time!($hour : $minute : $second_subsecond); $($rest)* | |
) | |
}; | |
(@time[$date:expr] $hour:tt : $minute:tt $($rest:tt)*) => { | |
$crate::datetime!(@offset $date; $crate::time!($hour : $minute); $($rest)*) | |
}; | |
(@offset $date:expr; $time:expr;) => { | |
$date.with_time($time) | |
}; | |
(@offset $date:expr; $time:expr; UTC) => { | |
$date.with_time($time).assume_utc() | |
}; | |
(@offset $date:expr; $time:expr; $sign:tt $hours:tt $(: $minutes:tt $(: $seconds:tt)?)?) => { | |
$date | |
.with_time($time) | |
.assume_offset($crate::offset!($sign $hours $(: $minutes $(: $seconds)?)?)) | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment