Skip to main content

use_year/
lib.rs

1#![forbid(unsafe_code)]
2//! Primitive calendar year helpers.
3//!
4//! These helpers keep leap-year and year-boundary calculations explicit.
5//!
6//! # Examples
7//!
8//! ```rust
9//! use use_year::{CalendarYear, first_day_of_year, last_day_of_year};
10//!
11//! let year = CalendarYear::new(2024);
12//!
13//! assert!(year.is_leap_year());
14//! assert_eq!(year.days_in_year(), 366);
15//! assert_eq!(first_day_of_year(2024).unwrap().month(), 1);
16//! assert_eq!(last_day_of_year(2024).unwrap().day(), 31);
17//! ```
18
19use use_date::CalendarDate;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct CalendarYear {
23    year: i32,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum YearError {
28    InvalidDate,
29}
30
31#[must_use]
32pub fn is_leap_year(year: i32) -> bool {
33    year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
34}
35
36#[must_use]
37pub fn days_in_year(year: i32) -> u16 {
38    if is_leap_year(year) {
39        366
40    } else {
41        365
42    }
43}
44
45impl CalendarYear {
46    #[must_use]
47    pub fn new(year: i32) -> Self {
48        Self { year }
49    }
50
51    #[must_use]
52    pub fn year(&self) -> i32 {
53        self.year
54    }
55
56    #[must_use]
57    pub fn is_leap_year(&self) -> bool {
58        is_leap_year(self.year)
59    }
60
61    #[must_use]
62    pub fn days_in_year(&self) -> u16 {
63        days_in_year(self.year)
64    }
65}
66
67pub fn first_day_of_year(year: i32) -> Result<CalendarDate, YearError> {
68    CalendarDate::new(year, 1, 1).map_err(|_| YearError::InvalidDate)
69}
70
71pub fn last_day_of_year(year: i32) -> Result<CalendarDate, YearError> {
72    CalendarDate::new(year, 12, 31).map_err(|_| YearError::InvalidDate)
73}
74
75#[cfg(test)]
76mod tests {
77    use super::{days_in_year, first_day_of_year, is_leap_year, last_day_of_year, CalendarYear};
78    use use_date::CalendarDate;
79
80    #[test]
81    fn checks_leap_years_and_lengths() {
82        let year = CalendarYear::new(2024);
83
84        assert_eq!(year.year(), 2024);
85        assert!(year.is_leap_year());
86        assert_eq!(year.days_in_year(), 366);
87        assert!(!is_leap_year(1900));
88        assert!(is_leap_year(2000));
89        assert_eq!(days_in_year(2023), 365);
90    }
91
92    #[test]
93    fn builds_year_boundary_dates() {
94        assert_eq!(
95            first_day_of_year(2024).unwrap(),
96            CalendarDate::new(2024, 1, 1).unwrap()
97        );
98        assert_eq!(
99            last_day_of_year(2024).unwrap(),
100            CalendarDate::new(2024, 12, 31).unwrap()
101        );
102    }
103}