Skip to content

Commit 0cb1204

Browse files
committed
Add initialization macros
1 parent e74dcba commit 0cb1204

2 files changed

Lines changed: 208 additions & 0 deletions

File tree

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,8 @@ pub use duration::Duration;
479479
#[cfg(feature = "std")]
480480
pub use duration::OutOfRangeError;
481481

482+
mod macros;
483+
482484
use core::fmt;
483485

484486
#[cfg(feature = "__doctest")]

src/macros.rs

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
//! Macro's for easy initialization of date and time values.
2+
3+
/// Create a `NaiveDate` with a statically known value.
4+
///
5+
/// Supported formats are 'year-month-day' and 'year-ordinal'.
6+
///
7+
/// Note: rustfmt wants to add spaces around `-` in this macro.
8+
/// For nice formatting use `#[rustfmt::skip::macros(date)]`, or use as `date! {2023-09-08}`
9+
///
10+
/// # Examples
11+
/// ```
12+
/// use chrono::date;
13+
///
14+
/// assert_eq!(date!(2023-09-08), date!(2023-251));
15+
/// ```
16+
#[macro_export]
17+
macro_rules! date {
18+
($y:literal-$m:literal-$d:literal) => {{
19+
#[allow(clippy::zero_prefixed_literal)]
20+
match $crate::NaiveDate::from_ymd_opt($y, $m, $d) {
21+
Some(d) => d,
22+
None => panic!("invalid calendar date"),
23+
}
24+
}};
25+
($y:literal-$o:literal) => {{
26+
#[allow(clippy::zero_prefixed_literal)]
27+
match $crate::NaiveDate::from_yo_opt($y, $o) {
28+
Some(d) => d,
29+
None => panic!("invalid ordinal date"),
30+
}
31+
}};
32+
}
33+
34+
/// Create a `NaiveTime` with a statically known value.
35+
///
36+
/// Supported formats are 'hour:minute' and 'hour:minute:second'.
37+
///
38+
/// # Examples
39+
/// ```
40+
/// use chrono::time;
41+
/// # use chrono::Timelike;
42+
///
43+
/// assert_eq!(time!(7:03), time!(7:03:00));
44+
/// let leap_second = time!(23:59:60);
45+
/// # assert!(leap_second.second() == 59 && leap_second.nanosecond() == 1_000_000_000);
46+
/// ```
47+
#[macro_export]
48+
macro_rules! time {
49+
($h:literal:$m:literal:$s:literal) => {{
50+
#[allow(clippy::zero_prefixed_literal)]
51+
{
52+
let (s, nanos) = match $s {
53+
60u32 => (59, 1_000_000_000),
54+
s => (s, 0),
55+
};
56+
match $crate::NaiveTime::from_hms_nano_opt($h, $m, s, nanos) {
57+
Some(t) => t,
58+
None => panic!("invalid time"),
59+
}
60+
}
61+
}};
62+
($h:literal:$m:literal) => {{
63+
#[allow(clippy::zero_prefixed_literal)]
64+
match $crate::NaiveTime::from_hms_opt($h, $m, 0) {
65+
Some(t) => t,
66+
None => panic!("invalid time"),
67+
}
68+
}};
69+
}
70+
71+
/// Create a `NaiveDateTime` or `DateTime<FixedOffset>` with a statically known value.
72+
///
73+
/// # Examples
74+
/// ```
75+
/// use chrono::datetime;
76+
///
77+
/// // NaiveDateTime
78+
/// let _ = datetime!(2023-09-08 7:03);
79+
/// let _ = datetime!(2023-09-08 7:03:25);
80+
/// // DateTime<FixedOffset>
81+
/// let _ = datetime!(2023-09-08 7:03:25+02:00);
82+
/// let _ = datetime!(2023-09-08 7:03:25-02:00);
83+
/// ```
84+
#[macro_export]
85+
macro_rules! datetime {
86+
($y:literal-$m:literal-$d:literal $h:literal:$min:literal:$s:literal+$hh:literal:$mm:literal) => {{
87+
#[allow(clippy::zero_prefixed_literal)]
88+
{
89+
let date = match $crate::NaiveDate::from_ymd_opt($y, $m, $d) {
90+
Some(d) => d,
91+
None => panic!("invalid calendar date"),
92+
};
93+
let (s, nanos) = match $s {
94+
60u32 => (59, 1_000_000_000),
95+
s => (s, 0),
96+
};
97+
let time = match $crate::NaiveTime::from_hms_nano_opt($h, $min, s, nanos) {
98+
Some(t) => t,
99+
None => panic!("invalid time"),
100+
};
101+
assert!($hh < 24u32 || $mm < 60, "invalid offset");
102+
let offset = match $crate::FixedOffset::east_opt(($hh * 3600 + $mm * 60) as i32) {
103+
Some(o) => o,
104+
None => panic!("invalid offset"),
105+
};
106+
let dt = match date.and_time(time).checked_sub_offset(offset) {
107+
Some(o) => o,
108+
None => panic!("datetime out of range"),
109+
};
110+
$crate::DateTime::<$crate::FixedOffset>::from_naive_utc_and_offset(dt, offset)
111+
}
112+
}};
113+
($y:literal-$m:literal-$d:literal $h:literal:$min:literal:$s:literal-$hh:literal:$mm:literal) => {{
114+
#[allow(clippy::zero_prefixed_literal)]
115+
{
116+
let date = match $crate::NaiveDate::from_ymd_opt($y, $m, $d) {
117+
Some(d) => d,
118+
None => panic!("invalid calendar date"),
119+
};
120+
let (s, nanos) = match $s {
121+
60u32 => (59, 1_000_000_000),
122+
s => (s, 0),
123+
};
124+
let time = match $crate::NaiveTime::from_hms_nano_opt($h, $min, s, nanos) {
125+
Some(t) => t,
126+
None => panic!("invalid time"),
127+
};
128+
assert!($hh < 24u32 || $mm < 60, "invalid offset");
129+
let offset = match $crate::FixedOffset::west_opt(($hh * 3600 + $mm * 60) as i32) {
130+
Some(o) => o,
131+
None => panic!("invalid offset"),
132+
};
133+
let dt = match date.and_time(time).checked_sub_offset(offset) {
134+
Some(o) => o,
135+
None => panic!("datetime out of range"),
136+
};
137+
$crate::DateTime::<$crate::FixedOffset>::from_naive_utc_and_offset(dt, offset)
138+
}
139+
}};
140+
($y:literal-$m:literal-$d:literal $h:literal:$min:literal:$s:literal) => {{
141+
#[allow(clippy::zero_prefixed_literal)]
142+
{
143+
let date = match $crate::NaiveDate::from_ymd_opt($y, $m, $d) {
144+
Some(d) => d,
145+
None => panic!("invalid calendar date"),
146+
};
147+
let (s, nanos) = match $s {
148+
60u32 => (59, 1_000_000_000),
149+
s => (s, 0),
150+
};
151+
let time = match $crate::NaiveTime::from_hms_nano_opt($h, $min, s, nanos) {
152+
Some(t) => t,
153+
None => panic!("invalid time"),
154+
};
155+
date.and_time(time)
156+
}
157+
}};
158+
($y:literal-$m:literal-$d:literal $h:literal:$min:literal) => {{
159+
#[allow(clippy::zero_prefixed_literal)]
160+
{
161+
let date = match $crate::NaiveDate::from_ymd_opt($y, $m, $d) {
162+
Some(d) => d,
163+
None => panic!("invalid calendar date"),
164+
};
165+
let time = match $crate::NaiveTime::from_hms_opt($h, $min, 0) {
166+
Some(t) => t,
167+
None => panic!("invalid time"),
168+
};
169+
date.and_time(time)
170+
}
171+
}};
172+
}
173+
174+
#[cfg(test)]
175+
#[rustfmt::skip::macros(date)]
176+
mod tests {
177+
use crate::{FixedOffset, NaiveDate, NaiveTime, TimeZone};
178+
179+
#[test]
180+
fn init_macros() {
181+
assert_eq!(date!(2023-09-08), NaiveDate::from_ymd_opt(2023, 9, 8).unwrap());
182+
assert_eq!(date!(2023-253), NaiveDate::from_yo_opt(2023, 253).unwrap());
183+
assert_eq!(time!(7:03), NaiveTime::from_hms_opt(7, 3, 0).unwrap());
184+
assert_eq!(time!(7:03:25), NaiveTime::from_hms_opt(7, 3, 25).unwrap());
185+
assert_eq!(
186+
time!(23:59:60),
187+
NaiveTime::from_hms_nano_opt(23, 59, 59, 1_000_000_000).unwrap()
188+
);
189+
assert_eq!(
190+
datetime!(2023-09-08 7:03),
191+
NaiveDate::from_ymd_opt(2023, 9, 8).unwrap().and_hms_opt(7, 3, 0).unwrap(),
192+
);
193+
assert_eq!(
194+
datetime!(2023-09-08 7:03:25),
195+
NaiveDate::from_ymd_opt(2023, 9, 8).unwrap().and_hms_opt(7, 3, 25).unwrap(),
196+
);
197+
assert_eq!(
198+
datetime!(2023-09-08 7:03:25+02:00),
199+
FixedOffset::east_opt(7200).unwrap().with_ymd_and_hms(2023, 9, 8, 7, 3, 25).unwrap(),
200+
);
201+
assert_eq!(
202+
datetime!(2023-09-08 7:03:25-02:00),
203+
FixedOffset::east_opt(-7200).unwrap().with_ymd_and_hms(2023, 9, 8, 7, 3, 25).unwrap(),
204+
);
205+
}
206+
}

0 commit comments

Comments
 (0)