use super::civil::{day_of_week, days_in_month, days_to_ymd, ymd_to_days};
use super::config::CalendarConfig;
pub fn week_of_year_iso(year: i32, month: u32, day: u32) -> (i32, u32) {
let days = ymd_to_days(year, month, day);
let weekday = day_of_week(days);
let thursday_offset = 3 - weekday as i64;
let thursday_days = days + thursday_offset;
let (thursday_year, _thursday_month, _thursday_day) = days_to_ymd(thursday_days);
let jan4_days = ymd_to_days(thursday_year, 1, 4);
let jan4_weekday = day_of_week(jan4_days);
let week1_monday = jan4_days - jan4_weekday as i64;
let days_since_week1_monday = thursday_days - week1_monday;
let week_num = (days_since_week1_monday / 7 + 1) as u32;
(thursday_year, week_num)
}
pub fn week_of_year_custom(
year: i32,
month: u32,
day: u32,
config: &CalendarConfig,
) -> u32 {
let days = ymd_to_days(year, month, day);
let jan1_days = ymd_to_days(year, 1, 1);
let jan1_weekday = day_of_week(jan1_days);
let adjusted_jan1_weekday = (jan1_weekday + 7 - config.week_start) % 7;
let week1_start = jan1_days - adjusted_jan1_weekday as i64;
let days_since_week1 = days - week1_start;
((days_since_week1 / 7) + 1) as u32
}
pub fn week_of_month(year: i32, month: u32, day: u32, config: &CalendarConfig) -> u32 {
let first_day_days = ymd_to_days(year, month, 1);
let first_day_weekday = day_of_week(first_day_days);
let adjusted_first_weekday = (first_day_weekday + 7 - config.week_start) % 7;
let week1_day = if adjusted_first_weekday == 0 {
1
} else {
8 - adjusted_first_weekday
};
if day < week1_day {
1
} else {
((day - week1_day) / 7) + 2
}
}
#[inline]
pub fn quarter_of_year(month: u32) -> u32 {
(month - 1) / 3 + 1
}
pub fn fiscal_quarter(_year: i32, month: u32, config: &CalendarConfig) -> u32 {
let offset = if month >= config.fiscal_quarter_start {
month - config.fiscal_quarter_start
} else {
12 + month - config.fiscal_quarter_start
};
(offset / 3) + 1
}
pub fn fiscal_year(year: i32, month: u32, config: &CalendarConfig) -> i32 {
if month >= config.fiscal_year_start {
year
} else {
year - 1
}
}
#[inline]
pub fn half_year(month: u32) -> u32 {
if month <= 6 {
1
} else {
2
}
}
pub fn fiscal_half_year(_year: i32, month: u32, config: &CalendarConfig) -> u32 {
let offset = if month >= config.fiscal_year_start {
month - config.fiscal_year_start
} else {
12 + month - config.fiscal_year_start
};
if offset < 6 {
1
} else {
2
}
}
pub fn first_day_of_week(
year: i32,
month: u32,
day: u32,
config: &CalendarConfig,
) -> (i32, u32, u32) {
let days = ymd_to_days(year, month, day);
let weekday = day_of_week(days);
let adjusted_weekday = (weekday + 7 - config.week_start) % 7;
let first_day_days = days - adjusted_weekday as i64;
days_to_ymd(first_day_days)
}
pub fn last_day_of_week(
year: i32,
month: u32,
day: u32,
config: &CalendarConfig,
) -> (i32, u32, u32) {
let days = ymd_to_days(year, month, day);
let weekday = day_of_week(days);
let adjusted_weekday = (weekday + 7 - config.week_start) % 7;
let last_day_days = days + (6 - adjusted_weekday) as i64;
days_to_ymd(last_day_days)
}
#[inline]
pub fn first_day_of_month(year: i32, month: u32) -> (i32, u32, u32) {
(year, month, 1)
}
#[inline]
pub fn last_day_of_month(year: i32, month: u32) -> (i32, u32, u32) {
(year, month, days_in_month(year, month))
}
pub fn first_day_of_quarter(year: i32, quarter: u32) -> (i32, u32, u32) {
let month = (quarter - 1) * 3 + 1;
(year, month, 1)
}
pub fn last_day_of_quarter(year: i32, quarter: u32) -> (i32, u32, u32) {
let month = quarter * 3;
(year, month, days_in_month(year, month))
}
#[inline]
pub fn first_day_of_year(year: i32) -> (i32, u32, u32) {
(year, 1, 1)
}
#[inline]
pub fn last_day_of_year(year: i32) -> (i32, u32, u32) {
(year, 12, 31)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_quarter_of_year() {
assert_eq!(quarter_of_year(1), 1);
assert_eq!(quarter_of_year(3), 1);
assert_eq!(quarter_of_year(4), 2);
assert_eq!(quarter_of_year(6), 2);
assert_eq!(quarter_of_year(7), 3);
assert_eq!(quarter_of_year(9), 3);
assert_eq!(quarter_of_year(10), 4);
assert_eq!(quarter_of_year(12), 4);
}
#[test]
fn test_half_year() {
assert_eq!(half_year(1), 1);
assert_eq!(half_year(6), 1);
assert_eq!(half_year(7), 2);
assert_eq!(half_year(12), 2);
}
#[test]
fn test_fiscal_quarter() {
let config = CalendarConfig::new().with_fiscal_quarter_start(7);
assert_eq!(fiscal_quarter(2024, 7, &config), 1);
assert_eq!(fiscal_quarter(2024, 9, &config), 1);
assert_eq!(fiscal_quarter(2024, 10, &config), 2);
assert_eq!(fiscal_quarter(2024, 1, &config), 3);
assert_eq!(fiscal_quarter(2024, 4, &config), 4);
}
#[test]
fn test_fiscal_year() {
let config = CalendarConfig::new().with_fiscal_year_start(7);
assert_eq!(fiscal_year(2024, 7, &config), 2024);
assert_eq!(fiscal_year(2024, 12, &config), 2024);
assert_eq!(fiscal_year(2024, 1, &config), 2023);
assert_eq!(fiscal_year(2024, 6, &config), 2023);
}
#[test]
fn test_week_of_year_iso() {
let (wy, wn) = week_of_year_iso(2024, 1, 1);
assert_eq!(wy, 2024);
assert_eq!(wn, 1);
let (wy, wn) = week_of_year_iso(2023, 1, 1);
assert_eq!(wy, 2022);
assert_eq!(wn, 52);
}
#[test]
fn test_first_last_day_of_month() {
assert_eq!(first_day_of_month(2024, 2), (2024, 2, 1));
assert_eq!(last_day_of_month(2024, 2), (2024, 2, 29)); assert_eq!(last_day_of_month(2023, 2), (2023, 2, 28)); }
#[test]
fn test_first_last_day_of_quarter() {
assert_eq!(first_day_of_quarter(2024, 1), (2024, 1, 1));
assert_eq!(last_day_of_quarter(2024, 1), (2024, 3, 31));
assert_eq!(first_day_of_quarter(2024, 2), (2024, 4, 1));
assert_eq!(last_day_of_quarter(2024, 2), (2024, 6, 30));
assert_eq!(first_day_of_quarter(2024, 4), (2024, 10, 1));
assert_eq!(last_day_of_quarter(2024, 4), (2024, 12, 31));
}
#[test]
fn test_first_last_day_of_year() {
assert_eq!(first_day_of_year(2024), (2024, 1, 1));
assert_eq!(last_day_of_year(2024), (2024, 12, 31));
}
#[test]
fn test_first_day_of_week() {
let config = CalendarConfig::new();
let (y, m, d) = first_day_of_week(2024, 1, 3, &config);
assert_eq!((y, m, d), (2024, 1, 1));
}
#[test]
fn test_last_day_of_week() {
let config = CalendarConfig::new();
let (y, m, d) = last_day_of_week(2024, 1, 3, &config);
assert_eq!((y, m, d), (2024, 1, 7));
}
}