@@ -83,19 +83,81 @@ macro_rules! time {
8383 } } ;
8484}
8585
86- /// Create a `NaiveDateTime` with a statically known value.
86+ /// Create a `NaiveDateTime` or `DateTime<FixedOffset>` with a statically known value.
8787///
8888/// The input is checked at compile time.
8989///
9090/// # Examples
9191/// ```
9292/// use chrono::datetime;
9393///
94+ /// // NaiveDateTime
9495/// let _ = datetime!(2023-09-08 7:03);
9596/// let _ = datetime!(2023-09-08 7:03:25);
97+ /// // DateTime<FixedOffset>
98+ /// let _ = datetime!(2023-09-08 7:03:25+02:00);
99+ /// let _ = datetime!(2023-09-08 7:03:25-02:00);
96100/// ```
97101#[ macro_export]
98102macro_rules! datetime {
103+ ( $y: literal-$m: literal-$d: literal $h: literal: $min: literal: $s: literal+$hh: literal: $mm: literal) => { {
104+ #[ allow( clippy:: zero_prefixed_literal) ]
105+ {
106+ const DATE : $crate:: NaiveDate = match $crate:: NaiveDate :: from_ymd_opt( $y, $m, $d) {
107+ Some ( d) => d,
108+ None => panic!( "invalid calendar date" ) ,
109+ } ;
110+ const SECS_NANOS : ( u32 , u32 ) = match $s {
111+ 60u32 => ( 59 , 1_000_000_000 ) ,
112+ s => ( s, 0 ) ,
113+ } ;
114+ const TIME : $crate:: NaiveTime =
115+ match $crate:: NaiveTime :: from_hms_nano_opt( $h, $min, SECS_NANOS . 0 , SECS_NANOS . 1 ) {
116+ Some ( t) => t,
117+ None => panic!( "invalid time" ) ,
118+ } ;
119+ assert!( $hh < 24u32 || $mm < 60 , "invalid offset" ) ;
120+ const OFFSET : $crate:: FixedOffset =
121+ match $crate:: FixedOffset :: east_opt( ( $hh * 3600 + $mm * 60 ) as i32 ) {
122+ Some ( o) => o,
123+ None => panic!( "invalid offset" ) ,
124+ } ;
125+ const DT : $crate:: NaiveDateTime = match DATE . and_time( TIME ) . checked_sub_offset( OFFSET ) {
126+ Some ( o) => o,
127+ None => panic!( "datetime out of range" ) ,
128+ } ;
129+ $crate:: DateTime :: <$crate:: FixedOffset >:: __from_naive_utc_and_fixed_offset( DT , OFFSET )
130+ }
131+ } } ;
132+ ( $y: literal-$m: literal-$d: literal $h: literal: $min: literal: $s: literal-$hh: literal: $mm: literal) => { {
133+ #[ allow( clippy:: zero_prefixed_literal) ]
134+ {
135+ const DATE : $crate:: NaiveDate = match $crate:: NaiveDate :: from_ymd_opt( $y, $m, $d) {
136+ Some ( d) => d,
137+ None => panic!( "invalid calendar date" ) ,
138+ } ;
139+ const SECS_NANOS : ( u32 , u32 ) = match $s {
140+ 60u32 => ( 59 , 1_000_000_000 ) ,
141+ s => ( s, 0 ) ,
142+ } ;
143+ const TIME : $crate:: NaiveTime =
144+ match $crate:: NaiveTime :: from_hms_nano_opt( $h, $min, SECS_NANOS . 0 , SECS_NANOS . 1 ) {
145+ Some ( t) => t,
146+ None => panic!( "invalid time" ) ,
147+ } ;
148+ assert!( $hh < 24u32 || $mm < 60 , "invalid offset" ) ;
149+ const OFFSET : $crate:: FixedOffset =
150+ match $crate:: FixedOffset :: west_opt( ( $hh * 3600 + $mm * 60 ) as i32 ) {
151+ Some ( o) => o,
152+ None => panic!( "invalid offset" ) ,
153+ } ;
154+ const DT : $crate:: NaiveDateTime = match DATE . and_time( TIME ) . checked_sub_offset( OFFSET ) {
155+ Some ( o) => o,
156+ None => panic!( "datetime out of range" ) ,
157+ } ;
158+ $crate:: DateTime :: <$crate:: FixedOffset >:: __from_naive_utc_and_fixed_offset( DT , OFFSET )
159+ }
160+ } } ;
99161 ( $y: literal-$m: literal-$d: literal $h: literal: $min: literal: $s: literal) => { {
100162 #[ allow( clippy:: zero_prefixed_literal) ]
101163 {
@@ -202,7 +264,7 @@ macro_rules! offset {
202264#[ cfg( test) ]
203265#[ rustfmt:: skip:: macros( date) ]
204266mod tests {
205- use crate :: { FixedOffset , NaiveDate , NaiveDateTime , NaiveTime } ;
267+ use crate :: { DateTime , FixedOffset , NaiveDate , NaiveDateTime , NaiveTime , TimeZone } ;
206268
207269 #[ test]
208270 fn init_macros ( ) {
@@ -222,6 +284,14 @@ mod tests {
222284 datetime!( 2023 -09 -08 7 : 03 : 25 ) ,
223285 NaiveDate :: from_ymd_opt( 2023 , 9 , 8 ) . unwrap( ) . and_hms_opt( 7 , 3 , 25 ) . unwrap( ) ,
224286 ) ;
287+ assert_eq ! (
288+ datetime!( 2023 -09 -08 7 : 03 : 25 +02 : 00 ) ,
289+ FixedOffset :: east_opt( 7200 ) . unwrap( ) . with_ymd_and_hms( 2023 , 9 , 8 , 7 , 3 , 25 ) . unwrap( ) ,
290+ ) ;
291+ assert_eq ! (
292+ datetime!( 2023 -09 -08 7 : 03 : 25 -02 : 00 ) ,
293+ FixedOffset :: east_opt( -7200 ) . unwrap( ) . with_ymd_and_hms( 2023 , 9 , 8 , 7 , 3 , 25 ) . unwrap( ) ,
294+ ) ;
225295 assert_eq ! ( offset!( +05 : 43 ) , FixedOffset :: east_opt( 20_580 ) . unwrap( ) ) ;
226296 assert_eq ! ( offset!( -05 : 43 ) , FixedOffset :: east_opt( -20_580 ) . unwrap( ) ) ;
227297 assert_eq ! ( offset!( +05 : 43 : 21 ) , FixedOffset :: east_opt( 20_601 ) . unwrap( ) ) ;
@@ -236,6 +306,11 @@ mod tests {
236306 assert_eq ! ( DATE . and_time( TIME ) , NAIVEDATETIME ) ;
237307
238308 const OFFSET_1 : FixedOffset = offset ! ( +02 : 00 ) ;
309+ const DATETIME_1 : DateTime < FixedOffset > = datetime ! ( 2023 -09 -08 7 : 03 : 25 +02 : 00 ) ;
310+ assert_eq ! ( OFFSET_1 . from_local_datetime( & NAIVEDATETIME ) . unwrap( ) , DATETIME_1 ) ;
311+
239312 const OFFSET_2 : FixedOffset = offset ! ( -02 : 00 ) ;
313+ const DATETIME_2 : DateTime < FixedOffset > = datetime ! ( 2023 -09 -08 7 : 03 : 25 -02 : 00 ) ;
314+ assert_eq ! ( OFFSET_2 . from_local_datetime( & NAIVEDATETIME ) . unwrap( ) , DATETIME_2 ) ;
240315 }
241316}
0 commit comments