summaryrefslogtreecommitdiffstats
path: root/third_party/rust/time-0.1.45/src/parse.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/time-0.1.45/src/parse.rs')
-rw-r--r--third_party/rust/time-0.1.45/src/parse.rs395
1 files changed, 395 insertions, 0 deletions
diff --git a/third_party/rust/time-0.1.45/src/parse.rs b/third_party/rust/time-0.1.45/src/parse.rs
new file mode 100644
index 0000000000..602bdc5c76
--- /dev/null
+++ b/third_party/rust/time-0.1.45/src/parse.rs
@@ -0,0 +1,395 @@
+use super::{Timespec, Tm, at_utc, ParseError, NSEC_PER_SEC};
+
+/// Parses the time from the string according to the format string.
+pub fn strptime(mut s: &str, format: &str) -> Result<Tm, ParseError> {
+ let mut tm = Tm {
+ tm_sec: 0,
+ tm_min: 0,
+ tm_hour: 0,
+ tm_mday: 0,
+ tm_mon: 0,
+ tm_year: 0,
+ tm_wday: 0,
+ tm_yday: 0,
+ tm_isdst: 0,
+ tm_utcoff: 0,
+ tm_nsec: 0,
+ };
+ let mut chars = format.chars();
+
+ while let Some(ch) = chars.next() {
+ if ch == '%' {
+ if let Some(ch) = chars.next() {
+ parse_type(&mut s, ch, &mut tm)?;
+ }
+ } else {
+ parse_char(&mut s, ch)?;
+ }
+ }
+
+ Ok(tm)
+}
+
+fn parse_type(s: &mut &str, ch: char, tm: &mut Tm) -> Result<(), ParseError> {
+ match ch {
+ 'A' => match match_strs(s, &[("Sunday", 0),
+ ("Monday", 1),
+ ("Tuesday", 2),
+ ("Wednesday", 3),
+ ("Thursday", 4),
+ ("Friday", 5),
+ ("Saturday", 6)]) {
+ Some(v) => { tm.tm_wday = v; Ok(()) }
+ None => Err(ParseError::InvalidDay)
+ },
+ 'a' => match match_strs(s, &[("Sun", 0),
+ ("Mon", 1),
+ ("Tue", 2),
+ ("Wed", 3),
+ ("Thu", 4),
+ ("Fri", 5),
+ ("Sat", 6)]) {
+ Some(v) => { tm.tm_wday = v; Ok(()) }
+ None => Err(ParseError::InvalidDay)
+ },
+ 'B' => match match_strs(s, &[("January", 0),
+ ("February", 1),
+ ("March", 2),
+ ("April", 3),
+ ("May", 4),
+ ("June", 5),
+ ("July", 6),
+ ("August", 7),
+ ("September", 8),
+ ("October", 9),
+ ("November", 10),
+ ("December", 11)]) {
+ Some(v) => { tm.tm_mon = v; Ok(()) }
+ None => Err(ParseError::InvalidMonth)
+ },
+ 'b' | 'h' => match match_strs(s, &[("Jan", 0),
+ ("Feb", 1),
+ ("Mar", 2),
+ ("Apr", 3),
+ ("May", 4),
+ ("Jun", 5),
+ ("Jul", 6),
+ ("Aug", 7),
+ ("Sep", 8),
+ ("Oct", 9),
+ ("Nov", 10),
+ ("Dec", 11)]) {
+ Some(v) => { tm.tm_mon = v; Ok(()) }
+ None => Err(ParseError::InvalidMonth)
+ },
+ 'C' => match match_digits_in_range(s, 1, 2, false, 0, 99) {
+ Some(v) => { tm.tm_year += (v * 100) - 1900; Ok(()) }
+ None => Err(ParseError::InvalidYear)
+ },
+ 'c' => {
+ parse_type(s, 'a', tm)
+ .and_then(|()| parse_char(s, ' '))
+ .and_then(|()| parse_type(s, 'b', tm))
+ .and_then(|()| parse_char(s, ' '))
+ .and_then(|()| parse_type(s, 'e', tm))
+ .and_then(|()| parse_char(s, ' '))
+ .and_then(|()| parse_type(s, 'T', tm))
+ .and_then(|()| parse_char(s, ' '))
+ .and_then(|()| parse_type(s, 'Y', tm))
+ }
+ 'D' | 'x' => {
+ parse_type(s, 'm', tm)
+ .and_then(|()| parse_char(s, '/'))
+ .and_then(|()| parse_type(s, 'd', tm))
+ .and_then(|()| parse_char(s, '/'))
+ .and_then(|()| parse_type(s, 'y', tm))
+ }
+ 'd' => match match_digits_in_range(s, 1, 2, false, 1, 31) {
+ Some(v) => { tm.tm_mday = v; Ok(()) }
+ None => Err(ParseError::InvalidDayOfMonth)
+ },
+ 'e' => match match_digits_in_range(s, 1, 2, true, 1, 31) {
+ Some(v) => { tm.tm_mday = v; Ok(()) }
+ None => Err(ParseError::InvalidDayOfMonth)
+ },
+ 'f' => {
+ tm.tm_nsec = match_fractional_seconds(s);
+ Ok(())
+ }
+ 'F' => {
+ parse_type(s, 'Y', tm)
+ .and_then(|()| parse_char(s, '-'))
+ .and_then(|()| parse_type(s, 'm', tm))
+ .and_then(|()| parse_char(s, '-'))
+ .and_then(|()| parse_type(s, 'd', tm))
+ }
+ 'H' => {
+ match match_digits_in_range(s, 1, 2, false, 0, 23) {
+ Some(v) => { tm.tm_hour = v; Ok(()) }
+ None => Err(ParseError::InvalidHour)
+ }
+ }
+ 'I' => {
+ match match_digits_in_range(s, 1, 2, false, 1, 12) {
+ Some(v) => { tm.tm_hour = if v == 12 { 0 } else { v }; Ok(()) }
+ None => Err(ParseError::InvalidHour)
+ }
+ }
+ 'j' => {
+ match match_digits_in_range(s, 1, 3, false, 1, 366) {
+ Some(v) => { tm.tm_yday = v - 1; Ok(()) }
+ None => Err(ParseError::InvalidDayOfYear)
+ }
+ }
+ 'k' => {
+ match match_digits_in_range(s, 1, 2, true, 0, 23) {
+ Some(v) => { tm.tm_hour = v; Ok(()) }
+ None => Err(ParseError::InvalidHour)
+ }
+ }
+ 'l' => {
+ match match_digits_in_range(s, 1, 2, true, 1, 12) {
+ Some(v) => { tm.tm_hour = if v == 12 { 0 } else { v }; Ok(()) }
+ None => Err(ParseError::InvalidHour)
+ }
+ }
+ 'M' => {
+ match match_digits_in_range(s, 1, 2, false, 0, 59) {
+ Some(v) => { tm.tm_min = v; Ok(()) }
+ None => Err(ParseError::InvalidMinute)
+ }
+ }
+ 'm' => {
+ match match_digits_in_range(s, 1, 2, false, 1, 12) {
+ Some(v) => { tm.tm_mon = v - 1; Ok(()) }
+ None => Err(ParseError::InvalidMonth)
+ }
+ }
+ 'n' => parse_char(s, '\n'),
+ 'P' => match match_strs(s, &[("am", 0), ("pm", 12)]) {
+ Some(v) => { tm.tm_hour += v; Ok(()) }
+ None => Err(ParseError::InvalidHour)
+ },
+ 'p' => match match_strs(s, &[("AM", 0), ("PM", 12)]) {
+ Some(v) => { tm.tm_hour += v; Ok(()) }
+ None => Err(ParseError::InvalidHour)
+ },
+ 'R' => {
+ parse_type(s, 'H', tm)
+ .and_then(|()| parse_char(s, ':'))
+ .and_then(|()| parse_type(s, 'M', tm))
+ }
+ 'r' => {
+ parse_type(s, 'I', tm)
+ .and_then(|()| parse_char(s, ':'))
+ .and_then(|()| parse_type(s, 'M', tm))
+ .and_then(|()| parse_char(s, ':'))
+ .and_then(|()| parse_type(s, 'S', tm))
+ .and_then(|()| parse_char(s, ' '))
+ .and_then(|()| parse_type(s, 'p', tm))
+ }
+ 's' => {
+ match match_digits_i64(s, 1, 18, false) {
+ Some(v) => {
+ *tm = at_utc(Timespec::new(v, 0));
+ Ok(())
+ },
+ None => Err(ParseError::InvalidSecondsSinceEpoch)
+ }
+ }
+ 'S' => {
+ match match_digits_in_range(s, 1, 2, false, 0, 60) {
+ Some(v) => { tm.tm_sec = v; Ok(()) }
+ None => Err(ParseError::InvalidSecond)
+ }
+ }
+ //'s' {}
+ 'T' | 'X' => {
+ parse_type(s, 'H', tm)
+ .and_then(|()| parse_char(s, ':'))
+ .and_then(|()| parse_type(s, 'M', tm))
+ .and_then(|()| parse_char(s, ':'))
+ .and_then(|()| parse_type(s, 'S', tm))
+ }
+ 't' => parse_char(s, '\t'),
+ 'u' => {
+ match match_digits_in_range(s, 1, 1, false, 1, 7) {
+ Some(v) => { tm.tm_wday = if v == 7 { 0 } else { v }; Ok(()) }
+ None => Err(ParseError::InvalidDayOfWeek)
+ }
+ }
+ 'v' => {
+ parse_type(s, 'e', tm)
+ .and_then(|()| parse_char(s, '-'))
+ .and_then(|()| parse_type(s, 'b', tm))
+ .and_then(|()| parse_char(s, '-'))
+ .and_then(|()| parse_type(s, 'Y', tm))
+ }
+ //'W' {}
+ 'w' => {
+ match match_digits_in_range(s, 1, 1, false, 0, 6) {
+ Some(v) => { tm.tm_wday = v; Ok(()) }
+ None => Err(ParseError::InvalidDayOfWeek)
+ }
+ }
+ 'Y' => {
+ match match_digits(s, 4, 4, false) {
+ Some(v) => { tm.tm_year = v - 1900; Ok(()) }
+ None => Err(ParseError::InvalidYear)
+ }
+ }
+ 'y' => {
+ match match_digits_in_range(s, 1, 2, false, 0, 99) {
+ Some(v) => { tm.tm_year = v; Ok(()) }
+ None => Err(ParseError::InvalidYear)
+ }
+ }
+ 'Z' => {
+ if match_str(s, "UTC") || match_str(s, "GMT") {
+ tm.tm_utcoff = 0;
+ Ok(())
+ } else {
+ // It's odd, but to maintain compatibility with c's
+ // strptime we ignore the timezone.
+ for (i, ch) in s.char_indices() {
+ if ch == ' ' {
+ *s = &s[i..];
+ return Ok(())
+ }
+ }
+ *s = "";
+ Ok(())
+ }
+ }
+ 'z' => {
+ if parse_char(s, 'Z').is_ok() {
+ tm.tm_utcoff = 0;
+ Ok(())
+ } else {
+ let sign = if parse_char(s, '+').is_ok() {1}
+ else if parse_char(s, '-').is_ok() {-1}
+ else { return Err(ParseError::InvalidZoneOffset) };
+
+ let hours;
+ let minutes;
+
+ match match_digits(s, 2, 2, false) {
+ Some(h) => hours = h,
+ None => return Err(ParseError::InvalidZoneOffset)
+ }
+
+ // consume the colon if its present,
+ // just ignore it otherwise
+ let _ = parse_char(s, ':');
+
+ match match_digits(s, 2, 2, false) {
+ Some(m) => minutes = m,
+ None => return Err(ParseError::InvalidZoneOffset)
+ }
+
+ tm.tm_utcoff = sign * (hours * 60 * 60 + minutes * 60);
+ Ok(())
+ }
+ }
+ '%' => parse_char(s, '%'),
+ ch => Err(ParseError::InvalidFormatSpecifier(ch))
+ }
+}
+
+
+fn match_str(s: &mut &str, needle: &str) -> bool {
+ if s.starts_with(needle) {
+ *s = &s[needle.len()..];
+ true
+ } else {
+ false
+ }
+}
+
+fn match_strs(ss: &mut &str, strs: &[(&str, i32)]) -> Option<i32> {
+ for &(needle, value) in strs.iter() {
+ if match_str(ss, needle) {
+ return Some(value)
+ }
+ }
+ None
+}
+
+fn match_digits(ss: &mut &str, min_digits : usize, max_digits: usize, ws: bool) -> Option<i32> {
+ match match_digits_i64(ss, min_digits, max_digits, ws) {
+ Some(v) => Some(v as i32),
+ None => None
+ }
+}
+
+fn match_digits_i64(ss: &mut &str, min_digits : usize, max_digits: usize, ws: bool) -> Option<i64> {
+ let mut value : i64 = 0;
+ let mut n = 0;
+ if ws {
+ #[allow(deprecated)] // use `trim_start_matches` starting in 1.30
+ let s2 = ss.trim_left_matches(" ");
+ n = ss.len() - s2.len();
+ if n > max_digits { return None }
+ }
+ let chars = ss[n..].char_indices();
+ for (_, ch) in chars.take(max_digits - n) {
+ match ch {
+ '0' ... '9' => value = value * 10 + (ch as i64 - '0' as i64),
+ _ => break,
+ }
+ n += 1;
+ }
+
+ if n >= min_digits && n <= max_digits {
+ *ss = &ss[n..];
+ Some(value)
+ } else {
+ None
+ }
+}
+
+fn match_fractional_seconds(ss: &mut &str) -> i32 {
+ let mut value = 0;
+ let mut multiplier = NSEC_PER_SEC / 10;
+
+ let mut chars = ss.char_indices();
+ let orig = *ss;
+ for (i, ch) in &mut chars {
+ *ss = &orig[i..];
+ match ch {
+ '0' ... '9' => {
+ // This will drop digits after the nanoseconds place
+ let digit = ch as i32 - '0' as i32;
+ value += digit * multiplier;
+ multiplier /= 10;
+ }
+ _ => break
+ }
+ }
+
+ value
+}
+
+fn match_digits_in_range(ss: &mut &str,
+ min_digits : usize, max_digits : usize,
+ ws: bool, min: i32, max: i32) -> Option<i32> {
+ let before = *ss;
+ match match_digits(ss, min_digits, max_digits, ws) {
+ Some(val) if val >= min && val <= max => Some(val),
+ _ => { *ss = before; None }
+ }
+}
+
+fn parse_char(s: &mut &str, c: char) -> Result<(), ParseError> {
+ match s.char_indices().next() {
+ Some((i, c2)) => {
+ if c == c2 {
+ *s = &s[i + c2.len_utf8()..];
+ Ok(())
+ } else {
+ Err(ParseError::UnexpectedCharacter(c, c2))
+ }
+ }
+ None => Err(ParseError::InvalidTime),
+ }
+}