1use chrono::{Datelike, NaiveDate, Weekday};
2use whatwg_infra::collect_codepoints;
3
4#[inline]
5pub(crate) fn is_valid_month(month: &u32) -> bool {
6 (1..=12).contains(month)
7}
8
9#[inline]
10pub(crate) fn is_valid_hour(hour: &u32) -> bool {
11 (0..=23).contains(hour)
12}
13
14#[inline]
15pub(crate) fn is_valid_min_or_sec(val: &u32) -> bool {
16 (0..60).contains(val)
17}
18
19#[inline]
20pub(crate) fn collect_ascii_digits(s: &str, position: &mut usize) -> String {
21 collect_codepoints(s, position, |c| c.is_ascii_digit())
22}
23
24pub const fn max_days_in_month_year(month: u32, year: u32) -> Option<u32> {
25 match month {
26 1 | 3 | 5 | 7 | 8 | 10 | 12 => Some(31),
27 4 | 6 | 9 | 11 => Some(30),
28 2 => {
29 if year % 400 == 0 || (year % 4 == 0 && year % 100 != 0) {
30 Some(29)
31 } else {
32 Some(28)
33 }
34 }
35 _ => None,
36 }
37}
38
39pub fn week_number_of_year(year: i32) -> Option<u32> {
41 let naive_date = NaiveDate::from_ymd_opt(year, 1u32, 1u32).unwrap();
44 let weekday = naive_date.weekday();
45
46 match weekday {
47 Weekday::Thu => Some(53u32),
48 Weekday::Wed => {
49 if year % 400 == 0 || (year % 4 == 0 && year % 100 != 0) {
50 Some(53u32)
51 } else {
52 Some(52u32)
53 }
54 }
55 _ => Some(52u32),
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::{max_days_in_month_year, week_number_of_year};
62
63 #[test]
64 fn test_max_days_in_month_28_days() {
65 assert_eq!(max_days_in_month_year(2, 2021), Some(28));
66 assert_eq!(max_days_in_month_year(2, 2022), Some(28));
67 assert_eq!(max_days_in_month_year(2, 2023), Some(28));
68 }
69
70 #[test]
71 fn test_max_days_in_month_29_days() {
72 assert_eq!(max_days_in_month_year(2, 2020), Some(29));
73 assert_eq!(max_days_in_month_year(2, 2024), Some(29));
74 assert_eq!(max_days_in_month_year(2, 2028), Some(29));
75 assert_eq!(max_days_in_month_year(2, 2400), Some(29));
76 }
77
78 #[test]
79 fn test_max_days_in_month_30_days() {
80 assert_eq!(max_days_in_month_year(4, 2021), Some(30));
81 assert_eq!(max_days_in_month_year(6, 2021), Some(30));
82 assert_eq!(max_days_in_month_year(9, 2021), Some(30));
83 assert_eq!(max_days_in_month_year(11, 2021), Some(30));
84 }
85
86 #[test]
87 fn test_max_days_in_month_31_days() {
88 assert_eq!(max_days_in_month_year(1, 2021), Some(31));
89 assert_eq!(max_days_in_month_year(3, 2019), Some(31));
90 assert_eq!(max_days_in_month_year(5, 2000), Some(31));
91 assert_eq!(max_days_in_month_year(7, 3097), Some(31));
92 assert_eq!(max_days_in_month_year(8, 1985), Some(31));
93 assert_eq!(max_days_in_month_year(10, 1426), Some(31));
94 assert_eq!(max_days_in_month_year(12, 1953), Some(31));
95 }
96
97 #[test]
98 fn test_max_days_in_month_nothing() {
99 assert_eq!(max_days_in_month_year(13, 2022), None);
100 }
101
102 #[test]
104 fn test_week_number_of_year_is_52() {
105 assert_eq!(week_number_of_year(2012), Some(52));
106 assert_eq!(week_number_of_year(2017), Some(52));
107 assert_eq!(week_number_of_year(2018), Some(52));
108 assert_eq!(week_number_of_year(2019), Some(52));
109 assert_eq!(week_number_of_year(2021), Some(52));
110 assert_eq!(week_number_of_year(2022), Some(52));
111 assert_eq!(week_number_of_year(2023), Some(52));
112 }
113
114 #[test]
116 fn test_week_number_of_year_is_53() {
117 assert_eq!(week_number_of_year(1801), Some(53));
118 assert_eq!(week_number_of_year(2004), Some(53));
119 assert_eq!(week_number_of_year(2009), Some(53));
120 assert_eq!(week_number_of_year(2015), Some(53));
121 assert_eq!(week_number_of_year(2020), Some(53));
122 }
123
124 #[test]
127 fn test_week_number_of_year_starts_on_wednesday_and_not_leap_year_is_52() {
128 assert_eq!(week_number_of_year(2014), Some(52));
129 assert_eq!(week_number_of_year(2025), Some(52));
130 }
131}