use std::ops::Div;
use chrono::Datelike;
pub trait HalfMonthBucket {
fn half_month_bucket(&self) -> i32;
}
impl<T> HalfMonthBucket for T
where
T: Datelike,
{
#[allow(
clippy::cast_possible_wrap,
clippy::expect_used,
clippy::cast_sign_loss
)]
fn half_month_bucket(&self) -> i32 {
const DAYS_PER_MONTH: [i32; 12] = [
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, ];
let month = self.month0() as i32;
let day = self.day() as i32;
let days_in_month = DAYS_PER_MONTH
.get(month as usize)
.copied()
.expect("month is between 0 and 11, so this should never panic.");
let half_month = i32::from(day > days_in_month.div(2));
month * 2 + half_month
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::NaiveDate;
#[allow(clippy::shadow_unrelated, clippy::unwrap_used)]
#[test]
fn test_half_month_bucket() {
let date = NaiveDate::from_ymd_opt(2020, 1, 1).unwrap();
assert_eq!(date.half_month_bucket(), 0);
let date = NaiveDate::from_ymd_opt(2020, 1, 15).unwrap();
assert_eq!(date.half_month_bucket(), 0);
let date = NaiveDate::from_ymd_opt(2020, 1, 16).unwrap();
assert_eq!(date.half_month_bucket(), 1);
let date = NaiveDate::from_ymd_opt(2020, 2, 1).unwrap();
assert_eq!(date.half_month_bucket(), 2);
let date = NaiveDate::from_ymd_opt(2020, 2, 15).unwrap();
assert_eq!(date.half_month_bucket(), 3);
let date = NaiveDate::from_ymd_opt(2020, 12, 31).unwrap();
assert_eq!(date.half_month_bucket(), 23);
}
}