| 259 | } |
| 260 | |
| 261 | fn build_timezone_offset_second( |
| 262 | tokens: &[TimeStrToken], |
| 263 | value: &str, |
| 264 | spec: TimezoneSpec, |
| 265 | ) -> Result<Timezone, String> { |
| 266 | use TimeStrToken::*; |
| 267 | static ALL_FORMATS: [&[TimeStrToken]; 12] = [ |
| 268 | &[Plus, Num(0, 1), Colon, Num(0, 1), Colon, Num(0, 1)], |
| 269 | &[Dash, Num(0, 1), Colon, Num(0, 1), Colon, Num(0, 1)], |
| 270 | &[Plus, Num(0, 1), Colon, Num(0, 1)], |
| 271 | &[Dash, Num(0, 1), Colon, Num(0, 1)], |
| 272 | &[Plus, Num(0, 1), Num(0, 1), Num(0, 1)], |
| 273 | &[Dash, Num(0, 1), Num(0, 1), Num(0, 1)], |
| 274 | &[Plus, Num(0, 1), Num(0, 1)], |
| 275 | &[Dash, Num(0, 1), Num(0, 1)], |
| 276 | &[Plus, Num(0, 1)], |
| 277 | &[Dash, Num(0, 1)], |
| 278 | &[TzName(String::new())], |
| 279 | &[Zulu], |
| 280 | ]; |
| 281 | |
| 282 | let mut is_positive = true; |
| 283 | let mut hour_offset: Option<i32> = None; |
| 284 | let mut minute_offset: Option<i32> = None; |
| 285 | let mut second_offset: Option<i32> = None; |
| 286 | |
| 287 | for format in ALL_FORMATS { |
| 288 | let actual = tokens.iter(); |
| 289 | |
| 290 | if actual.len() != format.len() { |
| 291 | continue; |
| 292 | } |
| 293 | |
| 294 | for (i, (atok, etok)) in actual.zip_eq(format).enumerate() { |
| 295 | match (atok, etok) { |
| 296 | (Colon, Colon) | (Plus, Plus) => { /* Matching punctuation */ } |
| 297 | (Dash, Dash) => { |
| 298 | is_positive = false; |
| 299 | } |
| 300 | (Num(val, _), Num(_, _)) => { |
| 301 | let val = *val; |
| 302 | match (hour_offset, minute_offset, second_offset) { |
| 303 | (None, None, None) => { |
| 304 | // Postgres allows timezones in the range -15:59:59..15:59:59 |
| 305 | if val <= 15 { |
| 306 | hour_offset = Some(i32::try_from(val).expect( |
| 307 | "number between 0 and 15 should fit in signed 32-bit integer", |
| 308 | )); |
| 309 | } else { |
| 310 | return Err(format!( |
| 311 | "Invalid timezone string ({}): timezone hour invalid {}", |
| 312 | value, val |
| 313 | )); |
| 314 | } |
| 315 | } |
| 316 | (Some(_), None, None) => { |
| 317 | if val < 60 { |
| 318 | minute_offset = Some(i32::try_from(val).expect( |