easy_time/
lib.rs

1//! # Easy Time
2//!
3//! A simple and intuitive library for handling time in Rust.
4//!
5//! `easy_time` provides a convenient wrapper around [`chrono`] to make common time operations
6//! more ergonomic and readable. Calculate dates in the future or past with ease using
7//! human-friendly static methods.
8//!
9//! ## Features
10//!
11//! - Simple static API for time calculations (seconds, minutes, hours, days, months, years, etc.)
12//! - Support for both local time and UTC
13//! - Generic over any `chrono` timezone
14//! - Handles edge cases like leap years and month boundaries
15//! - Human-readable method names like `days_from_now()` and `months_ago()`
16//!
17//! ## Quick Start
18//!
19//! ```rust
20//! use easy_time::EasyTime;
21//! use chrono::Local;
22//!
23//! // Calculate 5 days from now
24//! let future = EasyTime::<Local>::days_from_now(5);
25//!
26//! // Calculate 3 months ago
27//! let past = EasyTime::<Local>::months_ago(3);
28//!
29//! // Using UTC
30//! let utc_future = EasyTime::<Local>::utc_hours_from_now(10);
31//! ```
32
33// Allow intentional casts - year values in practice never exceed i32 range
34#![allow(clippy::cast_possible_truncation)]
35#![allow(clippy::cast_possible_wrap)]
36#![allow(clippy::cast_sign_loss)]
37
38use chrono::prelude::{DateTime, TimeZone};
39use chrono::{Datelike, Duration, Local, LocalResult, Utc};
40
41/// Default date format: `YYYY-MM-DD HH:MM:SS`
42pub const DEFAULT_DATE_FORMAT: &str = "%Y-%m-%d %H:%M:%S";
43/// Date-only format: `YYYY-MM-DD`
44pub const DATE_FORMAT: &str = "%Y-%m-%d";
45/// Time-only format: `HH:MM:SS`
46pub const TIME_FORMAT: &str = "%H:%M:%S";
47
48// Constant array for days in each month (non-leap year)
49const DAYS_IN_MONTH: [u32; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
50
51/// The main struct for time operations.
52///
53/// `EasyTime` provides convenient static methods for calculating dates in the future or past.
54/// It is generic over any timezone `F` that implements `chrono::TimeZone`.
55///
56/// # Type Parameters
57///
58/// * `F` - A timezone type that implements [`chrono::TimeZone`]
59///
60/// # Example
61///
62/// ```rust
63/// use easy_time::EasyTime;
64/// use chrono::{Local, Utc};
65///
66/// // Simple time calculations with Local timezone
67/// let five_days_from_now = EasyTime::<Local>::days_from_now(5);
68/// let five_days_ago = EasyTime::<Local>::days_ago(5);
69///
70/// // Using UTC timezone
71/// let ten_hours_from_now = EasyTime::<Utc>::hours_from_now(10);
72///
73/// // Convenience UTC methods
74/// let utc_future = EasyTime::<Local>::utc_days_from_now(7);
75/// ```
76#[derive(Clone, PartialEq, Debug, Eq)]
77pub struct EasyTime<F: TimeZone> {
78    _marker: std::marker::PhantomData<F>,
79}
80
81// ----------------------------------------------------------
82//                    Internal Helpers
83// ----------------------------------------------------------
84
85/// Checks if a year is a leap year.
86#[inline]
87const fn is_leap_year(year: i32) -> bool {
88    (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
89}
90
91/// Returns the number of days in a given month.
92#[inline]
93fn days_in_month(year: i32, month: u32) -> u32 {
94    if month == 2 && is_leap_year(year) {
95        29
96    } else {
97        DAYS_IN_MONTH[(month - 1) as usize]
98    }
99}
100
101/// Tries to build a `DateTime<F>` from a naive date-time.
102fn build_datetime_from_naive<F: TimeZone>(tz: &F, naive: chrono::NaiveDateTime) -> DateTime<F> {
103    match tz.from_local_datetime(&naive) {
104        LocalResult::Single(dt) => dt,
105        LocalResult::Ambiguous(a, _b) => a,
106        LocalResult::None => panic!("Invalid or non-existent local time."),
107    }
108}
109
110/// Adds or subtracts months from a datetime.
111fn add_months_to_datetime<F: TimeZone>(time: &DateTime<F>, months: i32) -> DateTime<F> {
112    let naive = time.naive_local();
113    let (year, month, day) = (naive.year(), naive.month() as i32, naive.day());
114
115    let total_months = year * 12 + (month - 1) + months;
116    let target_year = total_months.div_euclid(12);
117    let target_month = total_months.rem_euclid(12) + 1;
118
119    let days_in_target = days_in_month(target_year, target_month as u32);
120    let target_day = std::cmp::min(day, days_in_target);
121
122    let target_date =
123        chrono::NaiveDate::from_ymd_opt(target_year, target_month as u32, target_day)
124            .expect("Invalid date after adding months");
125
126    let target_naive_dt = target_date.and_time(naive.time());
127    build_datetime_from_naive(&time.timezone(), target_naive_dt)
128}
129
130/// Adds or subtracts years from a datetime.
131fn add_years_to_datetime<F: TimeZone>(time: &DateTime<F>, years: i32) -> DateTime<F> {
132    let naive = time.naive_local();
133    let (year, month, day) = (naive.year() + years, naive.month(), naive.day());
134
135    let days_in_target = days_in_month(year, month);
136    let target_day = std::cmp::min(day, days_in_target);
137
138    let target_date = chrono::NaiveDate::from_ymd_opt(year, month, target_day)
139        .expect("Invalid date after adding years");
140
141    let target_naive_dt = target_date.and_time(naive.time());
142    build_datetime_from_naive(&time.timezone(), target_naive_dt)
143}
144
145// ----------------------------------------------------------
146//           EasyTime<Local>: Static Methods
147// ----------------------------------------------------------
148impl EasyTime<Local> {
149    // ------------------------------------------------------------------
150    //           Simple Offsets: seconds, minutes, hours, days
151    // ------------------------------------------------------------------
152
153    /// Returns a datetime that is `value` seconds in the future from now.
154    ///
155    /// # Example
156    /// ```rust
157    /// use easy_time::EasyTime;
158    /// use chrono::Local;
159    ///
160    /// let future = EasyTime::<Local>::seconds_from_now(30);
161    /// ```
162    #[inline]
163    #[must_use]
164    pub fn seconds_from_now(value: i64) -> DateTime<Local> {
165        Local::now() + Duration::seconds(value)
166    }
167
168    /// Returns a datetime that is `value` seconds in the past from now.
169    #[inline]
170    #[must_use]
171    pub fn seconds_ago(value: i64) -> DateTime<Local> {
172        Local::now() - Duration::seconds(value)
173    }
174
175    /// Returns a datetime that is `value` minutes in the future from now.
176    #[inline]
177    #[must_use]
178    pub fn minutes_from_now(value: i64) -> DateTime<Local> {
179        Local::now() + Duration::minutes(value)
180    }
181
182    /// Returns a datetime that is `value` minutes in the past from now.
183    #[inline]
184    #[must_use]
185    pub fn minutes_ago(value: i64) -> DateTime<Local> {
186        Local::now() - Duration::minutes(value)
187    }
188
189    /// Returns a datetime that is `value` hours in the future from now.
190    #[inline]
191    #[must_use]
192    pub fn hours_from_now(value: i64) -> DateTime<Local> {
193        Local::now() + Duration::hours(value)
194    }
195
196    /// Returns a datetime that is `value` hours in the past from now.
197    #[inline]
198    #[must_use]
199    pub fn hours_ago(value: i64) -> DateTime<Local> {
200        Local::now() - Duration::hours(value)
201    }
202
203    /// Returns a datetime that is `value` days in the future from now.
204    #[inline]
205    #[must_use]
206    pub fn days_from_now(value: i64) -> DateTime<Local> {
207        Local::now() + Duration::days(value)
208    }
209
210    /// Returns a datetime that is `value` days in the past from now.
211    #[inline]
212    #[must_use]
213    pub fn days_ago(value: i64) -> DateTime<Local> {
214        Local::now() - Duration::days(value)
215    }
216
217    /// Returns a datetime that is `value` weeks in the future from now.
218    #[inline]
219    #[must_use]
220    pub fn weeks_from_now(value: i64) -> DateTime<Local> {
221        Local::now() + Duration::weeks(value)
222    }
223
224    /// Returns a datetime that is `value` weeks in the past from now.
225    #[inline]
226    #[must_use]
227    pub fn weeks_ago(value: i64) -> DateTime<Local> {
228        Local::now() - Duration::weeks(value)
229    }
230
231    // ------------------------------------------------------------------
232    //               Month-Based Offsets
233    // ------------------------------------------------------------------
234
235    /// Returns a datetime that is `value` months in the future from now.
236    ///
237    /// Handles edge cases like months with different numbers of days.
238    #[must_use]
239    pub fn months_from_now(value: i64) -> DateTime<Local> {
240        add_months_to_datetime(&Local::now(), value as i32)
241    }
242
243    /// Returns a datetime that is `value` months in the past from now.
244    #[must_use]
245    pub fn months_ago(value: i64) -> DateTime<Local> {
246        add_months_to_datetime(&Local::now(), -(value as i32))
247    }
248
249    // ------------------------------------------------------------------
250    //               Year-Based Offsets
251    // ------------------------------------------------------------------
252
253    /// Returns a datetime that is `value` years in the future from now.
254    #[must_use]
255    pub fn years_from_now(value: i64) -> DateTime<Local> {
256        add_years_to_datetime(&Local::now(), value as i32)
257    }
258
259    /// Returns a datetime that is `value` years in the past from now.
260    #[must_use]
261    pub fn years_ago(value: i64) -> DateTime<Local> {
262        add_years_to_datetime(&Local::now(), -(value as i32))
263    }
264
265    /// Returns a datetime that is `value` decades (10 years) in the future from now.
266    #[must_use]
267    pub fn decades_from_now(value: i64) -> DateTime<Local> {
268        add_years_to_datetime(&Local::now(), value as i32 * 10)
269    }
270
271    /// Returns a datetime that is `value` decades (10 years) in the past from now.
272    #[must_use]
273    pub fn decades_ago(value: i64) -> DateTime<Local> {
274        add_years_to_datetime(&Local::now(), -(value as i32) * 10)
275    }
276
277    /// Returns a datetime that is `value` centuries (100 years) in the future from now.
278    #[must_use]
279    pub fn centuries_from_now(value: i64) -> DateTime<Local> {
280        add_years_to_datetime(&Local::now(), value as i32 * 100)
281    }
282
283    /// Returns a datetime that is `value` centuries (100 years) in the past from now.
284    #[must_use]
285    pub fn centuries_ago(value: i64) -> DateTime<Local> {
286        add_years_to_datetime(&Local::now(), -(value as i32) * 100)
287    }
288
289    /// Returns a datetime that is `value` millenniums (1000 years) in the future from now.
290    #[must_use]
291    pub fn millenniums_from_now(value: i64) -> DateTime<Local> {
292        add_years_to_datetime(&Local::now(), value as i32 * 1000)
293    }
294
295    /// Returns a datetime that is `value` millenniums (1000 years) in the past from now.
296    #[must_use]
297    pub fn millenniums_ago(value: i64) -> DateTime<Local> {
298        add_years_to_datetime(&Local::now(), -(value as i32) * 1000)
299    }
300
301    // ------------------------------------------------------------------
302    //           Methods with custom base time
303    // ------------------------------------------------------------------
304
305    /// Returns a datetime that is `value` seconds from the given base time.
306    #[inline]
307    #[must_use]
308    pub fn seconds_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
309        base + Duration::seconds(value)
310    }
311
312    /// Returns a datetime that is `value` seconds before the given base time.
313    #[inline]
314    #[must_use]
315    pub fn seconds_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
316        base - Duration::seconds(value)
317    }
318
319    /// Returns a datetime that is `value` minutes from the given base time.
320    #[inline]
321    #[must_use]
322    pub fn minutes_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
323        base + Duration::minutes(value)
324    }
325
326    /// Returns a datetime that is `value` minutes before the given base time.
327    #[inline]
328    #[must_use]
329    pub fn minutes_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
330        base - Duration::minutes(value)
331    }
332
333    /// Returns a datetime that is `value` hours from the given base time.
334    #[inline]
335    #[must_use]
336    pub fn hours_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
337        base + Duration::hours(value)
338    }
339
340    /// Returns a datetime that is `value` hours before the given base time.
341    #[inline]
342    #[must_use]
343    pub fn hours_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
344        base - Duration::hours(value)
345    }
346
347    /// Returns a datetime that is `value` days from the given base time.
348    #[inline]
349    #[must_use]
350    pub fn days_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
351        base + Duration::days(value)
352    }
353
354    /// Returns a datetime that is `value` days before the given base time.
355    #[inline]
356    #[must_use]
357    pub fn days_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
358        base - Duration::days(value)
359    }
360
361    /// Returns a datetime that is `value` weeks from the given base time.
362    #[inline]
363    #[must_use]
364    pub fn weeks_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
365        base + Duration::weeks(value)
366    }
367
368    /// Returns a datetime that is `value` weeks before the given base time.
369    #[inline]
370    #[must_use]
371    pub fn weeks_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
372        base - Duration::weeks(value)
373    }
374
375    /// Returns a datetime that is `value` months from the given base time.
376    #[must_use]
377    pub fn months_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
378        add_months_to_datetime(&base, value as i32)
379    }
380
381    /// Returns a datetime that is `value` months before the given base time.
382    #[must_use]
383    pub fn months_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
384        add_months_to_datetime(&base, -(value as i32))
385    }
386
387    /// Returns a datetime that is `value` years from the given base time.
388    #[must_use]
389    pub fn years_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
390        add_years_to_datetime(&base, value as i32)
391    }
392
393    /// Returns a datetime that is `value` years before the given base time.
394    #[must_use]
395    pub fn years_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
396        add_years_to_datetime(&base, -(value as i32))
397    }
398
399    /// Returns a datetime that is `value` decades from the given base time.
400    #[must_use]
401    pub fn decades_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
402        add_years_to_datetime(&base, value as i32 * 10)
403    }
404
405    /// Returns a datetime that is `value` decades before the given base time.
406    #[must_use]
407    pub fn decades_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
408        add_years_to_datetime(&base, -(value as i32) * 10)
409    }
410
411    /// Returns a datetime that is `value` centuries from the given base time.
412    #[must_use]
413    pub fn centuries_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
414        add_years_to_datetime(&base, value as i32 * 100)
415    }
416
417    /// Returns a datetime that is `value` centuries before the given base time.
418    #[must_use]
419    pub fn centuries_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
420        add_years_to_datetime(&base, -(value as i32) * 100)
421    }
422
423    /// Returns a datetime that is `value` millenniums from the given base time.
424    #[must_use]
425    pub fn millenniums_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
426        add_years_to_datetime(&base, value as i32 * 1000)
427    }
428
429    /// Returns a datetime that is `value` millenniums before the given base time.
430    #[must_use]
431    pub fn millenniums_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
432        add_years_to_datetime(&base, -(value as i32) * 1000)
433    }
434}
435
436// ----------------------------------------------------------
437//           EasyTime<Utc>: Static Methods
438// ----------------------------------------------------------
439impl EasyTime<Utc> {
440    // ------------------------------------------------------------------
441    //           Simple Offsets: seconds, minutes, hours, days
442    // ------------------------------------------------------------------
443
444    /// Returns a UTC datetime that is `value` seconds in the future from now.
445    #[inline]
446    #[must_use]
447    pub fn seconds_from_now(value: i64) -> DateTime<Utc> {
448        Utc::now() + Duration::seconds(value)
449    }
450
451    /// Returns a UTC datetime that is `value` seconds in the past from now.
452    #[inline]
453    #[must_use]
454    pub fn seconds_ago(value: i64) -> DateTime<Utc> {
455        Utc::now() - Duration::seconds(value)
456    }
457
458    /// Returns a UTC datetime that is `value` minutes in the future from now.
459    #[inline]
460    #[must_use]
461    pub fn minutes_from_now(value: i64) -> DateTime<Utc> {
462        Utc::now() + Duration::minutes(value)
463    }
464
465    /// Returns a UTC datetime that is `value` minutes in the past from now.
466    #[inline]
467    #[must_use]
468    pub fn minutes_ago(value: i64) -> DateTime<Utc> {
469        Utc::now() - Duration::minutes(value)
470    }
471
472    /// Returns a UTC datetime that is `value` hours in the future from now.
473    #[inline]
474    #[must_use]
475    pub fn hours_from_now(value: i64) -> DateTime<Utc> {
476        Utc::now() + Duration::hours(value)
477    }
478
479    /// Returns a UTC datetime that is `value` hours in the past from now.
480    #[inline]
481    #[must_use]
482    pub fn hours_ago(value: i64) -> DateTime<Utc> {
483        Utc::now() - Duration::hours(value)
484    }
485
486    /// Returns a UTC datetime that is `value` days in the future from now.
487    #[inline]
488    #[must_use]
489    pub fn days_from_now(value: i64) -> DateTime<Utc> {
490        Utc::now() + Duration::days(value)
491    }
492
493    /// Returns a UTC datetime that is `value` days in the past from now.
494    #[inline]
495    #[must_use]
496    pub fn days_ago(value: i64) -> DateTime<Utc> {
497        Utc::now() - Duration::days(value)
498    }
499
500    /// Returns a UTC datetime that is `value` weeks in the future from now.
501    #[inline]
502    #[must_use]
503    pub fn weeks_from_now(value: i64) -> DateTime<Utc> {
504        Utc::now() + Duration::weeks(value)
505    }
506
507    /// Returns a UTC datetime that is `value` weeks in the past from now.
508    #[inline]
509    #[must_use]
510    pub fn weeks_ago(value: i64) -> DateTime<Utc> {
511        Utc::now() - Duration::weeks(value)
512    }
513
514    // ------------------------------------------------------------------
515    //               Month-Based Offsets
516    // ------------------------------------------------------------------
517
518    /// Returns a UTC datetime that is `value` months in the future from now.
519    #[must_use]
520    pub fn months_from_now(value: i64) -> DateTime<Utc> {
521        add_months_to_datetime(&Utc::now(), value as i32)
522    }
523
524    /// Returns a UTC datetime that is `value` months in the past from now.
525    #[must_use]
526    pub fn months_ago(value: i64) -> DateTime<Utc> {
527        add_months_to_datetime(&Utc::now(), -(value as i32))
528    }
529
530    // ------------------------------------------------------------------
531    //               Year-Based Offsets
532    // ------------------------------------------------------------------
533
534    /// Returns a UTC datetime that is `value` years in the future from now.
535    #[must_use]
536    pub fn years_from_now(value: i64) -> DateTime<Utc> {
537        add_years_to_datetime(&Utc::now(), value as i32)
538    }
539
540    /// Returns a UTC datetime that is `value` years in the past from now.
541    #[must_use]
542    pub fn years_ago(value: i64) -> DateTime<Utc> {
543        add_years_to_datetime(&Utc::now(), -(value as i32))
544    }
545
546    /// Returns a UTC datetime that is `value` decades (10 years) in the future from now.
547    #[must_use]
548    pub fn decades_from_now(value: i64) -> DateTime<Utc> {
549        add_years_to_datetime(&Utc::now(), value as i32 * 10)
550    }
551
552    /// Returns a UTC datetime that is `value` decades (10 years) in the past from now.
553    #[must_use]
554    pub fn decades_ago(value: i64) -> DateTime<Utc> {
555        add_years_to_datetime(&Utc::now(), -(value as i32) * 10)
556    }
557
558    /// Returns a UTC datetime that is `value` centuries (100 years) in the future from now.
559    #[must_use]
560    pub fn centuries_from_now(value: i64) -> DateTime<Utc> {
561        add_years_to_datetime(&Utc::now(), value as i32 * 100)
562    }
563
564    /// Returns a UTC datetime that is `value` centuries (100 years) in the past from now.
565    #[must_use]
566    pub fn centuries_ago(value: i64) -> DateTime<Utc> {
567        add_years_to_datetime(&Utc::now(), -(value as i32) * 100)
568    }
569
570    /// Returns a UTC datetime that is `value` millenniums (1000 years) in the future from now.
571    #[must_use]
572    pub fn millenniums_from_now(value: i64) -> DateTime<Utc> {
573        add_years_to_datetime(&Utc::now(), value as i32 * 1000)
574    }
575
576    /// Returns a UTC datetime that is `value` millenniums (1000 years) in the past from now.
577    #[must_use]
578    pub fn millenniums_ago(value: i64) -> DateTime<Utc> {
579        add_years_to_datetime(&Utc::now(), -(value as i32) * 1000)
580    }
581
582    // ------------------------------------------------------------------
583    //           Methods with custom base time
584    // ------------------------------------------------------------------
585
586    /// Returns a UTC datetime that is `value` seconds from the given base time.
587    #[inline]
588    #[must_use]
589    pub fn seconds_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
590        base + Duration::seconds(value)
591    }
592
593    /// Returns a UTC datetime that is `value` seconds before the given base time.
594    #[inline]
595    #[must_use]
596    pub fn seconds_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
597        base - Duration::seconds(value)
598    }
599
600    /// Returns a UTC datetime that is `value` minutes from the given base time.
601    #[inline]
602    #[must_use]
603    pub fn minutes_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
604        base + Duration::minutes(value)
605    }
606
607    /// Returns a UTC datetime that is `value` minutes before the given base time.
608    #[inline]
609    #[must_use]
610    pub fn minutes_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
611        base - Duration::minutes(value)
612    }
613
614    /// Returns a UTC datetime that is `value` hours from the given base time.
615    #[inline]
616    #[must_use]
617    pub fn hours_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
618        base + Duration::hours(value)
619    }
620
621    /// Returns a UTC datetime that is `value` hours before the given base time.
622    #[inline]
623    #[must_use]
624    pub fn hours_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
625        base - Duration::hours(value)
626    }
627
628    /// Returns a UTC datetime that is `value` days from the given base time.
629    #[inline]
630    #[must_use]
631    pub fn days_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
632        base + Duration::days(value)
633    }
634
635    /// Returns a UTC datetime that is `value` days before the given base time.
636    #[inline]
637    #[must_use]
638    pub fn days_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
639        base - Duration::days(value)
640    }
641
642    /// Returns a UTC datetime that is `value` weeks from the given base time.
643    #[inline]
644    #[must_use]
645    pub fn weeks_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
646        base + Duration::weeks(value)
647    }
648
649    /// Returns a UTC datetime that is `value` weeks before the given base time.
650    #[inline]
651    #[must_use]
652    pub fn weeks_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
653        base - Duration::weeks(value)
654    }
655
656    /// Returns a UTC datetime that is `value` months from the given base time.
657    #[must_use]
658    pub fn months_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
659        add_months_to_datetime(&base, value as i32)
660    }
661
662    /// Returns a UTC datetime that is `value` months before the given base time.
663    #[must_use]
664    pub fn months_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
665        add_months_to_datetime(&base, -(value as i32))
666    }
667
668    /// Returns a UTC datetime that is `value` years from the given base time.
669    #[must_use]
670    pub fn years_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
671        add_years_to_datetime(&base, value as i32)
672    }
673
674    /// Returns a UTC datetime that is `value` years before the given base time.
675    #[must_use]
676    pub fn years_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
677        add_years_to_datetime(&base, -(value as i32))
678    }
679
680    /// Returns a UTC datetime that is `value` decades from the given base time.
681    #[must_use]
682    pub fn decades_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
683        add_years_to_datetime(&base, value as i32 * 10)
684    }
685
686    /// Returns a UTC datetime that is `value` decades before the given base time.
687    #[must_use]
688    pub fn decades_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
689        add_years_to_datetime(&base, -(value as i32) * 10)
690    }
691
692    /// Returns a UTC datetime that is `value` centuries from the given base time.
693    #[must_use]
694    pub fn centuries_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
695        add_years_to_datetime(&base, value as i32 * 100)
696    }
697
698    /// Returns a UTC datetime that is `value` centuries before the given base time.
699    #[must_use]
700    pub fn centuries_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
701        add_years_to_datetime(&base, -(value as i32) * 100)
702    }
703
704    /// Returns a UTC datetime that is `value` millenniums from the given base time.
705    #[must_use]
706    pub fn millenniums_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
707        add_years_to_datetime(&base, value as i32 * 1000)
708    }
709
710    /// Returns a UTC datetime that is `value` millenniums before the given base time.
711    #[must_use]
712    pub fn millenniums_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
713        add_years_to_datetime(&base, -(value as i32) * 1000)
714    }
715}
716
717// ----------------------------------------------------------
718//           Convenience UTC methods (no type parameter needed)
719// ----------------------------------------------------------
720impl<F: TimeZone> EasyTime<F> {
721    /// Returns a UTC datetime that is `value` seconds in the future from now.
722    ///
723    /// Convenience method that doesn't require specifying a type parameter.
724    ///
725    /// # Example
726    /// ```rust
727    /// use easy_time::EasyTime;
728    /// use chrono::Local;
729    ///
730    /// let future = EasyTime::<Local>::utc_seconds_from_now(30);
731    /// ```
732    #[inline]
733    #[must_use]
734    pub fn utc_seconds_from_now(value: i64) -> DateTime<Utc> {
735        Utc::now() + Duration::seconds(value)
736    }
737
738    /// Returns a UTC datetime that is `value` seconds in the past from now.
739    #[inline]
740    #[must_use]
741    pub fn utc_seconds_ago(value: i64) -> DateTime<Utc> {
742        Utc::now() - Duration::seconds(value)
743    }
744
745    /// Returns a UTC datetime that is `value` minutes in the future from now.
746    #[inline]
747    #[must_use]
748    pub fn utc_minutes_from_now(value: i64) -> DateTime<Utc> {
749        Utc::now() + Duration::minutes(value)
750    }
751
752    /// Returns a UTC datetime that is `value` minutes in the past from now.
753    #[inline]
754    #[must_use]
755    pub fn utc_minutes_ago(value: i64) -> DateTime<Utc> {
756        Utc::now() - Duration::minutes(value)
757    }
758
759    /// Returns a UTC datetime that is `value` hours in the future from now.
760    #[inline]
761    #[must_use]
762    pub fn utc_hours_from_now(value: i64) -> DateTime<Utc> {
763        Utc::now() + Duration::hours(value)
764    }
765
766    /// Returns a UTC datetime that is `value` hours in the past from now.
767    #[inline]
768    #[must_use]
769    pub fn utc_hours_ago(value: i64) -> DateTime<Utc> {
770        Utc::now() - Duration::hours(value)
771    }
772
773    /// Returns a UTC datetime that is `value` days in the future from now.
774    #[inline]
775    #[must_use]
776    pub fn utc_days_from_now(value: i64) -> DateTime<Utc> {
777        Utc::now() + Duration::days(value)
778    }
779
780    /// Returns a UTC datetime that is `value` days in the past from now.
781    #[inline]
782    #[must_use]
783    pub fn utc_days_ago(value: i64) -> DateTime<Utc> {
784        Utc::now() - Duration::days(value)
785    }
786
787    /// Returns a UTC datetime that is `value` weeks in the future from now.
788    #[inline]
789    #[must_use]
790    pub fn utc_weeks_from_now(value: i64) -> DateTime<Utc> {
791        Utc::now() + Duration::weeks(value)
792    }
793
794    /// Returns a UTC datetime that is `value` weeks in the past from now.
795    #[inline]
796    #[must_use]
797    pub fn utc_weeks_ago(value: i64) -> DateTime<Utc> {
798        Utc::now() - Duration::weeks(value)
799    }
800
801    /// Returns a UTC datetime that is `value` months in the future from now.
802    #[must_use]
803    pub fn utc_months_from_now(value: i64) -> DateTime<Utc> {
804        add_months_to_datetime(&Utc::now(), value as i32)
805    }
806
807    /// Returns a UTC datetime that is `value` months in the past from now.
808    #[must_use]
809    pub fn utc_months_ago(value: i64) -> DateTime<Utc> {
810        add_months_to_datetime(&Utc::now(), -(value as i32))
811    }
812
813    /// Returns a UTC datetime that is `value` years in the future from now.
814    #[must_use]
815    pub fn utc_years_from_now(value: i64) -> DateTime<Utc> {
816        add_years_to_datetime(&Utc::now(), value as i32)
817    }
818
819    /// Returns a UTC datetime that is `value` years in the past from now.
820    #[must_use]
821    pub fn utc_years_ago(value: i64) -> DateTime<Utc> {
822        add_years_to_datetime(&Utc::now(), -(value as i32))
823    }
824
825    /// Returns a UTC datetime that is `value` decades in the future from now.
826    #[must_use]
827    pub fn utc_decades_from_now(value: i64) -> DateTime<Utc> {
828        add_years_to_datetime(&Utc::now(), value as i32 * 10)
829    }
830
831    /// Returns a UTC datetime that is `value` decades in the past from now.
832    #[must_use]
833    pub fn utc_decades_ago(value: i64) -> DateTime<Utc> {
834        add_years_to_datetime(&Utc::now(), -(value as i32) * 10)
835    }
836
837    /// Returns a UTC datetime that is `value` centuries in the future from now.
838    #[must_use]
839    pub fn utc_centuries_from_now(value: i64) -> DateTime<Utc> {
840        add_years_to_datetime(&Utc::now(), value as i32 * 100)
841    }
842
843    /// Returns a UTC datetime that is `value` centuries in the past from now.
844    #[must_use]
845    pub fn utc_centuries_ago(value: i64) -> DateTime<Utc> {
846        add_years_to_datetime(&Utc::now(), -(value as i32) * 100)
847    }
848
849    /// Returns a UTC datetime that is `value` millenniums in the future from now.
850    #[must_use]
851    pub fn utc_millenniums_from_now(value: i64) -> DateTime<Utc> {
852        add_years_to_datetime(&Utc::now(), value as i32 * 1000)
853    }
854
855    /// Returns a UTC datetime that is `value` millenniums in the past from now.
856    #[must_use]
857    pub fn utc_millenniums_ago(value: i64) -> DateTime<Utc> {
858        add_years_to_datetime(&Utc::now(), -(value as i32) * 1000)
859    }
860
861    // ------------------------------------------------------------------
862    //           Utility Functions
863    // ------------------------------------------------------------------
864
865    /// Checks if a year is a leap year.
866    ///
867    /// # Example
868    /// ```rust
869    /// use easy_time::EasyTime;
870    /// use chrono::Local;
871    ///
872    /// assert!(EasyTime::<Local>::is_leap_year(2024));
873    /// assert!(!EasyTime::<Local>::is_leap_year(2023));
874    /// ```
875    #[inline]
876    #[must_use]
877    pub fn is_leap_year(year: i32) -> bool {
878        is_leap_year(year)
879    }
880
881    /// Returns the number of days in a given month.
882    ///
883    /// # Example
884    /// ```rust
885    /// use easy_time::EasyTime;
886    /// use chrono::Local;
887    ///
888    /// assert_eq!(EasyTime::<Local>::days_in_month(2024, 2), 29); // Leap year
889    /// assert_eq!(EasyTime::<Local>::days_in_month(2023, 2), 28); // Non-leap year
890    /// ```
891    #[inline]
892    #[must_use]
893    pub fn days_in_month(year: i32, month: u32) -> u32 {
894        days_in_month(year, month)
895    }
896}
897
898// ----------------------------------------------------------
899//           Formatting Utilities (standalone functions)
900// ----------------------------------------------------------
901
902/// Formats a datetime using the default format: `YYYY-MM-DD HH:MM:SS`.
903#[must_use]
904pub fn format_datetime<F: TimeZone>(time: &DateTime<F>) -> String
905where
906    F::Offset: std::fmt::Display,
907{
908    time.format(DEFAULT_DATE_FORMAT).to_string()
909}
910
911/// Formats a datetime using a custom format string.
912#[must_use]
913pub fn format_datetime_with<F: TimeZone>(time: &DateTime<F>, format_str: &str) -> String
914where
915    F::Offset: std::fmt::Display,
916{
917    time.format(format_str).to_string()
918}
919
920/// Formats a datetime with timezone offset appended.
921#[must_use]
922pub fn format_datetime_with_timezone<F: TimeZone>(time: &DateTime<F>) -> String
923where
924    F::Offset: std::fmt::Display,
925{
926    format!("{} {}", time.format(DEFAULT_DATE_FORMAT), time.offset())
927}
928
929/// Formats a datetime with custom format and timezone offset.
930#[must_use]
931pub fn format_datetime_with_timezone_format<F: TimeZone>(
932    time: &DateTime<F>,
933    format_str: &str,
934) -> String
935where
936    F::Offset: std::fmt::Display,
937{
938    format!("{} {}", time.format(format_str), time.offset())
939}
940
941/// Returns just the date portion as `YYYY-MM-DD`.
942#[must_use]
943pub fn to_date<F: TimeZone>(time: &DateTime<F>) -> String
944where
945    F::Offset: std::fmt::Display,
946{
947    time.format(DATE_FORMAT).to_string()
948}
949
950/// Returns just the time portion as `HH:MM:SS`.
951#[must_use]
952pub fn to_time<F: TimeZone>(time: &DateTime<F>) -> String
953where
954    F::Offset: std::fmt::Display,
955{
956    time.format(TIME_FORMAT).to_string()
957}
958
959/// Returns the Unix timestamp (seconds since epoch).
960#[must_use]
961pub fn to_timestamp<F: TimeZone>(time: &DateTime<F>) -> i64 {
962    time.timestamp()
963}