1pub fn parse(s: &str) -> Result<i32, String> {
4 if s == "Z" || s == "z" {
5 return Ok(0);
6 }
7
8 let sign = match s.as_bytes().first() {
9 Some(b'+') => 1,
10 Some(b'-') => -1,
11 _ => return Err(format!("invalid timezone offset '{s}'; expected +HHMM, -HH:MM, or Z")),
12 };
13
14 let body = &s[1..];
15 let (hours, minutes) = if body.len() == 4 && body.is_ascii() {
16 let h = body[..2].parse::<i32>().map_err(|_| format!("invalid timezone offset '{s}'"))?;
17 let m = body[2..].parse::<i32>().map_err(|_| format!("invalid timezone offset '{s}'"))?;
18 (h, m)
19 } else if body.len() == 5 && body.as_bytes()[2] == b':' {
20 let h = body[..2].parse::<i32>().map_err(|_| format!("invalid timezone offset '{s}'"))?;
21 let m = body[3..].parse::<i32>().map_err(|_| format!("invalid timezone offset '{s}'"))?;
22 (h, m)
23 } else {
24 return Err(format!("invalid timezone offset '{s}'; expected +HHMM, -HH:MM, or Z"));
25 };
26
27 if hours > 99 {
28 return Err(format!("invalid timezone offset '{s}'; hours must be 00-99"));
29 }
30 if minutes > 59 {
31 return Err(format!("invalid timezone offset '{s}'; minutes must be 00-59"));
32 }
33
34 Ok(sign * (hours * 60 + minutes))
35}