ponjika/
calendar.rs

1//! # Calendar: Calendar related common functions
2//! The module contains common functions related to calendar
3//! The functions are used to format the date in Bengali and English
4
5use chrono::{Datelike, Local};
6use std::convert::TryInto;
7
8use crate::{
9    BengaliDate, BengaliMonths, BengaliWeekDays, Date, DateError, EnglishDate, EnglishMonths,
10};
11
12fn is_leap_year(year: u16) -> bool {
13    year % 4 == 0
14}
15
16/// converts Gregorian date to Bengali date
17/// # Arguments
18/// * `english_date` - EnglishDate
19/// # Returns
20/// * `Date` - Bengali date
21/// # Note
22/// * The function will return `DateError` if the conversion fails
23fn gregorian_to_bengali_date(english_date: EnglishDate) -> Result<Date, DateError> {
24    let (english_day, english_month, english_year) = english_date.get_date();
25
26    // The Bengali year starts from 14th April
27    // If the English date is before 14th April, the Bengali year is the English year - 594
28    // Otherwise, the Bengali year is the English year - 593
29    let bengali_year: u16 = if english_month < 4 || (english_month == 4 && english_day < 14) {
30        match english_year.checked_sub(594) {
31            Some(year) => year,
32            None => {
33                return Err(DateError::ArithmeticError);
34            }
35        }
36    } else {
37        match english_year.checked_sub(593) {
38            Some(year) => year,
39            None => {
40                return Err(DateError::ArithmeticError);
41            }
42        }
43    };
44
45    // get the Bengali day and month
46    let (bengali_day, bengali_month) = if english_month == 1 {
47        if english_day <= 14 {
48            (english_day + 16, 9)
49        } else {
50            (english_day - 14, 10)
51        }
52    } else if english_month == 2 {
53        if english_day <= 13 {
54            (english_day + 17, 10)
55        } else {
56            (english_day - 13, 11)
57        }
58    } else if english_month == 3 {
59        if english_day <= 14 {
60            if is_leap_year(english_year) {
61                (english_day + 16, 11)
62            } else {
63                (english_day + 15, 11)
64            }
65        } else {
66            (english_day - 14, 12)
67        }
68    } else if english_month == 4 {
69        if english_day <= 13 {
70            (english_day + 17, 12)
71        } else {
72            (english_day - 13, 1)
73        }
74    } else if english_month == 5 {
75        if english_day <= 14 {
76            (english_day + 17, 1)
77        } else {
78            (english_day - 14, 2)
79        }
80    } else if english_month == 6 {
81        if english_day <= 14 {
82            (english_day + 17, 2)
83        } else {
84            (english_day - 14, 3)
85        }
86    } else if english_month == 7 {
87        if english_day <= 15 {
88            (english_day + 16, 3)
89        } else {
90            (english_day - 15, 4)
91        }
92    } else if english_month == 8 {
93        if english_day <= 15 {
94            (english_day + 16, 4)
95        } else {
96            (english_day - 15, 5)
97        }
98    } else if english_month == 9 {
99        if english_day <= 15 {
100            (english_day + 16, 5)
101        } else {
102            (english_day - 15, 6)
103        }
104    } else if english_month == 10 {
105        if english_day <= 16 {
106            (english_day + 15, 6)
107        } else {
108            (english_day - 16, 7)
109        }
110    } else if english_month == 11 {
111        if english_day <= 15 {
112            (english_day + 15, 7)
113        } else {
114            (english_day - 15, 8)
115        }
116    } else if english_month == 12 {
117        if english_day <= 15 {
118            (english_day + 15, 8)
119        } else {
120            (english_day - 15, 9)
121        }
122    } else {
123        return Err(DateError::WrongDay);
124    };
125
126    let bengali_weekday =
127        BengaliWeekDays::get_english_weekday(english_date.get_week_day().unwrap().as_str());
128
129    match BengaliDate::create_date_with_weekday(
130        bengali_day,
131        bengali_weekday.unwrap(),
132        BengaliMonths::get_month(bengali_month).unwrap(),
133        bengali_year,
134    ) {
135        Ok(bengali_date) => Ok(Date::Bengali(bengali_date)),
136        Err(err) => Err(err),
137    }
138}
139
140/// converts Bengali date to Gregorian date
141/// # Arguments
142/// * `bengali_date` - BengaliDate
143/// # Returns
144/// * `Date` - Gregorian date
145/// # Note
146/// * The function will return `DateError` if the conversion fails
147fn bengali_to_gregorian_date(bengali_date: BengaliDate) -> Result<Date, DateError> {
148    let (bengali_day, bengali_month, bengali_year) = bengali_date.get_date();
149
150    let english_year: u16 = if (bengali_month == 9 && bengali_day >= 17)
151        || (bengali_month == 12 && bengali_day <= 30)
152        || bengali_month == 10
153        || bengali_month == 11
154    {
155        match bengali_year.checked_add(594) {
156            Some(year) => year,
157            None => return Err(DateError::ArithmeticError),
158        }
159    } else {
160        match bengali_year.checked_add(593) {
161            Some(year) => year,
162            None => return Err(DateError::ArithmeticError),
163        }
164    };
165
166    let (english_date, english_month) = if bengali_month == 9 {
167        if bengali_day >= 17 {
168            (bengali_day - 16, 1)
169        } else {
170            (bengali_day + 15, 12)
171        }
172    } else if bengali_month == 10 {
173        if bengali_day >= 18 {
174            (bengali_day - 17, 2)
175        } else {
176            (bengali_day + 14, 1)
177        }
178    } else if bengali_month == 11 {
179        if is_leap_year(english_year) {
180            if bengali_day >= 17 {
181                (bengali_day - 16, 3)
182            } else {
183                (bengali_day + 13, 2)
184            }
185        } else {
186            if bengali_day >= 16 {
187                (bengali_day - 15, 3)
188            } else {
189                (bengali_day + 13, 2)
190            }
191        }
192    } else if bengali_month == 12 {
193        if bengali_day >= 18 {
194            (bengali_day - 17, 4)
195        } else {
196            (bengali_day + 14, 3)
197        }
198    } else if bengali_month == 1 {
199        if bengali_day >= 18 {
200            (bengali_day - 17, 5)
201        } else {
202            (bengali_day + 13, 4)
203        }
204    } else if bengali_month == 2 {
205        if bengali_day >= 18 {
206            (bengali_day - 17, 6)
207        } else {
208            (bengali_day + 14, 5)
209        }
210    } else if bengali_month == 3 {
211        if bengali_day >= 17 {
212            (bengali_day - 16, 7)
213        } else {
214            (bengali_day + 14, 6)
215        }
216    } else if bengali_month == 4 {
217        if bengali_day >= 17 {
218            (bengali_day - 16, 8)
219        } else {
220            (bengali_day + 15, 7)
221        }
222    } else if bengali_month == 5 {
223        if bengali_day >= 17 {
224            (bengali_day - 16, 9)
225        } else {
226            (bengali_day + 15, 8)
227        }
228    } else if bengali_month == 6 {
229        if bengali_day >= 16 {
230            (bengali_day - 15, 10)
231        } else {
232            (bengali_day + 15, 9)
233        }
234    } else if bengali_month == 7 {
235        if bengali_day >= 16 {
236            (bengali_day - 15, 11)
237        } else {
238            (bengali_day + 16, 10)
239        }
240    } else if bengali_month == 8 {
241        if bengali_day >= 16 {
242            (bengali_day - 15, 12)
243        } else {
244            (bengali_day + 15, 11)
245        }
246    } else {
247        return Err(DateError::WrongDay);
248    };
249
250    match EnglishDate::create_date(
251        english_date,
252        EnglishMonths::get_month(english_month).unwrap(),
253        english_year,
254    ) {
255        Ok(english_date) => Ok(Date::English(english_date)),
256        Err(err) => Err(err),
257    }
258}
259
260/// Get today's Bengali date
261/// # Returns
262/// * `Result<Date, DateError>` - Bengali date
263/// # Example
264/// ```
265/// use ponjika::calendar;
266/// let today = calendar::get_today_bengali_date();
267/// match today {
268///   Ok(bengali_date) => {
269///     println!("{}", bengali_date.to_string());
270///   }
271///   Err(_) => {
272///     eprintln!("The date is not a valid greogrian date");
273///   }
274/// }
275/// ```
276/// # Note
277/// * The function will return `DateError` if the system date is invalid
278pub fn get_today_bengali_date() -> Result<Date, DateError> {
279    let today = Local::now();
280
281    let today_day: u8 = match today.day().try_into() {
282        Ok(day) => day,
283        Err(err) => {
284            return Err(DateError::CastingError(err)); // or handle the error as needed
285        }
286    };
287
288    let today_month: u8 = match today.month().try_into() {
289        Ok(month) => month,
290        Err(err) => {
291            return Err(DateError::CastingError(err)); // or handle the error as needed
292        }
293    };
294
295    let today_year: u16 = match today.year().try_into() {
296        Ok(year) => year,
297        Err(err) => {
298            return Err(DateError::CastingError(err)); // or handle the error as needed
299        }
300    };
301
302    match EnglishMonths::get_month(today_month) {
303        Ok(month) => {
304            let english_date = EnglishDate::create_date(today_day, month, today_year);
305
306            match english_date {
307                Ok(date) => match gregorian_to_bengali_date(date) {
308                    Ok(bengali_date) => Ok(bengali_date),
309                    Err(err) => Err(err),
310                },
311                Err(err) => return Err(err),
312            }
313        }
314        Err(err) => match err {
315            _ => return Err(DateError::WrongMonth(err)),
316        },
317    }
318}
319
320/// Get Bengali date from Gregorian date
321/// # Arguments
322/// * `english_date` - EnglishDate
323/// # Returns
324/// * `Result<Date, DateError>` - Bengali date
325/// # Example
326/// ```
327/// use ponjika::{calendar, EnglishDate, EnglishMonths};
328/// let date = EnglishDate::create_date(10, EnglishMonths::October, 2010);
329/// match date {
330///  Ok(english_date) => {
331///   let bengali_date = calendar::get_bengali_date_from_gregorian(english_date);
332///   match bengali_date {
333///    Ok(date) => {
334///     println!("{}", date.to_string());
335///    }
336///    Err(_) => {
337///     eprintln!("Failed to convert to Bengali date");
338///    }
339///   }
340///  }
341///  Err(_) => {
342///   eprintln!("The date is not a valid greogrian date");
343///  }
344/// }
345/// ```
346/// # Note
347/// * The function will return `DateError` if the conversion fails
348pub fn get_bengali_date_from_gregorian(english_date: EnglishDate) -> Result<Date, DateError> {
349    match gregorian_to_bengali_date(english_date) {
350        Ok(date) => Ok(date),
351        Err(err) => Err(err),
352    }
353}
354
355/// Get Gregorian date from Bengali date
356/// # Arguments
357/// * `bengali_date` - BengaliDate
358/// # Returns
359/// * `Result<Date, DateError>` - Gregorian date
360/// # Example
361/// ```
362/// use ponjika::{calendar, BengaliDate, BengaliMonths};
363/// let bengali_date = BengaliDate::create_date(3, BengaliMonths::Falgun, 1430);
364/// match bengali_date {
365///   Ok(bengali_date) => {
366///     let gregorian_date = calendar::get_gregorian_date_from_bengali(bengali_date);
367///     match gregorian_date {
368///       Ok(date) => {
369///         println!("Gregorian Date: {}", date.to_string());
370///       }
371///       Err(_) => {
372///         eprintln!("Failed to convert to Gregorian date");
373///       }
374///     }
375///   }
376///   Err(_) => {
377///     eprintln!("The date is not a valid Bengali date");
378///   }
379/// }
380/// ```
381/// # Note
382/// * The function will return `DateError` if the conversion fails
383pub fn get_gregorian_date_from_bengali(bengali_date: BengaliDate) -> Result<Date, DateError> {
384    match bengali_to_gregorian_date(bengali_date) {
385        Ok(date) => Ok(date),
386        Err(err) => Err(err),
387    }
388}