use errors::*;
use scanlex::{Scanner, Token};
use types::*;
#[derive(Clone, Copy, Debug)]
enum TimeKind {
Formal,
Informal,
AmPm(bool),
Unknown,
}
pub struct DateParser<'a> {
s: Scanner<'a>,
direct: Direction,
maybe_time: Option<(u32, TimeKind)>,
pub american: bool, }
impl<'a> DateParser<'a> {
pub fn new(text: &'a str) -> DateParser<'a> {
DateParser {
s: Scanner::new(text).no_float(),
direct: Direction::Here,
maybe_time: None,
american: false,
}
}
pub fn american_date(mut self) -> DateParser<'a> {
self.american = true;
self
}
fn iso_date(&mut self, y: u32) -> DateResult<DateSpec> {
let month = self.s.get_int::<u32>()?;
self.s.get_ch_matching(&['-'])?;
let day = self.s.get_int::<u32>()?;
Ok(DateSpec::absolute(y, month, day))
}
fn informal_date(&mut self, day_or_month: u32) -> DateResult<DateSpec> {
let month_or_day = self.s.get_int::<u32>()?;
let (d, m) = if self.american {
(month_or_day, day_or_month)
} else {
(day_or_month, month_or_day)
};
Ok(if self.s.peek() == '/' {
self.s.get();
let y = self.s.get_int::<u32>()?;
let y = if y < 100 {
if y > 40 {
1900 + y
} else {
2000 + y
}
} else {
y
};
DateSpec::absolute(y, m, d)
} else {
DateSpec::FromName(ByName::from_day_month(d, m, self.direct))
})
}
fn parse_date(&mut self) -> DateResult<Option<DateSpec>> {
let mut t = self.s.next().or_err("empty date string")?;
let sign = if t.is_char() && t.as_char().unwrap() == '-' {
true
} else {
false
};
if sign {
t = self.s.next().or_err("nothing after '-'")?;
}
if let Some(name) = t.as_iden() {
let shortcut = match name {
"now" => Some(0),
"today" => Some(0),
"yesterday" => Some(-1),
"tomorrow" => Some(1),
_ => None,
};
if let Some(skip) = shortcut {
return Ok(Some(DateSpec::skip(time_unit("day").unwrap(), skip)));
} else
if let Some(d) = Direction::from_name(&name) {
self.direct = d;
}
}
if self.direct != Direction::Here {
t = self.s.next().or_err("nothing after last/next")?;
}
Ok(match t {
Token::Iden(ref name) => {
let name = name.to_lowercase();
if let Some(by_name) = ByName::from_name(&name, self.direct) {
if let Some(month) = by_name.as_month() {
let t = self.s.get();
if t.is_integer() {
let day = t.to_int_result::<u32>()?;
return Ok(Some(if self.s.peek() == ',' {
self.s.get_char()?; let year = self.s.get_int::<u32>()?;
DateSpec::absolute(year, month, day)
} else {
DateSpec::from_day_month(day, month, self.direct)
}));
}
}
Some(DateSpec::FromName(by_name))
} else {
return date_result("expected week day or month name");
}
}
Token::Int(_) => {
let n = t.to_int_result::<u32>()?;
let t = self.s.get();
if t.finished() {
return Ok(Some(DateSpec::absolute(n, 1, 1)));
}
match t {
Token::Iden(ref name) => {
let day = n;
let name = name.to_lowercase();
if let Some(month) = month_name(&name) {
if let Ok(year) = self.s.get_int::<u32>() {
Some(DateSpec::absolute(year, month, day))
} else {
Some(DateSpec::from_day_month(day, month, self.direct))
}
} else if let Some(u) = time_unit(&name) {
let mut n = n as i32;
if sign {
n = -n;
} else {
let t = self.s.get();
let got_ago = if let Some(name) = t.as_iden() {
if name == "ago" {
n = -n;
true
} else {
return date_result("only expected 'ago'");
}
} else {
false
};
if !got_ago {
if let Some(h) = t.to_integer() {
self.maybe_time = Some((h as u32, TimeKind::Unknown));
}
}
}
Some(DateSpec::skip(u, n))
} else if name == "am" || name == "pm" {
self.maybe_time = Some((n, TimeKind::AmPm(name == "pm")));
None
} else {
return date_result("expected month or time unit");
}
}
Token::Char(ch) => match ch {
'-' => Some(self.iso_date(n)?),
'/' => Some(self.informal_date(n)?),
':' | '.' => {
let kind = if ch == ':' {
TimeKind::Formal
} else {
TimeKind::Informal
};
self.maybe_time = Some((n, kind));
None
}
_ => return date_result(&format!("unexpected char {:?}", ch)),
},
_ => return date_result(&format!("unexpected token {:?}", t)),
}
}
_ => return date_result(&format!("not expected token {:?}", t)),
})
}
fn formal_time(&mut self, hour: u32) -> DateResult<TimeSpec> {
let min = self.s.get_int::<u32>()?;
let mut tnext = None;
let sec = if let Some(t) = self.s.next() {
if let Some(ch) = t.as_char() {
if ch != ':' {
return date_result("expecting ':'");
}
self.s.get_int::<u32>()?
} else {
tnext = Some(t);
0
}
} else {
0
};
if tnext.is_none() {
tnext = self.s.next();
}
let micros = if let Some(Some('.')) = tnext.as_ref().map(|t| t.as_char()) {
let frac = self.s.grab_while(char::is_numeric);
if frac.is_empty() {
return date_result("expected fractional second after '.'");
}
let frac = "0.".to_owned() + &frac;
let micros_f = frac.parse::<f64>().unwrap() * 1.0e6;
tnext = self.s.next();
micros_f as u32
} else {
0
};
if tnext.is_none() {
Ok(TimeSpec::new(hour, min, sec, micros))
} else {
let tok = tnext.as_ref().unwrap();
if let Some(ch) = tok.as_char() {
let expecting_offset = match ch {
'+' | '-' => true,
_ => return date_result("expected +/- before timezone"),
};
let offset = if expecting_offset {
let h = self.s.get_int::<u32>()?;
let (h, m) = if self.s.peek() == ':' {
self.s.nextch();
(h, self.s.get_int::<u32>()?)
} else {
let hh = h;
let h = hh / 100;
let m = hh % 100;
(h, m)
};
let res = 60 * (m + 60 * h);
(res as i64) * if ch == '-' { -1 } else { 1 }
} else {
0
};
Ok(TimeSpec::new_with_offset(hour, min, sec, offset, micros))
} else if let Some(id) = tok.as_iden() {
if id == "Z" {
Ok(TimeSpec::new_with_offset(hour, min, sec, 0, micros))
} else {
let hour = DateParser::am_pm(&id, hour)?;
Ok(TimeSpec::new(hour, min, sec, micros))
}
} else {
Ok(TimeSpec::new(hour, min, sec, micros))
}
}
}
fn informal_time(&mut self, hour: u32) -> DateResult<TimeSpec> {
let min = self.s.get_int::<u32>()?;
let hour = if let Some(t) = self.s.next() {
let name = t.to_iden_result()?;
DateParser::am_pm(&name, hour)?
} else {
hour
};
Ok(TimeSpec::new(hour, min, 0, 0))
}
fn am_pm(name: &str, mut hour: u32) -> DateResult<u32> {
if name == "pm" {
hour += 12;
} else if name != "am" {
return date_result("expected am or pm");
}
Ok(hour)
}
fn hour_time(name: &str, hour: u32) -> DateResult<TimeSpec> {
Ok(TimeSpec::new(DateParser::am_pm(name, hour)?, 0, 0, 0))
}
fn parse_time(&mut self) -> DateResult<Option<TimeSpec>> {
if let Some(hour_sep) = self.maybe_time {
let (h, mut kind) = hour_sep;
if let TimeKind::Unknown = kind {
kind = match self.s.get_char()? {
':' => TimeKind::Formal,
'.' => TimeKind::Informal,
ch => return date_result(&format!("expected : or ., not {}", ch)),
};
}
Ok(Some(match kind {
TimeKind::Formal => self.formal_time(h)?,
TimeKind::Informal => self.informal_time(h)?,
TimeKind::AmPm(is_pm) => DateParser::hour_time(if is_pm { "pm" } else { "am" }, h)?,
TimeKind::Unknown => unreachable!(),
}))
} else {
if self.s.peek() == 'T' {
self.s.nextch();
}
let t = self.s.get();
if t.finished() {
return Ok(None);
}
let hour = t.to_int_result::<u32>()?;
Ok(Some(match self.s.get() {
Token::Char(ch) => match ch {
':' => self.formal_time(hour)?,
'.' => self.informal_time(hour)?,
ch => return date_result(&format!("unexpected char {:?}", ch)),
},
Token::Iden(name) => DateParser::hour_time(&name, hour)?,
t => return date_result(&format!("unexpected token {:?}", t)),
}))
}
}
pub fn parse(&mut self) -> DateResult<DateTimeSpec> {
let date = self.parse_date()?;
let time = self.parse_time()?;
Ok(DateTimeSpec {
date: date,
time: time,
})
}
}