1 use scanlex::{Scanner,Token}; 2 use errors::*; 3 use types::*; 4 5 // when we parse dates, there's often a bit of time parsed.. 6 #[derive(Clone,Copy,Debug)] 7 enum TimeKind { 8 Formal, 9 Informal, 10 AmPm(bool), 11 Unknown, 12 } 13 14 pub struct DateParser<'a> { 15 s: Scanner<'a>, 16 direct: Direction, 17 maybe_time: Option<(u32,TimeKind)>, 18 pub american: bool, // 9/11, not 20/03 19 } 20 21 impl <'a> DateParser<'a> { 22 new(text: &'a str) -> DateParser<'a>23 pub fn new(text: &'a str) -> DateParser<'a> { 24 DateParser{ 25 s: Scanner::new(text).no_float(), 26 direct: Direction::Here, 27 maybe_time: None, 28 american: false 29 } 30 } 31 american_date(mut self) -> DateParser<'a>32 pub fn american_date(mut self) -> DateParser<'a> { 33 self.american = true; 34 self 35 } 36 iso_date(&mut self, y: u32) -> DateResult<DateSpec>37 fn iso_date(&mut self, y: u32) -> DateResult<DateSpec> { 38 let month = self.s.get_int::<u32>()?; 39 self.s.get_ch_matching(&['-'])?; 40 let day = self.s.get_int::<u32>()?; 41 Ok(DateSpec::absolute(y,month,day)) 42 } 43 informal_date(&mut self, day_or_month: u32) -> DateResult<DateSpec>44 fn informal_date(&mut self, day_or_month: u32) -> DateResult<DateSpec> { 45 let month_or_day = self.s.get_int::<u32>()?; 46 let (d,m) = if self.american { 47 (month_or_day, day_or_month) 48 } else { 49 (day_or_month, month_or_day) 50 }; 51 Ok(if self.s.peek() == '/' { 52 self.s.get(); 53 let y = self.s.get_int::<u32>()?; 54 let y = if y < 100 { // pivot (1940, 2040) 55 if y > 40 { 56 1900 + y 57 } else { 58 2000 + y 59 } 60 } else { 61 y 62 }; 63 DateSpec::absolute(y,m,d) 64 } else { 65 DateSpec::FromName(ByName::from_day_month(d,m,self.direct)) 66 }) 67 } 68 parse_date(&mut self) -> DateResult<Option<DateSpec>>69 fn parse_date(&mut self) -> DateResult<Option<DateSpec>> { 70 let mut t = self.s.next().or_err("empty date string")?; 71 72 let sign = if t.is_char() && t.as_char().unwrap() == '-' { 73 true 74 } else { 75 false 76 }; 77 if sign { 78 t = self.s.next().or_err("nothing after '-'")?; 79 } 80 if let Some(name) = t.as_iden() { 81 let shortcut = match name { 82 "now" => Some(0), 83 "today" => Some(0), 84 "yesterday" => Some(-1), 85 "tomorrow" => Some(1), 86 _ => None 87 }; 88 if let Some(skip) = shortcut { 89 return Ok(Some( 90 DateSpec::skip(time_unit("day").unwrap(), skip) 91 )); 92 } else // maybe next or last? 93 if let Some(d) = Direction::from_name(&name) { 94 self.direct = d; 95 } 96 } 97 if self.direct != Direction::Here { 98 t = self.s.next().or_err("nothing after last/next")?; 99 } 100 Ok(match t { 101 Token::Iden(ref name) => { 102 let name = name.to_lowercase(); 103 // maybe weekday or month name? 104 if let Some(by_name) = ByName::from_name(&name,self.direct) { 105 // however, MONTH _might_ be followed by DAY, YEAR 106 if let Some(month) = by_name.as_month() { 107 let t = self.s.get(); 108 if t.is_integer() { 109 let day = t.to_int_result::<u32>()?; 110 return Ok(Some(if self.s.peek() == ',' { 111 self.s.get_char()?; // eat ',' 112 let year = self.s.get_int::<u32>()?; 113 DateSpec::absolute(year,month,day) 114 } else { // MONTH DAY is like DAY MONTH (tho no time!) 115 DateSpec::from_day_month(day, month, self.direct) 116 })); 117 } 118 } 119 Some(DateSpec::FromName(by_name)) 120 } else { 121 return date_result("expected week day or month name"); 122 } 123 }, 124 Token::Int(_) => { 125 let n = t.to_int_result::<u32>()?; 126 let t = self.s.get(); 127 if t.finished() { // must be a year... 128 return Ok(Some(DateSpec::absolute(n,1,1))); 129 } 130 match t { 131 Token::Iden(ref name) => { 132 let day = n; 133 let name = name.to_lowercase(); 134 if let Some(month) = month_name(&name) { 135 if let Ok(year) = self.s.get_int::<u32>() { 136 // 4 July 2017 137 Some(DateSpec::absolute(year,month,day)) 138 } else { 139 // 4 July 140 Some(DateSpec::from_day_month(day, month, self.direct)) 141 } 142 } else 143 if let Some(u) = time_unit(&name) { // '2 days' 144 let mut n = n as i32; 145 if sign { 146 n = -n; 147 } else { 148 let t = self.s.get(); 149 let got_ago = if let Some(name) = t.as_iden() { 150 if name == "ago" { 151 n = -n; 152 true 153 } else { 154 return date_result("only expected 'ago'"); 155 } 156 } else { 157 false 158 }; 159 if ! got_ago { 160 if let Some(h) = t.to_integer() { 161 self.maybe_time = Some((h as u32, TimeKind::Unknown)); 162 } 163 } 164 } 165 Some(DateSpec::skip(u, n)) 166 } else 167 if name == "am" || name == "pm" { 168 self.maybe_time = Some((n, TimeKind::AmPm(name == "pm"))); 169 None 170 } else { 171 return date_result("expected month or time unit"); 172 } 173 }, 174 Token::Char(ch) => { 175 match ch { 176 '-' => Some(self.iso_date(n)?), 177 '/' => Some(self.informal_date(n)?), 178 ':' | '.' => { 179 let kind = if ch == ':' { 180 TimeKind::Formal 181 } else { 182 TimeKind::Informal 183 }; 184 self.maybe_time = Some((n,kind)); 185 None 186 } 187 _ => return date_result(&format!("unexpected char {:?}",ch)), 188 } 189 }, 190 _ => return date_result(&format!("unexpected token {:?}",t)), 191 192 } 193 }, 194 _ => return date_result(&format!("not expected token {:?}",t)), 195 }) 196 197 } 198 formal_time(&mut self, hour: u32) -> DateResult<TimeSpec>199 fn formal_time(&mut self, hour: u32) -> DateResult<TimeSpec> { 200 let min = self.s.get_int::<u32>()?; 201 // minute may be followed by [:secs][am|pm] 202 let mut tnext = None; 203 let sec = if let Some(t) = self.s.next() { 204 if let Some(ch) = t.as_char() { 205 if ch != ':' { 206 return date_result("expecting ':'"); 207 } 208 self.s.get_int::<u32>()? 209 } else { 210 tnext = Some(t); 211 0 212 } 213 } else { 214 0 215 }; 216 // we found seconds, look ahead 217 if tnext.is_none() { 218 tnext = self.s.next(); 219 } 220 let micros = if let Some(Some('.')) = tnext.as_ref().map(|t| t.as_char()) { 221 let frac = self.s.grab_while(char::is_numeric); 222 if frac.is_empty() { 223 return date_result("expected fractional second after '.'"); 224 } 225 let frac = "0.".to_owned() + &frac; 226 let micros_f = frac.parse::<f64>().unwrap() * 1.0e6; 227 tnext = self.s.next(); 228 micros_f as u32 229 } else { 230 0 231 }; 232 if tnext.is_none() { 233 Ok(TimeSpec::new(hour, min, sec, micros )) 234 } else { 235 let tok = tnext.as_ref().unwrap(); 236 if let Some(ch) = tok.as_char() { 237 let expecting_offset = match ch { 238 '+' | '-' => true, 239 _ => return date_result("expected +/- before timezone") 240 }; 241 let offset = if expecting_offset { 242 let h = self.s.get_int::<u32>()?; 243 let (h, m) = if self.s.peek() == ':' { // 02:00 244 self.s.nextch(); 245 (h, self.s.get_int::<u32>()?) 246 } else { // 0030 .... 247 let hh = h; 248 let h = hh / 100; 249 let m = hh % 100; 250 (h, m) 251 }; 252 let res = 60 * (m + 60 * h); 253 (res as i64) * if ch == '-' { -1 } else { 1 } 254 } else { 255 0 256 }; 257 Ok(TimeSpec::new_with_offset(hour, min, sec, offset,micros)) 258 } else if let Some(id) = tok.as_iden() { 259 if id == "Z" { 260 Ok(TimeSpec::new_with_offset(hour,min,sec,0,micros)) 261 } else { // am or pm 262 let hour = DateParser::am_pm(&id, hour)?; 263 Ok(TimeSpec::new(hour, min, sec, micros)) 264 } 265 } else { 266 Ok(TimeSpec::new(hour, min, sec, micros)) 267 } 268 } 269 } 270 informal_time(&mut self, hour: u32) -> DateResult<TimeSpec>271 fn informal_time(&mut self, hour: u32) -> DateResult<TimeSpec> { 272 let min = self.s.get_int::<u32>()?; 273 let hour = if let Some(t) = self.s.next() { 274 let name = t.to_iden_result()?; 275 DateParser::am_pm(&name,hour)? 276 } else { 277 hour 278 }; 279 Ok(TimeSpec::new(hour, min, 0, 0)) 280 } 281 am_pm(name: &str, mut hour: u32) -> DateResult<u32>282 fn am_pm(name: &str, mut hour: u32) -> DateResult<u32> { 283 if name == "pm" { 284 hour += 12; 285 } else 286 if name != "am" { 287 return date_result("expected am or pm"); 288 } 289 Ok(hour) 290 } 291 hour_time(name: &str, hour: u32) -> DateResult<TimeSpec>292 fn hour_time(name: &str, hour: u32) -> DateResult<TimeSpec> { 293 Ok(TimeSpec::new (DateParser::am_pm(name, hour)?, 0, 0, 0)) 294 } 295 parse_time(&mut self) -> DateResult<Option<TimeSpec>>296 fn parse_time(&mut self) -> DateResult<Option<TimeSpec>> { 297 // here the date parser looked ahead and saw an hour followed by some separator 298 if let Some(hour_sep) = self.maybe_time { // didn't see a separator, so look... 299 let (h,mut kind) = hour_sep; 300 if let TimeKind::Unknown = kind { 301 kind = match self.s.get_char()? { 302 ':' => TimeKind::Formal, 303 '.' => TimeKind::Informal, 304 ch => return date_result(&format!("expected : or ., not {}", ch)), 305 }; 306 } 307 Ok(Some( 308 match kind { 309 TimeKind::Formal => self.formal_time(h)?, 310 TimeKind::Informal => self.informal_time(h)?, 311 TimeKind::AmPm(is_pm) => 312 DateParser::hour_time(if is_pm {"pm"} else {"am"},h)?, 313 TimeKind::Unknown => unreachable!(), 314 } 315 )) 316 } else { // no lookahead... 317 if self.s.peek() == 'T' { 318 self.s.nextch(); 319 } 320 let t = self.s.get(); 321 if t.finished() { 322 return Ok(None); 323 } 324 325 let hour = t.to_int_result::<u32>()?; 326 Ok(Some(match self.s.get() { 327 Token::Char(ch) => match ch { 328 ':' => self.formal_time(hour)?, 329 '.' => self.informal_time(hour)?, 330 ch => return date_result(&format!("unexpected char {:?}",ch)) 331 }, 332 Token::Iden(name) => { 333 DateParser::hour_time(&name,hour)? 334 } 335 t => return date_result(&format!("unexpected token {:?}",t)) 336 })) 337 } 338 } 339 parse(&mut self) -> DateResult<DateTimeSpec>340 pub fn parse(&mut self) -> DateResult<DateTimeSpec> { 341 let date = self.parse_date()?; 342 let time = self.parse_time()?; 343 Ok(DateTimeSpec{date: date, time: time}) 344 } 345 346 } 347