1#![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
41pub const DEFAULT_DATE_FORMAT: &str = "%Y-%m-%d %H:%M:%S";
43pub const DATE_FORMAT: &str = "%Y-%m-%d";
45pub const TIME_FORMAT: &str = "%H:%M:%S";
47
48const DAYS_IN_MONTH: [u32; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
50
51#[derive(Clone, PartialEq, Debug, Eq)]
77pub struct EasyTime<F: TimeZone> {
78 _marker: std::marker::PhantomData<F>,
79}
80
81#[inline]
87const fn is_leap_year(year: i32) -> bool {
88 (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
89}
90
91#[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
101fn 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
110fn 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
130fn 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
145impl EasyTime<Local> {
149 #[inline]
163 #[must_use]
164 pub fn seconds_from_now(value: i64) -> DateTime<Local> {
165 Local::now() + Duration::seconds(value)
166 }
167
168 #[inline]
170 #[must_use]
171 pub fn seconds_ago(value: i64) -> DateTime<Local> {
172 Local::now() - Duration::seconds(value)
173 }
174
175 #[inline]
177 #[must_use]
178 pub fn minutes_from_now(value: i64) -> DateTime<Local> {
179 Local::now() + Duration::minutes(value)
180 }
181
182 #[inline]
184 #[must_use]
185 pub fn minutes_ago(value: i64) -> DateTime<Local> {
186 Local::now() - Duration::minutes(value)
187 }
188
189 #[inline]
191 #[must_use]
192 pub fn hours_from_now(value: i64) -> DateTime<Local> {
193 Local::now() + Duration::hours(value)
194 }
195
196 #[inline]
198 #[must_use]
199 pub fn hours_ago(value: i64) -> DateTime<Local> {
200 Local::now() - Duration::hours(value)
201 }
202
203 #[inline]
205 #[must_use]
206 pub fn days_from_now(value: i64) -> DateTime<Local> {
207 Local::now() + Duration::days(value)
208 }
209
210 #[inline]
212 #[must_use]
213 pub fn days_ago(value: i64) -> DateTime<Local> {
214 Local::now() - Duration::days(value)
215 }
216
217 #[inline]
219 #[must_use]
220 pub fn weeks_from_now(value: i64) -> DateTime<Local> {
221 Local::now() + Duration::weeks(value)
222 }
223
224 #[inline]
226 #[must_use]
227 pub fn weeks_ago(value: i64) -> DateTime<Local> {
228 Local::now() - Duration::weeks(value)
229 }
230
231 #[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 #[must_use]
245 pub fn months_ago(value: i64) -> DateTime<Local> {
246 add_months_to_datetime(&Local::now(), -(value as i32))
247 }
248
249 #[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 #[must_use]
261 pub fn years_ago(value: i64) -> DateTime<Local> {
262 add_years_to_datetime(&Local::now(), -(value as i32))
263 }
264
265 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
307 #[must_use]
308 pub fn seconds_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
309 base + Duration::seconds(value)
310 }
311
312 #[inline]
314 #[must_use]
315 pub fn seconds_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
316 base - Duration::seconds(value)
317 }
318
319 #[inline]
321 #[must_use]
322 pub fn minutes_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
323 base + Duration::minutes(value)
324 }
325
326 #[inline]
328 #[must_use]
329 pub fn minutes_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
330 base - Duration::minutes(value)
331 }
332
333 #[inline]
335 #[must_use]
336 pub fn hours_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
337 base + Duration::hours(value)
338 }
339
340 #[inline]
342 #[must_use]
343 pub fn hours_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
344 base - Duration::hours(value)
345 }
346
347 #[inline]
349 #[must_use]
350 pub fn days_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
351 base + Duration::days(value)
352 }
353
354 #[inline]
356 #[must_use]
357 pub fn days_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
358 base - Duration::days(value)
359 }
360
361 #[inline]
363 #[must_use]
364 pub fn weeks_from(base: DateTime<Local>, value: i64) -> DateTime<Local> {
365 base + Duration::weeks(value)
366 }
367
368 #[inline]
370 #[must_use]
371 pub fn weeks_before(base: DateTime<Local>, value: i64) -> DateTime<Local> {
372 base - Duration::weeks(value)
373 }
374
375 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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
436impl EasyTime<Utc> {
440 #[inline]
446 #[must_use]
447 pub fn seconds_from_now(value: i64) -> DateTime<Utc> {
448 Utc::now() + Duration::seconds(value)
449 }
450
451 #[inline]
453 #[must_use]
454 pub fn seconds_ago(value: i64) -> DateTime<Utc> {
455 Utc::now() - Duration::seconds(value)
456 }
457
458 #[inline]
460 #[must_use]
461 pub fn minutes_from_now(value: i64) -> DateTime<Utc> {
462 Utc::now() + Duration::minutes(value)
463 }
464
465 #[inline]
467 #[must_use]
468 pub fn minutes_ago(value: i64) -> DateTime<Utc> {
469 Utc::now() - Duration::minutes(value)
470 }
471
472 #[inline]
474 #[must_use]
475 pub fn hours_from_now(value: i64) -> DateTime<Utc> {
476 Utc::now() + Duration::hours(value)
477 }
478
479 #[inline]
481 #[must_use]
482 pub fn hours_ago(value: i64) -> DateTime<Utc> {
483 Utc::now() - Duration::hours(value)
484 }
485
486 #[inline]
488 #[must_use]
489 pub fn days_from_now(value: i64) -> DateTime<Utc> {
490 Utc::now() + Duration::days(value)
491 }
492
493 #[inline]
495 #[must_use]
496 pub fn days_ago(value: i64) -> DateTime<Utc> {
497 Utc::now() - Duration::days(value)
498 }
499
500 #[inline]
502 #[must_use]
503 pub fn weeks_from_now(value: i64) -> DateTime<Utc> {
504 Utc::now() + Duration::weeks(value)
505 }
506
507 #[inline]
509 #[must_use]
510 pub fn weeks_ago(value: i64) -> DateTime<Utc> {
511 Utc::now() - Duration::weeks(value)
512 }
513
514 #[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 #[must_use]
526 pub fn months_ago(value: i64) -> DateTime<Utc> {
527 add_months_to_datetime(&Utc::now(), -(value as i32))
528 }
529
530 #[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 #[must_use]
542 pub fn years_ago(value: i64) -> DateTime<Utc> {
543 add_years_to_datetime(&Utc::now(), -(value as i32))
544 }
545
546 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
588 #[must_use]
589 pub fn seconds_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
590 base + Duration::seconds(value)
591 }
592
593 #[inline]
595 #[must_use]
596 pub fn seconds_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
597 base - Duration::seconds(value)
598 }
599
600 #[inline]
602 #[must_use]
603 pub fn minutes_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
604 base + Duration::minutes(value)
605 }
606
607 #[inline]
609 #[must_use]
610 pub fn minutes_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
611 base - Duration::minutes(value)
612 }
613
614 #[inline]
616 #[must_use]
617 pub fn hours_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
618 base + Duration::hours(value)
619 }
620
621 #[inline]
623 #[must_use]
624 pub fn hours_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
625 base - Duration::hours(value)
626 }
627
628 #[inline]
630 #[must_use]
631 pub fn days_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
632 base + Duration::days(value)
633 }
634
635 #[inline]
637 #[must_use]
638 pub fn days_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
639 base - Duration::days(value)
640 }
641
642 #[inline]
644 #[must_use]
645 pub fn weeks_from(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
646 base + Duration::weeks(value)
647 }
648
649 #[inline]
651 #[must_use]
652 pub fn weeks_before(base: DateTime<Utc>, value: i64) -> DateTime<Utc> {
653 base - Duration::weeks(value)
654 }
655
656 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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
717impl<F: TimeZone> EasyTime<F> {
721 #[inline]
733 #[must_use]
734 pub fn utc_seconds_from_now(value: i64) -> DateTime<Utc> {
735 Utc::now() + Duration::seconds(value)
736 }
737
738 #[inline]
740 #[must_use]
741 pub fn utc_seconds_ago(value: i64) -> DateTime<Utc> {
742 Utc::now() - Duration::seconds(value)
743 }
744
745 #[inline]
747 #[must_use]
748 pub fn utc_minutes_from_now(value: i64) -> DateTime<Utc> {
749 Utc::now() + Duration::minutes(value)
750 }
751
752 #[inline]
754 #[must_use]
755 pub fn utc_minutes_ago(value: i64) -> DateTime<Utc> {
756 Utc::now() - Duration::minutes(value)
757 }
758
759 #[inline]
761 #[must_use]
762 pub fn utc_hours_from_now(value: i64) -> DateTime<Utc> {
763 Utc::now() + Duration::hours(value)
764 }
765
766 #[inline]
768 #[must_use]
769 pub fn utc_hours_ago(value: i64) -> DateTime<Utc> {
770 Utc::now() - Duration::hours(value)
771 }
772
773 #[inline]
775 #[must_use]
776 pub fn utc_days_from_now(value: i64) -> DateTime<Utc> {
777 Utc::now() + Duration::days(value)
778 }
779
780 #[inline]
782 #[must_use]
783 pub fn utc_days_ago(value: i64) -> DateTime<Utc> {
784 Utc::now() - Duration::days(value)
785 }
786
787 #[inline]
789 #[must_use]
790 pub fn utc_weeks_from_now(value: i64) -> DateTime<Utc> {
791 Utc::now() + Duration::weeks(value)
792 }
793
794 #[inline]
796 #[must_use]
797 pub fn utc_weeks_ago(value: i64) -> DateTime<Utc> {
798 Utc::now() - Duration::weeks(value)
799 }
800
801 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[inline]
876 #[must_use]
877 pub fn is_leap_year(year: i32) -> bool {
878 is_leap_year(year)
879 }
880
881 #[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#[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#[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#[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#[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#[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#[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#[must_use]
961pub fn to_timestamp<F: TimeZone>(time: &DateTime<F>) -> i64 {
962 time.timestamp()
963}