whatwg_datetime/components/
week.rs1use crate::tokens::{TOKEN_ABBR_WEEK, TOKEN_HYPHEN};
2use crate::utils::{collect_ascii_digits, week_number_of_year};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct YearWeek {
14 pub(crate) year: i32,
15 pub(crate) week: u32,
16}
17
18impl YearWeek {
19 #[inline]
20 pub(crate) const fn new(year: i32, week: u32) -> Self {
21 Self { year, week }
22 }
23
24 pub fn new_opt(year: i32, week: u32) -> Option<Self> {
44 if year <= 0 {
45 return None;
46 }
47
48 if week < 1 || week > week_number_of_year(year)? {
49 return None;
50 }
51
52 Some(Self::new(year, week))
53 }
54
55 #[inline]
65 pub const fn year(&self) -> i32 {
66 self.year
67 }
68
69 #[inline]
80 pub const fn week(&self) -> u32 {
81 self.week
82 }
83}
84
85pub fn parse_week(input: &str) -> Option<YearWeek> {
102 let mut position = 0usize;
104
105 let year_string = collect_ascii_digits(input, &mut position);
107 let year = year_string.parse::<i32>().unwrap();
108 if year <= 0 {
109 return None;
110 }
111
112 if position > input.len() || input.chars().nth(position) != Some(TOKEN_HYPHEN) {
114 return None;
115 } else {
116 position += 1;
117 }
118
119 if position > input.len() || input.chars().nth(position) != Some(TOKEN_ABBR_WEEK) {
121 return None;
122 } else {
123 position += 1;
124 }
125
126 let parsed_week = collect_ascii_digits(input, &mut position);
128 if parsed_week.len() != 2 {
129 return None;
130 }
131
132 let week = parsed_week.parse::<u32>().unwrap();
133 let max_weeks = week_number_of_year(year)?;
134 if week < 1 || week > max_weeks {
135 return None;
136 }
137
138 Some(YearWeek::new(year, week))
139}
140
141#[cfg(test)]
142mod tests {
143 use super::{parse_week, YearWeek};
144
145 #[test]
146 fn test_parse_week() {
147 assert_eq!(parse_week("2004-W53"), Some(YearWeek::new(2004, 53)));
148 }
149
150 #[test]
151 fn test_parse_week_fails_year_is_zero() {
152 assert_eq!(parse_week("0000-W01"), None);
153 }
154
155 #[test]
156 fn test_parse_week_fails_invalid_separator() {
157 assert_eq!(parse_week("2004_W01"), None);
158 }
159
160 #[test]
161 fn test_parse_week_fails_invalid_week_abbr() {
162 assert_eq!(parse_week("2003-𝓌01"), None);
163 }
164
165 #[test]
166 fn test_parse_week_fails_invalid_week_length() {
167 assert_eq!(parse_week("2004-W1"), None);
168 assert_eq!(parse_week("2008-W001"), None);
169 }
170
171 #[test]
172 fn test_parse_week_fails_invalid_week_num_lower_bound() {
173 assert_eq!(parse_week("2022-W00"), None);
174 assert_eq!(parse_week("1897-W00"), None);
175 }
176
177 #[test]
178 fn test_parse_week_fails_invalid_week_num_upper_bound() {
179 assert_eq!(parse_week("2004-W54"), None);
180 assert_eq!(parse_week("1996-W53"), None);
181 }
182}