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
111pub 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}