use crate::core::date::{NEPALI_MONTHS, NEPALI_MONTHS_UNICODE, NEPALI_WEEKDAYS, NepaliDate};
impl NepaliDate {
pub fn format_date(&self, format_str: &str) -> String {
let mut result = String::new();
let mut chars = format_str.chars().peekable();
while let Some(ch) = chars.next() {
if ch == '%' {
if let Some(&next_ch) = chars.peek() {
chars.next(); match next_ch {
'Y' => result.push_str(&self.year.to_string()),
'y' => result.push_str(&format!("{:02}", self.year % 100)),
'm' => result.push_str(&format!("{:02}", self.month)),
'B' => result.push_str(NEPALI_MONTHS[(self.month - 1) as usize]),
'b' => result.push_str(&NEPALI_MONTHS[(self.month - 1) as usize][..3]),
'd' => result.push_str(&format!("{:02}", self.day)),
'e' => result.push_str(&format!("{:2}", self.day)),
'A' => {
if let Ok((y, m, d)) = self.to_gregorian() {
let weekday = calculate_weekday(y, m, d);
result.push_str(NEPALI_WEEKDAYS[weekday]);
}
}
'K' => result.push_str(&to_devanagari_number(self.year)),
'n' => result.push_str(&to_devanagari_number_padded(self.month as i32, 2)),
'D' => result.push_str(&to_devanagari_number_padded(self.day as i32, 2)),
'N' => result.push_str(NEPALI_MONTHS_UNICODE[(self.month - 1) as usize]),
'G' => {
if let Ok((y, m, d)) = self.to_gregorian() {
let weekday = calculate_weekday(y, m, d);
const DEVANAGARI_WEEKDAYS: [&str; 7] = [
"आइतवार",
"सोमवार",
"मङ्गलवार",
"बुधवार",
"बिहीवार",
"शुक्रवार",
"शनिवार",
];
result.push_str(DEVANAGARI_WEEKDAYS[weekday]);
}
}
'%' => result.push('%'),
_ => {
result.push('%');
result.push(next_ch);
}
}
} else {
result.push('%');
}
} else {
result.push(ch);
}
}
result
}
pub fn format_unicode(&self) -> String {
format!(
"{} {} {}",
to_devanagari_number(self.day as i32),
NEPALI_MONTHS_UNICODE[(self.month - 1) as usize],
to_devanagari_number(self.year)
)
}
pub fn month_calendar(&self) -> String {
let mut result = String::new();
let month_name = NEPALI_MONTHS[(self.month - 1) as usize];
let header = format!("{} {}", month_name, self.year);
result.push_str(&format!("{:^20}\n", header));
result.push_str("Su Mo Tu We Th Fr Sa\n");
let first_day = NepaliDate::new(self.year, self.month, 1).unwrap();
let (g_y, g_m, g_d) = first_day.to_gregorian().unwrap_or((1943, 4, 14));
let start_weekday = calculate_weekday(g_y, g_m, g_d);
for _ in 0..start_weekday {
result.push_str(" ");
}
let days = Self::days_in_month(self.year, self.month).unwrap_or(30);
for day in 1..=days {
result.push_str(&format!("{:2} ", day));
if (day as usize + start_weekday).is_multiple_of(7) {
result.push('\n');
}
}
if !(days as usize + start_weekday).is_multiple_of(7) {
result.push('\n');
}
result
}
}
fn calculate_weekday(year: i32, month: u8, day: u8) -> usize {
let mut y = year;
let mut m = month as i32;
if m < 3 {
m += 12;
y -= 1;
}
let q = day as i32;
let k = y % 100;
let j = y / 100;
let h = (q + (13 * (m + 1)) / 5 + k + k / 4 + j / 4 + 5 * j) % 7;
((h + 6) % 7) as usize
}
fn to_devanagari_number(num: i32) -> String {
const DEVANAGARI_DIGITS: [char; 10] = ['०', '१', '२', '३', '४', '५', '६', '७', '८', '९'];
num.to_string()
.chars()
.map(|c| {
if let Some(digit) = c.to_digit(10) {
DEVANAGARI_DIGITS[digit as usize]
} else {
c
}
})
.collect()
}
fn to_devanagari_number_padded(num: i32, width: usize) -> String {
let s = format!("{:0width$}", num, width = width);
const DEVANAGARI_DIGITS: [char; 10] = ['०', '१', '२', '३', '४', '५', '६', '७', '८', '९'];
s.chars()
.map(|c| {
if let Some(digit) = c.to_digit(10) {
DEVANAGARI_DIGITS[digit as usize]
} else {
c
}
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(any(feature = "lookup-tables", feature = "astronomical"))]
#[test]
fn test_format_year() {
let date = NepaliDate::new(2077, 5, 19).unwrap();
assert_eq!(date.format_date("%Y"), "2077");
assert_eq!(date.format_date("%y"), "77");
}
#[cfg(any(feature = "lookup-tables", feature = "astronomical"))]
#[test]
fn test_format_month() {
let date = NepaliDate::new(2077, 5, 19).unwrap();
assert_eq!(date.format_date("%m"), "05");
assert_eq!(date.format_date("%B"), "Bhadra");
assert_eq!(date.format_date("%b"), "Bha");
}
#[cfg(any(feature = "lookup-tables", feature = "astronomical"))]
#[test]
fn test_format_day() {
let date = NepaliDate::new(2077, 5, 9).unwrap();
assert_eq!(date.format_date("%d"), "09");
assert_eq!(date.format_date("%e"), " 9");
}
#[cfg(any(feature = "lookup-tables", feature = "astronomical"))]
#[test]
fn test_format_combined() {
let date = NepaliDate::new(2077, 5, 19).unwrap();
assert_eq!(date.format_date("%Y-%m-%d"), "2077-05-19");
assert_eq!(date.format_date("%d %B %Y"), "19 Bhadra 2077");
}
#[test]
fn test_devanagari_numbers() {
assert_eq!(to_devanagari_number(2077), "२०७७");
assert_eq!(to_devanagari_number(1), "१");
assert_eq!(to_devanagari_number(19), "१९");
}
#[test]
fn test_weekday_calculation() {
let weekday = calculate_weekday(2020, 9, 4);
assert_eq!(weekday, 5);
}
}