Skip to main content

dtt/
macros.rs

1// macros.rs
2//
3// Copyright © 2025 DateTime (DTT) library. All rights reserved.
4// SPDX-License-Identifier: Apache-2.0 OR MIT
5
6//! # Macros for the DTT Crate
7//!
8//! This module bundles all macros used across the DTT crate, facilitating date and time operations.
9//!
10//! ## Overview
11//!
12//! - **Utility Macros**: General purpose macros such as `dtt_print` for logging and `dtt_vec` for vector operations.
13//! - **DateTime Construction**: Macros like `dtt_now` and `dtt_new_with_tz` for creating `DateTime` instances.
14//! - **DateTime Operations**: Macros that perform arithmetic and comparisons on `DateTime` objects.
15//! - **Validation**: Macros like `is_valid` which validate various aspects of date and time inputs.
16
17/// Creates a new `DateTime` instance with the current date and time in UTC.
18///
19/// # Example
20///
21/// ```rust
22/// use dtt::dtt_now;
23///
24/// let now = dtt_now!();
25/// println!("Current date and time: {}", now);
26/// ```
27#[macro_export]
28macro_rules! dtt_now {
29    () => {{
30        $crate::datetime::DateTime::new()
31    }};
32}
33
34/// Prints the arguments to the console.
35///
36/// # Arguments
37///
38/// - `($($arg:tt)*)`: The arguments to be printed.
39///
40/// # Example
41///
42/// ```rust
43/// use dtt::dtt_print;
44///
45/// dtt_print!("Hello, World!");
46/// ```
47#[macro_export]
48macro_rules! dtt_parse {
49    ($input:expr) => {{
50        $crate::datetime::DateTime::parse($input)
51    }};
52}
53
54/// Prints the arguments to the console.
55///
56/// # Example
57///
58/// ```rust
59/// use dtt::dtt_print;
60///
61/// dtt_print!("Hello, World!");
62/// ```
63#[macro_export]
64macro_rules! dtt_print {
65    ($($arg:tt)*) => {
66        println!("{}", format_args!("{}", $($arg)*));
67    };
68}
69
70/// Creates a new vector of the given elements.
71///
72/// # Arguments
73///
74/// - `$($elem:expr),*`: The elements to be added to the vector.
75///
76/// # Example
77///
78/// ```rust
79/// use dtt::dtt_vec;
80///
81/// let v = dtt_vec!(1, 2, 3);
82/// assert_eq!(v, vec![1, 2, 3]);
83/// ```
84#[macro_export]
85macro_rules! dtt_vec {
86    ($($elem:expr),*) => {{
87        let mut v = Vec::new();
88        $(
89            v.push($elem);
90        )*
91        v
92    }};
93}
94
95/// Creates a new map of the given key-value pairs.
96///
97/// # Arguments
98///
99/// - `$($key:expr => $value:expr),*`: The key-value pairs to be added to the map.
100///
101/// # Example
102///
103/// ```rust
104/// use dtt::dtt_map;
105///
106/// let m = dtt_map!("one" => 1, "two" => 2);
107/// assert_eq!(m.get("one"), Some(&1));
108/// assert_eq!(m.get("two"), Some(&2));
109/// ```
110#[macro_export]
111macro_rules! dtt_map {
112    ($($key:expr => $value:expr),* $(,)?) => {{
113        use std::collections::HashMap;
114        let mut m = HashMap::new();
115        $(
116            m.insert($key, $value);
117        )*
118        m
119    }};
120}
121
122/// Asserts that the given expression is true.
123///
124/// If the expression is false, the macro panics with the message "Assertion failed!".
125///
126/// # Arguments
127///
128/// - `$cond:expr`: The condition to be asserted.
129/// - `$msg:expr` (optional): The message to display if the assertion fails.
130///
131/// # Example
132///
133/// ```rust
134/// use dtt::dtt_parse;
135/// use dtt::dtt_assert;
136///
137/// let is_valid = dtt_parse!("2020-02-29").is_ok();
138/// dtt_assert!(is_valid, "The date must be valid.");
139/// ```
140#[macro_export]
141macro_rules! dtt_assert {
142    ($cond:expr $(, $msg:expr)?) => {
143        if cfg!(debug_assertions) {
144            assert!($cond $(, $msg)?);
145        }
146    };
147}
148
149/// Generates a function that validates a given input string based on a specified type.
150///
151/// # Arguments
152///
153/// - `$name:ident`: The name of the validation function.
154/// - `$type:ty`: The type to validate.
155///
156/// # Example
157///
158/// ```rust
159/// use dtt::dtt_is_valid_function;
160///
161/// dtt_is_valid_function!(day, u8);
162/// assert!(is_valid_day("15"));
163/// assert!(!is_valid_day("32"));
164/// ```
165#[macro_export]
166macro_rules! dtt_is_valid_function {
167    ($name:ident, $type:ty) => {
168        ::pastey::paste! {
169            pub fn [<is_valid_ $name>](input: &str) -> bool {
170                if let Ok(parsed_val) = input.parse::<$type>() {
171                    $crate::datetime::DateTime::[<is_valid_ $name>](&parsed_val.to_string())
172                } else {
173                    false
174                }
175            }
176        }
177    };
178}
179
180/// Returns the minimum of the given values.
181///
182/// # Arguments
183///
184/// - `$x:expr`: The first value to be compared.
185/// - `$(, $y:expr)*`: Additional values to be compared.
186///
187/// # Example
188///
189/// ```rust
190/// use dtt::dtt_min;
191///
192/// assert_eq!(dtt_min!(10, 20, 30), 10);
193/// ```
194#[macro_export]
195macro_rules! dtt_min {
196    ($x:expr $(, $y:expr)*) => {{
197        let mut min = $x;
198        $(
199            let y = $y;
200            // This will cause a compile-time error if the types are not comparable
201            if std::cmp::PartialOrd::lt(&y, &min) { min = y; }
202        )*
203        min
204    }};
205}
206
207/// Returns the maximum of the given values.
208///
209/// # Arguments
210///
211/// - `$x:expr`: The first value to be compared.
212/// - `$(, $y:expr)*`: Additional values to be compared.
213///
214/// # Example
215///
216/// ```rust
217/// use dtt::dtt_max;
218///
219/// assert_eq!(dtt_max!(10, 20, 30), 30);
220/// ```
221#[macro_export]
222macro_rules! dtt_max {
223    ($x:expr $(, $y:expr)*) => {{
224        let mut max = $x;
225        $(
226            if max < $y { max = $y; }
227        )*
228        max
229    }};
230}
231
232/// Joins multiple strings into a single string.
233///
234/// # Arguments
235///
236/// - `$($s:expr),*`: The strings to be joined.
237///
238/// # Example
239///
240/// ```rust
241/// use dtt::dtt_join;
242///
243/// let s = dtt_join!("Hello", " ", "World");
244/// assert_eq!(s, "Hello World");
245/// ```
246#[macro_export]
247macro_rules! dtt_join {
248    ($($s:expr),*) => {{
249        let mut s = String::new();
250        $(
251            s += &$s;
252        )*
253        s
254    }};
255}
256
257/// Prints a vector of elements to the console.
258///
259/// # Arguments
260///
261/// - `$($v:expr),*`: The elements to be printed.
262///
263/// # Example
264///
265/// ```rust
266/// use dtt::dtt_print_vec;
267///
268/// dtt_print_vec!(&[1, 2, 3]);
269/// ```
270#[macro_export]
271macro_rules! dtt_print_vec {
272    ($($v:expr),*) => {{
273        for v in $($v),* {
274            println!("{}", v);
275        }
276    }};
277}
278
279/// Generates a function that validates a given input string based on a specified type.
280///
281/// # Arguments
282///
283/// - `$name:ident`: The name of the validation function.
284/// - `$type:ty`: The type to validate.
285///
286/// # Example
287///
288/// ```rust
289/// use dtt::dtt_is_valid_function;
290///
291/// dtt_is_valid_function!(day, u8);
292/// assert!(is_valid_day("15"));
293/// assert!(!is_valid_day("32"));
294/// ```
295#[macro_export]
296macro_rules! is_valid {
297    ($name:ident, $type:ty) => {
298        pub fn $name(input: &str) -> bool {
299            $crate::datetime::$name(input)
300        }
301    };
302}
303
304/// Creates a new `DateTime` instance with the specified timezone.
305///
306/// # Arguments
307///
308/// - `$tz:expr`: The timezone string.
309///
310/// # Example
311///
312/// ```rust
313/// use dtt::dtt_new_with_tz;
314///
315/// let dt = dtt_new_with_tz!("CET");
316/// assert_eq!(dt.offset().to_string(), "+01:00:00");
317/// ```
318#[macro_export]
319macro_rules! dtt_new_with_tz {
320    ($tz:expr) => {{
321        $crate::datetime::DateTime::new_with_tz($tz).expect(
322            "Failed to create DateTime with the specified timezone",
323        )
324    }};
325}
326
327/// Adds the specified number of days to the given `DateTime` instance.
328///
329/// # Arguments
330///
331/// - `$date:expr`: The `DateTime` instance.
332/// - `$days:expr`: The number of days to be added.
333///
334/// # Example
335///
336/// ```rust
337/// use dtt::{dtt_add_days, dtt_parse};
338///
339/// let dt = dtt_parse!("2023-01-01T12:00:00+00:00").unwrap();
340/// let future_date = dtt_add_days!(dt, 5).unwrap();
341/// assert_eq!(future_date.day(), 6);
342/// ```
343#[macro_export]
344macro_rules! dtt_add_days {
345    ($date:expr, $days:expr) => {
346        $date.add_days($days)
347    };
348}
349
350/// Subtracts the specified number of days from the given `DateTime` instance.
351///
352/// # Arguments
353///
354/// - `$date:expr`: The `DateTime` instance.
355/// - `$days:expr`: The number of days to be subtracted.
356///
357/// # Example
358///
359/// ```rust
360/// use dtt::{dtt_sub_days, dtt_parse};
361///
362/// let dt = dtt_parse!("2023-01-06T12:00:00+00:00").unwrap();
363/// let past_date = dtt_sub_days!(dt, 5).unwrap();
364/// assert_eq!(past_date.day(), 1);
365/// ```
366#[macro_export]
367macro_rules! dtt_sub_days {
368    ($date:expr, $days:expr) => {
369        $date.add_days(-$days)
370    };
371}
372
373/// A helper macro to calculate the difference between two `DateTime` instances.
374///
375/// # Parameters
376///
377/// - `$dt1:expr`: The first `DateTime` instance, as a string parseable as `i64`.
378/// - `$dt2:expr`: The second `DateTime` instance, as a string parseable as `i64`.
379/// - `$unit:expr`: The unit for the difference (seconds, days, etc.).
380///
381/// # Returns
382///
383/// `Some(i64)` containing the absolute difference in the specified unit, or
384/// `None` if either input cannot be parsed as `i64`.
385///
386/// # Example
387///
388/// ```rust
389/// use dtt::{dtt_diff, dtt_parse};
390///
391/// let dt1 = "1609459200"; // 2021-01-01 00:00:00 UTC
392/// let dt2 = "1609459230"; // 2021-01-01 00:00:30 UTC
393/// let seconds_difference = dtt_diff!(dt1, dt2, 1);
394/// assert_eq!(seconds_difference, Some(30i64));
395/// ```
396#[macro_export]
397macro_rules! dtt_diff {
398    ($dt1:expr, $dt2:expr, $unit:expr) => {{
399        match ($dt1.parse::<i64>(), $dt2.parse::<i64>()) {
400            (Ok(dt1), Ok(dt2)) => {
401                let difference =
402                    if dt1 <= dt2 { dt2 - dt1 } else { dt1 - dt2 };
403                Some((difference / $unit).abs())
404            }
405            _ => None,
406        }
407    }};
408}
409
410/// Calculates the difference in seconds between two `DateTime` instances.
411///
412/// # Arguments
413///
414/// - `$dt1:expr`: The first `DateTime` instance.
415/// - `$dt2:expr`: The second `DateTime` instance.
416///
417/// # Example
418///
419/// ```rust
420/// use dtt::dtt_diff_seconds;
421/// use dtt::dtt_diff;
422///
423/// let dt1 = "1609459200"; // 2021-01-01 00:00:00 UTC
424/// let dt2 = "1609459230"; // 2021-01-01 00:00:30 UTC
425/// let seconds_difference = dtt_diff_seconds!(dt1, dt2);
426/// assert_eq!(seconds_difference, Some(30i64));
427/// ```
428#[macro_export]
429macro_rules! dtt_diff_seconds {
430    ($dt1:expr, $dt2:expr) => {
431        dtt_diff!($dt1, $dt2, 1)
432    };
433}
434
435/// Calculates the difference in days between two `DateTime` instances.
436///
437/// # Arguments
438///
439/// - `$dt1:expr`: The first `DateTime` instance.
440/// - `$dt2:expr`: The second `DateTime` instance.
441///
442/// # Example
443///
444/// ```rust
445/// use dtt::dtt_diff_days;
446/// use dtt::dtt_diff;
447///
448/// let dt1 = "1609459200"; // 2021-01-01 00:00:00 UTC
449/// let dt2 = "1609545600"; // 2021-01-02 00:00:00 UTC
450/// let days_difference = dtt_diff_days!(dt1, dt2);
451/// assert_eq!(days_difference, Some(1i64));
452/// ```
453#[macro_export]
454macro_rules! dtt_diff_days {
455    ($dt1:expr, $dt2:expr) => {
456        dtt_diff!($dt1, $dt2, 86400) // 86400 seconds in a day
457    };
458}
459
460/// Creates a copy of the provided `DateTime` object.
461///
462/// # Arguments
463///
464/// - `$dt:expr`: The `DateTime` object to be cloned.
465///
466/// # Example
467///
468/// ```rust
469/// use dtt::{dtt_clone, dtt_parse};
470///
471/// let dt = dtt_parse!("2023-01-01T12:00:00+00:00").unwrap();
472/// let cloned = dtt_clone!(dt);
473/// assert_eq!(dt.year(), cloned.year());
474/// ```
475#[macro_export]
476macro_rules! dtt_clone {
477    ($dt:expr) => {{
478        let dt = $dt;
479        dt.clone()
480    }};
481}
482
483/// Formats a `DateTime` object using the provided format string.
484///
485/// # Arguments
486///
487/// - `$dt:expr`: The `DateTime` object to be formatted.
488/// - `$format:expr`: The format string to use for formatting the `DateTime` object.
489///
490/// # Example
491///
492/// ```rust
493/// use dtt::{dtt_format, dtt_parse};
494/// use time::Month;
495///
496/// let dt = dtt_parse!("2023-01-01T12:00:00+00:00").unwrap();
497/// let formatted = dtt_format!(
498///     dt,
499///     "{year}-{month}-{day}T{hour}:{minute}:{second}.{microsecond}{offset_sign}{offset_hour}:{offset_minute}"
500/// );
501/// assert_eq!(formatted, "2023-01-01T12:00:00.000000+00:00");
502/// ```
503#[macro_export]
504macro_rules! dtt_format {
505    ($dt:expr, $format:expr) => {{
506        // Convert the month to a numeric value manually
507        let month_number = match $dt.month() {
508            Month::January => 1,
509            Month::February => 2,
510            Month::March => 3,
511            Month::April => 4,
512            Month::May => 5,
513            Month::June => 6,
514            Month::July => 7,
515            Month::August => 8,
516            Month::September => 9,
517            Month::October => 10,
518            Month::November => 11,
519            Month::December => 12,
520        };
521
522        format!(
523            $format,
524            day = format!("{:02}", $dt.day()),
525            hour = format!("{:02}", $dt.hour()),
526            microsecond = format!("{:06}", $dt.microsecond()),
527            minute = format!("{:02}", $dt.minute()),
528            month = format!("{:02}", month_number), // Use the numeric month
529            offset_sign = if $dt.offset().whole_seconds() >= 0 { "+" } else { "-" },
530            offset_hour = format!("{:02}", $dt.offset().whole_hours().abs()),
531            offset_minute = format!("{:02}", ($dt.offset().whole_seconds().abs() % 3600) / 60),
532            second = format!("{:02}", $dt.second()),
533            year = $dt.year(),
534        )
535    }};
536}