private void expectToken(int desired, String errorMessage) { token = scanner.nextToken(); if (token.id != desired) { throw new ArgumentException(errorMessage); } }
internal TimeToken resolveMonthsMinutes(int newId) { if (token.id != TimeToken.MONTHS_MINUTES) { throw new ApplicationException("token id not MONTHS_MINUTES"); } if (newId != TimeToken.MONTHS && newId != TimeToken.MINUTES) { throw new ApplicationException("new token id not MONTHS_MINUTES || MONTHS"); } return(token = new TimeToken(token.value, newId)); }
private void plusMinus(int doop) { if (doop >= 0) { op = doop; expectToken(TimeToken.NUMBER, "There should be number after " + (op == TimeToken.PLUS ? '+' : '-')); prev_multiplier = -1; /* reset months-minutes guessing mechanics */ } int delta = int.Parse(token.value); token = scanner.nextToken(); if (token.id == TimeToken.MONTHS_MINUTES) { /* hard job to guess what does that -5m means: -5mon or -5min? */ switch (prev_multiplier) { case TimeToken.DAYS: case TimeToken.WEEKS: case TimeToken.MONTHS: case TimeToken.YEARS: token = scanner.resolveMonthsMinutes(TimeToken.MONTHS); break; case TimeToken.SECONDS: case TimeToken.MINUTES: case TimeToken.HOURS: token = scanner.resolveMonthsMinutes(TimeToken.MINUTES); break; default: token = delta < 6 ? scanner.resolveMonthsMinutes(TimeToken.MONTHS) : scanner.resolveMonthsMinutes(TimeToken.MINUTES); break; } } prev_multiplier = token.id; delta *= (op == TimeToken.PLUS) ? +1 : -1; switch (token.id) { case TimeToken.YEARS: spec.dyear += delta; return; case TimeToken.MONTHS: spec.dmonth += delta; return; case TimeToken.WEEKS: delta *= 7; spec.dday += delta; break; /* FALLTHRU */ case TimeToken.DAYS: spec.dday += delta; return; case TimeToken.HOURS: spec.dhour += delta; return; case TimeToken.MINUTES: spec.dmin += delta; return; default: // default is 'seconds' spec.dsec += delta; break; } }
/** * Parses the input string specified in the constructor. * @return Object representing parsed date/time. */ public TimeSpec parse() { long now = DateTime.Now.Ticks / TimeSpan.TicksPerSecond; int hr = 0; int time_reference; /* this MUST be initialized to zero for midnight/noon/teatime */ /* establish the default time reference */ spec.localtime(now); token = scanner.nextToken(); switch (token.id) { case TimeToken.PLUS: case TimeToken.MINUS: break; /* jump to OFFSET-SPEC part */ case TimeToken.START: spec.type = TimeSpec.TYPE_START; if (spec.type != TimeSpec.TYPE_START) { spec.type = TimeSpec.TYPE_END; } spec.year = spec.month = spec.day = spec.hour = spec.min = spec.sec = 0; time_reference = token.id; token = scanner.nextToken(); if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) { break; } if (time_reference != TimeToken.NOW) { throw new ArgumentException("Words 'start' or 'end' MUST be followed by +|- offset"); } if (token.id != TimeToken.EOF) { throw new ArgumentException("If 'now' is followed by a token it must be +|- offset"); } break; /* FALLTHRU */ case TimeToken.END: if (spec.type != TimeSpec.TYPE_START) { spec.type = TimeSpec.TYPE_END; } spec.year = spec.month = spec.day = spec.hour = spec.min = spec.sec = 0; time_reference = token.id; token = scanner.nextToken(); if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) { break; } if (time_reference != TimeToken.NOW) { throw new ArgumentException("Words 'start' or 'end' MUST be followed by +|- offset"); } if (token.id != TimeToken.EOF) { throw new ArgumentException("If 'now' is followed by a token it must be +|- offset"); } break; /* FALLTHRU */ case TimeToken.NOW: time_reference = token.id; token = scanner.nextToken(); if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) { break; } if (time_reference != TimeToken.NOW) { throw new ArgumentException("Words 'start' or 'end' MUST be followed by +|- offset"); } if (token.id != TimeToken.EOF) { throw new ArgumentException("If 'now' is followed by a token it must be +|- offset"); } break; /* Only absolute time specifications below */ case TimeToken.NUMBER: timeOfDay(); if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) { break; } day(); if (token.id != TimeToken.NUMBER) { break; } timeOfDay(); break; /* fix month parsing */ case TimeToken.JAN: case TimeToken.FEB: case TimeToken.MAR: case TimeToken.APR: case TimeToken.MAY: case TimeToken.JUN: case TimeToken.JUL: case TimeToken.AUG: case TimeToken.SEP: case TimeToken.OCT: case TimeToken.NOV: case TimeToken.DEC: day(); if (token.id != TimeToken.NUMBER) { break; } timeOfDay(); break; /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialized * hr to zero up above, then fall into this case in such a * way so we add +12 +4 hours to it for teatime, +12 hours * to it for noon, and nothing at all for midnight, then * set our rettime to that hour before leaping into the * month scanner */ case TimeToken.TEATIME: hr += 4; hr += 12; spec.hour = hr; spec.min = 0; spec.sec = 0; token = scanner.nextToken(); day(); break; /* FALLTHRU */ case TimeToken.NOON: hr += 12; spec.hour = hr; spec.min = 0; spec.sec = 0; token = scanner.nextToken(); day(); break; /* FALLTHRU */ case TimeToken.MIDNIGHT: spec.hour = hr; spec.min = 0; spec.sec = 0; token = scanner.nextToken(); day(); break; default: throw new ArgumentException("Unparsable time: " + token.value); } /* * the OFFSET-SPEC part * * (NOTE, the sc_tokid was prefetched for us by the previous code) */ if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) { scanner.setContext(false); while (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS || token.id == TimeToken.NUMBER) { if (token.id == TimeToken.NUMBER) { plusMinus(PREVIOUS_OP); } else { plusMinus(token.id); } token = scanner.nextToken(); /* We will get EOF eventually but that's OK, since * token() will return us as many EOFs as needed */ } } /* now we should be at EOF */ if (token.id != TimeToken.EOF) { throw new ArgumentException("Unparsable trailing text: " + token.value); } return(spec); }
private void day() { long mday = 0; long mon, year = spec.year; switch (token.id) { case TimeToken.YESTERDAY: spec.RemoveDays(1); token = scanner.nextToken(); break; /* FALLTRHU */ case TimeToken.TODAY: /* force ourselves to stay in today - no further processing */ token = scanner.nextToken(); break; case TimeToken.TOMORROW: spec.AddDays(1); token = scanner.nextToken(); break; case TimeToken.JAN: case TimeToken.FEB: case TimeToken.MAR: case TimeToken.APR: case TimeToken.MAY: case TimeToken.JUN: case TimeToken.JUL: case TimeToken.AUG: case TimeToken.SEP: case TimeToken.OCT: case TimeToken.NOV: case TimeToken.DEC: /* do month mday [year] */ mon = (token.id - TimeToken.JAN); expectToken(TimeToken.NUMBER, "the day of the month should follow month name"); mday = long.Parse(token.value); token = scanner.nextToken(); if (token.id == TimeToken.NUMBER) { year = long.Parse(token.value); token = scanner.nextToken(); } else { year = spec.year; } assignDate(mday, mon, year); break; case TimeToken.SUN: case TimeToken.MON: case TimeToken.TUE: case TimeToken.WED: case TimeToken.THU: case TimeToken.FRI: case TimeToken.SAT: /* do a particular day of the week */ int wday = (token.id - TimeToken.SUN); spec.AddDays(wday - spec.wday); token = scanner.nextToken(); break; case TimeToken.NUMBER: /* get numeric <sec since 1970>, MM/DD/[YY]YY, or DD.MM.[YY]YY */ // int tlen = token.value.length(); mon = long.Parse(token.value); if (mon > 10L * 365L * 24L * 60L * 60L) { spec.localtime(mon); token = scanner.nextToken(); break; } if (mon > 19700101 && mon < 24000101) { /*works between 1900 and 2400 */ year = mon / 10000; mday = mon % 100; mon = (mon / 100) % 100; token = scanner.nextToken(); } else { token = scanner.nextToken(); if (mon <= 31 && (token.id == TimeToken.SLASH || token.id == TimeToken.DOT)) { int sep = token.id; expectToken(TimeToken.NUMBER, "there should be " + (sep == TimeToken.DOT ? "month" : "day") + " number after " + (sep == TimeToken.DOT ? '.' : '/')); mday = long.Parse(token.value); token = scanner.nextToken(); if (token.id == sep) { expectToken(TimeToken.NUMBER, "there should be year number after " + (sep == TimeToken.DOT ? '.' : '/')); year = long.Parse(token.value); token = scanner.nextToken(); } /* flip months and days for European timing */ if (sep == TimeToken.DOT) { long x = mday; mday = mon; mon = x; } } } //mon--; if (mon < 1 || mon > 12) { throw new ArgumentException("Did you really mean month " + (mon)); } if (mday < 1 || mday > 31) { throw new ArgumentException("I'm afraid that " + mday + " is not a valid day of the month"); } assignDate(mday, mon, year); break; } }
private void timeOfDay() { int minute = 0; /* save token status in case we must abort */ scanner.saveState(); /* first pick out the time of day - we assume a HH (COLON|DOT) MM time */ if (token.value.Length > 2) { return; } int hour = int.Parse(token.value); token = scanner.nextToken(); if (token.id == TimeToken.SLASH || token.id == TimeToken.DOT) { /* guess we are looking at a date */ token = scanner.restoreState(); return; } if (token.id == TimeToken.COLON) { expectToken(TimeToken.NUMBER, "Parsing HH:MM syntax, expecting MM as number, got none"); minute = int.Parse(token.value); if (minute > 59) { throw new ArgumentException("Parsing HH:MM syntax, got MM = " + minute + " (>59!)"); } token = scanner.nextToken(); } /* check if an AM or PM specifier was given */ if (token.id == TimeToken.AM || token.id == TimeToken.PM) { if (hour > 12) { throw new ArgumentException("There cannot be more than 12 AM or PM hours"); } if (token.id == TimeToken.PM) { if (hour != 12) { /* 12:xx PM is 12:xx, not 24:xx */ hour += 12; } } else { if (hour == 12) { /* 12:xx AM is 00:xx, not 12:xx */ hour = 0; } } token = scanner.nextToken(); } else if (hour > 23) { /* guess it was not a time then ... */ token = scanner.restoreState(); return; } spec.hour = hour; spec.min = minute; spec.sec = 0; if (spec.hour == 24) { spec.hour = 0; spec.AddDays(1); } }
internal TimeToken restoreState() { pos = pos_save; return token = token_save; }
internal TimeToken resolveMonthsMinutes(int newId) { if (token.id != TimeToken.MONTHS_MINUTES) throw new ApplicationException("token id not MONTHS_MINUTES"); if (newId != TimeToken.MONTHS && newId != TimeToken.MINUTES) throw new ApplicationException("new token id not MONTHS_MINUTES || MONTHS"); return token = new TimeToken(token.value, newId); }
internal TimeToken nextToken() { StringBuilder buffer = new StringBuilder(""); while (pos < dateString.Length) { char c = dateString[pos++]; if (char.IsWhiteSpace(c) || c == '_' || c == ',') { continue; } buffer.Append(c); if (char.IsDigit(c)) { // pick as many digits as possible while (pos < dateString.Length) { char next = dateString[pos]; if (char.IsDigit(next)) { buffer.Append(next); pos++; } else { break; } } String value = buffer.ToString(); return token = new TimeToken(value, TimeToken.NUMBER); } if (char.IsLetter(c)) { // pick as many letters as possible while (pos < dateString.Length) { char next = dateString[pos]; if (char.IsLetter(next)) { buffer.Append(next); pos++; } else { break; } } String value = buffer.ToString(); return token = new TimeToken(value, parseToken(value)); } switch (c) { case ':': return token = new TimeToken(":", TimeToken.COLON); case '.': return token = new TimeToken(".", TimeToken.DOT); case '+': return token = new TimeToken("+", TimeToken.PLUS); case '-': return token = new TimeToken("-", TimeToken.MINUS); case '/': return token = new TimeToken("/", TimeToken.SLASH); default: pos--; return token = new TimeToken(null, TimeToken.EOF); } } return token = new TimeToken(null, TimeToken.EOF); }
/** * Parses the input string specified in the constructor. * @return Object representing parsed date/time. */ public TimeSpec parse() { long now = DateTime.Now.Ticks / TimeSpan.TicksPerSecond; int hr = 0; int time_reference; /* this MUST be initialized to zero for midnight/noon/teatime */ /* establish the default time reference */ spec.localtime(now); token = scanner.nextToken(); switch (token.id) { case TimeToken.PLUS: case TimeToken.MINUS: break; /* jump to OFFSET-SPEC part */ case TimeToken.START: spec.type = TimeSpec.TYPE_START; if (spec.type != TimeSpec.TYPE_START) { spec.type = TimeSpec.TYPE_END; } spec.year = spec.month = spec.day = spec.hour = spec.min = spec.sec = 0; time_reference = token.id; token = scanner.nextToken(); if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) { break; } if (time_reference != TimeToken.NOW) throw new ArgumentException("Words 'start' or 'end' MUST be followed by +|- offset"); if (token.id != TimeToken.EOF) throw new ArgumentException("If 'now' is followed by a token it must be +|- offset"); break; /* FALLTHRU */ case TimeToken.END: if (spec.type != TimeSpec.TYPE_START) { spec.type = TimeSpec.TYPE_END; } spec.year = spec.month = spec.day = spec.hour = spec.min = spec.sec = 0; time_reference = token.id; token = scanner.nextToken(); if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) break; if (time_reference != TimeToken.NOW) throw new ArgumentException("Words 'start' or 'end' MUST be followed by +|- offset"); if (token.id != TimeToken.EOF) throw new ArgumentException("If 'now' is followed by a token it must be +|- offset"); break; /* FALLTHRU */ case TimeToken.NOW: time_reference = token.id; token = scanner.nextToken(); if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) break; if (time_reference != TimeToken.NOW) throw new ArgumentException("Words 'start' or 'end' MUST be followed by +|- offset"); if (token.id != TimeToken.EOF) throw new ArgumentException("If 'now' is followed by a token it must be +|- offset"); break; /* Only absolute time specifications below */ case TimeToken.NUMBER: timeOfDay(); if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) { break; } day(); if (token.id != TimeToken.NUMBER) { break; } timeOfDay(); break; /* fix month parsing */ case TimeToken.JAN: case TimeToken.FEB: case TimeToken.MAR: case TimeToken.APR: case TimeToken.MAY: case TimeToken.JUN: case TimeToken.JUL: case TimeToken.AUG: case TimeToken.SEP: case TimeToken.OCT: case TimeToken.NOV: case TimeToken.DEC: day(); if (token.id != TimeToken.NUMBER) { break; } timeOfDay(); break; /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialized * hr to zero up above, then fall into this case in such a * way so we add +12 +4 hours to it for teatime, +12 hours * to it for noon, and nothing at all for midnight, then * set our rettime to that hour before leaping into the * month scanner */ case TimeToken.TEATIME: hr += 4; hr += 12; spec.hour = hr; spec.min = 0; spec.sec = 0; token = scanner.nextToken(); day(); break; /* FALLTHRU */ case TimeToken.NOON: hr += 12; spec.hour = hr; spec.min = 0; spec.sec = 0; token = scanner.nextToken(); day(); break; /* FALLTHRU */ case TimeToken.MIDNIGHT: spec.hour = hr; spec.min = 0; spec.sec = 0; token = scanner.nextToken(); day(); break; default: throw new ArgumentException("Unparsable time: " + token.value); } /* * the OFFSET-SPEC part * * (NOTE, the sc_tokid was prefetched for us by the previous code) */ if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) { scanner.setContext(false); while (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS || token.id == TimeToken.NUMBER) { if (token.id == TimeToken.NUMBER) { plusMinus(PREVIOUS_OP); } else { plusMinus(token.id); } token = scanner.nextToken(); /* We will get EOF eventually but that's OK, since token() will return us as many EOFs as needed */ } } /* now we should be at EOF */ if (token.id != TimeToken.EOF) throw new ArgumentException("Unparsable trailing text: " + token.value); return spec; }
internal TimeToken restoreState() { pos = pos_save; return(token = token_save); }
internal TimeToken nextToken() { StringBuilder buffer = new StringBuilder(""); while (pos < dateString.Length) { char c = dateString[pos++]; if (char.IsWhiteSpace(c) || c == '_' || c == ',') { continue; } buffer.Append(c); if (char.IsDigit(c)) { // pick as many digits as possible while (pos < dateString.Length) { char next = dateString[pos]; if (char.IsDigit(next)) { buffer.Append(next); pos++; } else { break; } } String value = buffer.ToString(); return(token = new TimeToken(value, TimeToken.NUMBER)); } if (char.IsLetter(c)) { // pick as many letters as possible while (pos < dateString.Length) { char next = dateString[pos]; if (char.IsLetter(next)) { buffer.Append(next); pos++; } else { break; } } String value = buffer.ToString(); return(token = new TimeToken(value, parseToken(value))); } switch (c) { case ':': return(token = new TimeToken(":", TimeToken.COLON)); case '.': return(token = new TimeToken(".", TimeToken.DOT)); case '+': return(token = new TimeToken("+", TimeToken.PLUS)); case '-': return(token = new TimeToken("-", TimeToken.MINUS)); case '/': return(token = new TimeToken("/", TimeToken.SLASH)); default: pos--; return(token = new TimeToken(null, TimeToken.EOF)); } } return(token = new TimeToken(null, TimeToken.EOF)); }