1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 package org.jrobin.core.timespec;
30
31 import org.jrobin.core.RrdException;
32 import org.jrobin.core.Util;
33
34
35
36
37
38
39 public class TimeParser {
40 private static final int PREVIOUS_OP = -1;
41
42 TimeToken token;
43 TimeScanner scanner;
44 TimeSpec spec;
45
46 int op = TimeToken.PLUS;
47 int prev_multiplier = -1;
48
49
50
51
52
53
54
55 public TimeParser(String dateString) {
56 scanner = new TimeScanner(dateString);
57 spec = new TimeSpec(dateString);
58 }
59
60 private void expectToken(int desired, String errorMessage) throws RrdException {
61 token = scanner.nextToken();
62 if (token.id != desired) {
63 throw new RrdException(errorMessage);
64 }
65 }
66
67 private void plusMinus(int doop) throws RrdException {
68 if (doop >= 0) {
69 op = doop;
70 expectToken(TimeToken.NUMBER, "There should be number after " +
71 (op == TimeToken.PLUS ? '+' : '-'));
72 prev_multiplier = -1;
73 }
74 int delta = Integer.parseInt(token.value);
75 token = scanner.nextToken();
76 if (token.id == TimeToken.MONTHS_MINUTES) {
77
78 switch (prev_multiplier) {
79 case TimeToken.DAYS:
80 case TimeToken.WEEKS:
81 case TimeToken.MONTHS:
82 case TimeToken.YEARS:
83 token = scanner.resolveMonthsMinutes(TimeToken.MONTHS);
84 break;
85 case TimeToken.SECONDS:
86 case TimeToken.MINUTES:
87 case TimeToken.HOURS:
88 token = scanner.resolveMonthsMinutes(TimeToken.MINUTES);
89 break;
90 default:
91 if (delta < 6) {
92 token = scanner.resolveMonthsMinutes(TimeToken.MONTHS);
93 }
94 else {
95 token = scanner.resolveMonthsMinutes(TimeToken.MINUTES);
96 }
97 }
98 }
99 prev_multiplier = token.id;
100 delta *= (op == TimeToken.PLUS) ? +1 : -1;
101 switch (token.id) {
102 case TimeToken.YEARS:
103 spec.dyear += delta;
104 break;
105 case TimeToken.MONTHS:
106 spec.dmonth += delta;
107 break;
108 case TimeToken.WEEKS:
109 delta *= 7;
110
111 case TimeToken.DAYS:
112 spec.dday += delta;
113 break;
114 case TimeToken.HOURS:
115 spec.dhour += delta;
116 break;
117 case TimeToken.MINUTES:
118 spec.dmin += delta;
119 break;
120 case TimeToken.SECONDS:
121 default:
122 spec.dsec += delta;
123 break;
124 }
125
126
127 }
128
129 private void timeOfDay() throws RrdException {
130 int hour, minute = 0;
131
132 scanner.saveState();
133
134 if (token.value.length() > 2) {
135 return;
136 }
137 hour = Integer.parseInt(token.value);
138 token = scanner.nextToken();
139 if (token.id == TimeToken.SLASH || token.id == TimeToken.DOT) {
140
141 token = scanner.restoreState();
142 return;
143 }
144 if (token.id == TimeToken.COLON) {
145 expectToken(TimeToken.NUMBER, "Parsing HH:MM syntax, expecting MM as number, got none");
146 minute = Integer.parseInt(token.value);
147 if (minute > 59) {
148 throw new RrdException("Parsing HH:MM syntax, got MM = " +
149 minute + " (>59!)");
150 }
151 token = scanner.nextToken();
152 }
153
154 if (token.id == TimeToken.AM || token.id == TimeToken.PM) {
155 if (hour > 12) {
156 throw new RrdException("There cannot be more than 12 AM or PM hours");
157 }
158 if (token.id == TimeToken.PM) {
159 if (hour != 12) {
160
161 hour += 12;
162 }
163 }
164 else {
165 if (hour == 12) {
166
167 hour = 0;
168 }
169 }
170 token = scanner.nextToken();
171 }
172 else if (hour > 23) {
173
174 token = scanner.restoreState();
175 return;
176 }
177 spec.hour = hour;
178 spec.min = minute;
179 spec.sec = 0;
180 if (spec.hour == 24) {
181 spec.hour = 0;
182 spec.day++;
183 }
184 }
185
186 private void assignDate(long mday, long mon, long year) throws RrdException {
187 if (year > 138) {
188 if (year > 1970) {
189 year -= 1900;
190 }
191 else {
192 throw new RrdException("Invalid year " + year +
193 " (should be either 00-99 or >1900)");
194 }
195 }
196 else if (year >= 0 && year < 38) {
197 year += 100;
198 }
199
200 if (year < 70) {
201 throw new RrdException("Won't handle dates before epoch (01/01/1970), sorry");
202 }
203 spec.year = (int) year;
204 spec.month = (int) mon;
205 spec.day = (int) mday;
206 }
207
208 private void day() throws RrdException {
209 long mday = 0, wday, mon, year = spec.year;
210 switch (token.id) {
211 case TimeToken.YESTERDAY:
212 spec.day--;
213
214 case TimeToken.TODAY:
215 token = scanner.nextToken();
216 break;
217 case TimeToken.TOMORROW:
218 spec.day++;
219 token = scanner.nextToken();
220 break;
221 case TimeToken.JAN:
222 case TimeToken.FEB:
223 case TimeToken.MAR:
224 case TimeToken.APR:
225 case TimeToken.MAY:
226 case TimeToken.JUN:
227 case TimeToken.JUL:
228 case TimeToken.AUG:
229 case TimeToken.SEP:
230 case TimeToken.OCT:
231 case TimeToken.NOV:
232 case TimeToken.DEC:
233
234 mon = (token.id - TimeToken.JAN);
235 expectToken(TimeToken.NUMBER, "the day of the month should follow month name");
236 mday = Long.parseLong(token.value);
237 token = scanner.nextToken();
238 if (token.id == TimeToken.NUMBER) {
239 year = Long.parseLong(token.value);
240 token = scanner.nextToken();
241 }
242 else {
243 year = spec.year;
244 }
245 assignDate(mday, mon, year);
246 break;
247 case TimeToken.SUN:
248 case TimeToken.MON:
249 case TimeToken.TUE:
250 case TimeToken.WED:
251 case TimeToken.THU:
252 case TimeToken.FRI:
253 case TimeToken.SAT:
254
255 wday = (token.id - TimeToken.SUN);
256 spec.day += (wday - spec.wday);
257 token = scanner.nextToken();
258 break;
259 case TimeToken.NUMBER:
260
261
262 mon = Long.parseLong(token.value);
263 if (mon > 10L * 365L * 24L * 60L * 60L) {
264 spec.localtime(mon);
265 token = scanner.nextToken();
266 break;
267 }
268 if (mon > 19700101 && mon < 24000101) {
269 year = mon / 10000;
270 mday = mon % 100;
271 mon = (mon / 100) % 100;
272 token = scanner.nextToken();
273 }
274 else {
275 token = scanner.nextToken();
276 if (mon <= 31 && (token.id == TimeToken.SLASH || token.id == TimeToken.DOT)) {
277 int sep = token.id;
278 expectToken(TimeToken.NUMBER, "there should be " +
279 (sep == TimeToken.DOT ? "month" : "day") +
280 " number after " +
281 (sep == TimeToken.DOT ? '.' : '/'));
282 mday = Long.parseLong(token.value);
283 token = scanner.nextToken();
284 if (token.id == sep) {
285 expectToken(TimeToken.NUMBER, "there should be year number after " +
286 (sep == TimeToken.DOT ? '.' : '/'));
287 year = Long.parseLong(token.value);
288 token = scanner.nextToken();
289 }
290
291 if (sep == TimeToken.DOT) {
292 long x = mday;
293 mday = mon;
294 mon = x;
295 }
296 }
297 }
298 mon--;
299 if (mon < 0 || mon > 11) {
300 throw new RrdException("Did you really mean month " + (mon + 1));
301 }
302 if (mday < 1 || mday > 31) {
303 throw new RrdException("I'm afraid that " + mday +
304 " is not a valid day of the month");
305 }
306 assignDate(mday, mon, year);
307 break;
308 }
309 }
310
311
312
313
314
315
316
317 public TimeSpec parse() throws RrdException {
318 long now = Util.getTime();
319 int hr = 0;
320
321
322 spec.localtime(now);
323 token = scanner.nextToken();
324 switch (token.id) {
325 case TimeToken.PLUS:
326 case TimeToken.MINUS:
327 break;
328 case TimeToken.START:
329 spec.type = TimeSpec.TYPE_START;
330
331 case TimeToken.END:
332 if (spec.type != TimeSpec.TYPE_START) {
333 spec.type = TimeSpec.TYPE_END;
334 }
335 spec.year = spec.month = spec.day = spec.hour = spec.min = spec.sec = 0;
336
337 case TimeToken.NOW:
338 int time_reference = token.id;
339 token = scanner.nextToken();
340 if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) {
341 break;
342 }
343 if (time_reference != TimeToken.NOW) {
344 throw new RrdException("Words 'start' or 'end' MUST be followed by +|- offset");
345 }
346 else if (token.id != TimeToken.EOF) {
347 throw new RrdException("If 'now' is followed by a token it must be +|- offset");
348 }
349 break;
350
351 case TimeToken.NUMBER:
352 timeOfDay();
353 if (token.id != TimeToken.NUMBER) {
354 break;
355 }
356
357 case TimeToken.JAN:
358 case TimeToken.FEB:
359 case TimeToken.MAR:
360 case TimeToken.APR:
361 case TimeToken.MAY:
362 case TimeToken.JUN:
363 case TimeToken.JUL:
364 case TimeToken.AUG:
365 case TimeToken.SEP:
366 case TimeToken.OCT:
367 case TimeToken.NOV:
368 case TimeToken.DEC:
369 day();
370 if (token.id != TimeToken.NUMBER) {
371 break;
372 }
373 timeOfDay();
374 break;
375
376
377
378
379
380
381
382
383 case TimeToken.TEATIME:
384 hr += 4;
385
386 case TimeToken.NOON:
387 hr += 12;
388
389 case TimeToken.MIDNIGHT:
390 spec.hour = hr;
391 spec.min = 0;
392 spec.sec = 0;
393 token = scanner.nextToken();
394 day();
395 break;
396 default:
397 throw new RrdException("Unparsable time: " + token.value);
398 }
399
400
401
402
403
404
405 if (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS) {
406 scanner.setContext(false);
407 while (token.id == TimeToken.PLUS || token.id == TimeToken.MINUS ||
408 token.id == TimeToken.NUMBER) {
409 if (token.id == TimeToken.NUMBER) {
410 plusMinus(PREVIOUS_OP);
411 }
412 else {
413 plusMinus(token.id);
414 }
415 token = scanner.nextToken();
416
417
418 }
419 }
420
421 if (token.id != TimeToken.EOF) {
422 throw new RrdException("Unparsable trailing text: " + token.value);
423 }
424 return spec;
425 }
426 }