jalali_date/
lib.rs

1static DAY_SUM: [u16; 12] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
2static DAY_SUM_KABISE: [u16; 12] = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335];
3
4#[derive(PartialEq, Eq, Debug)]
5pub struct JalaliDate {
6    pub day: u16,
7    pub year: u16,
8    pub month: u16,
9}
10
11pub fn to_jalali(day: u16, month: u16, year: u16) -> Result<JalaliDate, &'static str> {
12    if month > 12 || month < 1 {
13        return Err("cant do it");
14    }
15
16    let days_sum = if is_kabise(year) {
17        DAY_SUM_KABISE[month as usize - 1] + day
18    } else {
19        DAY_SUM[month as usize - 1] + day
20    };
21
22    if days_sum < 79 {
23        let days_sum = days_sum + dey_jan_diff(year);
24        let jalai_year = year - 622;
25
26        if days_sum % 30 == 0 {
27            return Ok(JalaliDate {
28                year: jalai_year,
29                day: 30,
30                month: (days_sum / 30) + 9,
31            });
32        } else {
33            return Ok(JalaliDate {
34                year: jalai_year,
35                day: days_sum % 30,
36                month: (days_sum / 30) + 10,
37            });
38        }
39    } else {
40        let days_sum = days_sum - 79;
41        let jalali_year = year - 621;
42
43        if days_sum <= 186 {
44            if days_sum % 31 == 0 {
45                return Ok(JalaliDate {
46                    day: 31,
47                    year: jalali_year,
48                    month: days_sum / 31,
49                });
50            } else {
51                return Ok(JalaliDate {
52                    day: days_sum % 31,
53                    year: jalali_year,
54                    month: (days_sum / 31) + 1,
55                });
56            }
57        } else {
58            let days_sum = days_sum - 186;
59            if days_sum % 30 == 0 {
60                return Ok(JalaliDate {
61                    day: 30,
62                    year: jalali_year,
63                    month: (days_sum / 30) + 6,
64                });
65            } else {
66                return Ok(JalaliDate {
67                    day: days_sum % 30,
68                    year: jalali_year,
69                    month: (days_sum / 30) + 7,
70                });
71            }
72        }
73    }
74}
75
76fn dey_jan_diff(year: u16) -> u16 {
77    if is_kabise(year) {
78        return 11;
79    }
80
81    return 10;
82}
83
84fn is_kabise(year: u16) -> bool {
85    if year % 4 == 0 && year % 100 != 0 {
86        return true;
87    }
88
89    if year % 400 == 0 && year % 100 == 0 {
90        return true;
91    }
92
93    false
94}
95
96fn year_is_leap(gregorian_year: i32) -> bool {
97    return ((gregorian_year % 100) != 0 && (gregorian_year % 4) == 0)
98        || ((gregorian_year % 100) == 0 && (gregorian_year % 400) == 0);
99}
100
101static gregorian_months: [i32; 12] = [30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 28, 31];
102static gregorian_month_leap: [i32; 12] = [30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29, 31];
103
104#[derive(Debug, PartialEq, Eq)]
105pub struct GregorianDate {
106    pub year: i32,
107    pub month: i32,
108    pub day: i32,
109}
110
111/// month range is 1..12
112/// day starts from 1
113pub fn jalali_to_gregorian(year: i32, month: i32, day: i32) -> GregorianDate {
114    let mut gregorian_year = year + 621;
115    let mut gregorian_day_of_month = 0;
116    let mut gregorian_month = 0;
117    let march_day_diff = if year_is_leap(gregorian_year) { 12 } else { 11 };
118    let mut day_count = 0;
119
120    if (1..=6).contains(&month) {
121        day_count = (month - 1) * 31 + day;
122    } else {
123        day_count = (6 * 31) + (month - 7) * 30 + day;
124    }
125
126    if day_count < march_day_diff {
127        gregorian_day_of_month = day_count + (31 - march_day_diff);
128        gregorian_month = 3;
129    } else {
130        let mut remain_days = day_count - march_day_diff;
131        let mut i = 0;
132
133        if year_is_leap(gregorian_year + 1) {
134            while remain_days > gregorian_months[i] {
135                remain_days -= gregorian_month_leap[i];
136                i += 1;
137            }
138        } else {
139            while remain_days > gregorian_months[i] {
140                remain_days -= gregorian_months[i];
141                i += 1;
142            }
143        }
144
145        gregorian_day_of_month = remain_days;
146
147        if i > 8 {
148            gregorian_month = i - 8;
149            gregorian_year += 1;
150        } else {
151            gregorian_month = i + 4;
152        }
153    }
154
155    GregorianDate {
156        year: gregorian_year,
157        month: gregorian_month as i32,
158        day: gregorian_day_of_month,
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use crate::{jalali_to_gregorian, to_jalali, GregorianDate, JalaliDate};
165
166    #[test]
167    fn convert_date() {
168        let result = to_jalali(6, 2, 2022).unwrap();
169        assert_eq!(
170            result,
171            JalaliDate {
172                day: 17,
173                month: 11,
174                year: 1400
175            }
176        );
177    }
178
179    #[test]
180    fn convert_date_2() {
181        let result = to_jalali(23, 11, 2015).unwrap();
182        assert_eq!(
183            result,
184            JalaliDate {
185                day: 2,
186                month: 9,
187                year: 1394
188            }
189        );
190    }
191
192    #[test]
193    fn jalali_to_gregorian_date() {
194        let result = jalali_to_gregorian(1402, 8, 24);
195        assert_eq!(
196            result,
197            GregorianDate {
198                day: 15,
199                month: 11,
200                year: 2023
201            }
202        );
203    }
204
205    #[test]
206    fn jalali_to_gregorian_date_2() {
207        let result = jalali_to_gregorian(1402, 3, 3);
208        assert_eq!(
209            result,
210            GregorianDate {
211                day: 24,
212                month: 5,
213                year: 2023
214            }
215        );
216    }
217}