Skip to content

Instantly share code, notes, and snippets.

@jhpratt
Last active May 18, 2021 04:34
Show Gist options
  • Save jhpratt/76a2db05de5266be79b85416a7f43fd0 to your computer and use it in GitHub Desktop.
Save jhpratt/76a2db05de5266be79b85416a7f43fd0 to your computer and use it in GitHub Desktop.
time macros
#![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