1- use chrono:: { DateTime , Datelike , TimeZone , Timelike , Utc } ;
1+ use chrono:: { DateTime , Datelike , Duration , TimeZone , Timelike , Utc , Weekday } ;
22
33pub fn add_months ( dt : DateTime < Utc > , months : u32 ) -> DateTime < Utc > {
44 let mut year = dt. year ( ) ;
@@ -23,3 +23,83 @@ pub fn days_in_month(year: i32, month: u32) -> u32 {
2323 . unwrap ( ) ;
2424 ( next_first_day - first_day) . num_days ( ) as u32
2525}
26+
27+ pub fn add_workdays ( mut date : DateTime < Utc > , mut n : i64 ) -> DateTime < Utc > {
28+ while n > 0 {
29+ date = date + Duration :: days ( 1 ) ;
30+ match date. weekday ( ) {
31+ Weekday :: Sat | Weekday :: Sun => continue ,
32+ _ => n -= 1 ,
33+ }
34+ }
35+ date
36+ }
37+
38+ #[ cfg( test) ]
39+ mod tests {
40+ use super :: * ;
41+ use chrono:: TimeZone ;
42+
43+ #[ test]
44+ fn test_days_in_month ( ) {
45+ assert_eq ! ( days_in_month( 2024 , 2 ) , 29 ) ; // Leap year
46+ assert_eq ! ( days_in_month( 2023 , 2 ) , 28 ) ;
47+ assert_eq ! ( days_in_month( 2023 , 1 ) , 31 ) ;
48+ assert_eq ! ( days_in_month( 2023 , 4 ) , 30 ) ;
49+ }
50+
51+ #[ test]
52+ fn test_add_months_basic ( ) {
53+ let date = Utc . with_ymd_and_hms ( 2023 , 1 , 15 , 12 , 0 , 0 ) . unwrap ( ) ;
54+ let result = add_months ( date, 1 ) ;
55+ assert_eq ! ( result, Utc . with_ymd_and_hms( 2023 , 2 , 15 , 12 , 0 , 0 ) . unwrap( ) ) ;
56+ }
57+
58+ #[ test]
59+ fn test_add_months_wrap_year ( ) {
60+ let date = Utc . with_ymd_and_hms ( 2023 , 11 , 30 , 9 , 0 , 0 ) . unwrap ( ) ;
61+ let result = add_months ( date, 2 ) ;
62+ assert_eq ! ( result, Utc . with_ymd_and_hms( 2024 , 1 , 30 , 9 , 0 , 0 ) . unwrap( ) ) ;
63+ }
64+
65+ #[ test]
66+ fn test_add_months_day_adjustment ( ) {
67+ let date = Utc . with_ymd_and_hms ( 2023 , 1 , 31 , 8 , 0 , 0 ) . unwrap ( ) ;
68+ let result = add_months ( date, 1 ) ;
69+ // February 2023 only has 28 days
70+ assert_eq ! ( result, Utc . with_ymd_and_hms( 2023 , 2 , 28 , 8 , 0 , 0 ) . unwrap( ) ) ;
71+ }
72+
73+ #[ test]
74+ fn test_add_workdays_simple ( ) {
75+ let monday = Utc . with_ymd_and_hms ( 2023 , 7 , 24 , 10 , 0 , 0 ) . unwrap ( ) ; // Monday
76+ let result = add_workdays ( monday, 5 ) ;
77+ assert_eq ! ( result. weekday( ) , Weekday :: Mon ) ;
78+ assert_eq ! (
79+ result. date_naive( ) ,
80+ monday. date_naive( ) + chrono:: Days :: new( 7 )
81+ ) ;
82+ }
83+
84+ #[ test]
85+ fn test_add_workdays_over_weekend ( ) {
86+ let friday = Utc . with_ymd_and_hms ( 2023 , 7 , 21 , 10 , 0 , 0 ) . unwrap ( ) ; // Friday
87+ let result = add_workdays ( friday, 1 ) ;
88+ assert_eq ! ( result. weekday( ) , Weekday :: Mon ) ;
89+ assert_eq ! (
90+ result. date_naive( ) ,
91+ friday. date_naive( ) + chrono:: Days :: new( 3 )
92+ ) ;
93+ }
94+
95+ #[ test]
96+ fn test_add_workdays_multiple_weeks ( ) {
97+ let tuesday = Utc . with_ymd_and_hms ( 2023 , 7 , 18 , 10 , 0 , 0 ) . unwrap ( ) ; // Tuesday
98+ let result = add_workdays ( tuesday, 10 ) ;
99+ assert_eq ! ( result. weekday( ) , Weekday :: Tue ) ;
100+ assert_eq ! (
101+ result. date_naive( ) ,
102+ tuesday. date_naive( ) + chrono:: Days :: new( 14 )
103+ ) ;
104+ }
105+ }
0 commit comments