1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package javax.mail.internet;
21
22 import java.text.FieldPosition;
23 import java.text.NumberFormat;
24 import java.text.ParseException;
25 import java.text.ParsePosition;
26 import java.text.SimpleDateFormat;
27 import java.util.Calendar;
28 import java.util.Date;
29 import java.util.GregorianCalendar;
30 import java.util.Locale;
31 import java.util.TimeZone;
32
33
34
35
36
37
38
39
40
41
42
43
44 public class MailDateFormat extends SimpleDateFormat {
45 public MailDateFormat() {
46 super("EEE, d MMM yyyy HH:mm:ss Z (z)", Locale.US);
47 }
48
49 public StringBuffer format(Date date, StringBuffer buffer, FieldPosition position) {
50 return super.format(date, buffer, position);
51 }
52
53
54
55
56
57
58
59
60
61
62
63 public Date parse(String string, ParsePosition position) {
64 MailDateParser parser = new MailDateParser(string, position);
65 try {
66 return parser.parse(isLenient());
67 } catch (ParseException e) {
68 e.printStackTrace();
69
70 return null;
71 }
72 }
73
74
75
76
77
78
79 public void setCalendar(Calendar calendar) {
80 throw new UnsupportedOperationException();
81 }
82
83
84
85
86
87
88 public void setNumberFormat(NumberFormat format) {
89 throw new UnsupportedOperationException();
90 }
91
92
93
94 class MailDateParser {
95
96 static final String whitespace = " \t\r\n";
97
98
99 int current;
100
101 int endOffset;
102
103 String source;
104
105
106 ParsePosition pos;
107
108 public MailDateParser(String source, ParsePosition pos)
109 {
110 this.source = source;
111 this.pos = pos;
112
113 this.current = pos.getIndex();
114 this.endOffset = source.length();
115 }
116
117
118
119
120
121
122
123
124
125 public Date parse(boolean lenient) throws ParseException {
126
127
128 locateNumeric();
129
130 int day = parseNumber(1, 2);
131
132 skipDateDelimiter();
133
134 int month = parseMonth();
135
136 skipDateDelimiter();
137
138 int year = parseYear();
139
140 skipRequiredWhiteSpace();
141
142 int hour = parseNumber(1, 2);
143 skipRequiredChar(':');
144
145 int minutes = parseNumber(2, 2);
146
147
148
149 int seconds = 0;
150 if (skipOptionalChar(':')) {
151 seconds = parseNumber(2, 2);
152 }
153
154 skipWhiteSpace();
155
156 int offset = parseTimeZone();
157
158
159 pos.setIndex(current);
160
161
162 Calendar greg = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
163
164 greg.setLenient(lenient);
165 greg.set(year, month, day, hour, minutes, seconds);
166
167
168
169
170
171 greg.add(Calendar.MINUTE, -offset);
172
173 return greg.getTime();
174 }
175
176
177
178
179
180
181
182
183
184
185 private void skipRequiredChar(char ch) throws ParseException {
186 if (current >= endOffset) {
187 parseError("Delimiter '" + ch + "' expected");
188 }
189 if (source.charAt(current) != ch) {
190 parseError("Delimiter '" + ch + "' expected");
191 }
192 current++;
193 }
194
195
196
197
198
199
200
201
202
203
204
205 private boolean skipOptionalChar(char ch) {
206 if (current >= endOffset) {
207 return false;
208 }
209 if (source.charAt(current) != ch) {
210 return false;
211 }
212 current++;
213 return true;
214 }
215
216
217
218
219
220
221
222 private void skipWhiteSpace() {
223 while (current < endOffset) {
224
225 if (whitespace.indexOf(source.charAt(current)) < 0) {
226 return;
227 }
228 current++;
229 }
230
231
232 }
233
234
235
236
237
238
239 private void skipNonWhiteSpace() {
240 while (current < endOffset) {
241
242 if (whitespace.indexOf(source.charAt(current)) >= 0) {
243 return;
244 }
245 current++;
246 }
247
248
249 }
250
251
252
253
254
255
256
257 private void skipRequiredWhiteSpace() throws ParseException {
258 int start = current;
259
260 while (current < endOffset) {
261
262 if (whitespace.indexOf(source.charAt(current)) < 0) {
263
264 if (start == current) {
265 parseError("White space character expected");
266 }
267 return;
268 }
269 current++;
270 }
271
272
273 if (start == current) {
274 parseError("White space character expected");
275 }
276 }
277
278 private void parseError(String message) throws ParseException {
279
280 pos.setErrorIndex(current);
281 throw new ParseException(message, current);
282 }
283
284
285
286
287
288
289
290 private void locateNumeric() throws ParseException {
291 while (current < endOffset) {
292
293 if (Character.isDigit(source.charAt(current))) {
294 return;
295 }
296 current++;
297 }
298
299 parseError("Number field expected");
300 }
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315 private int parseNumber(int minDigits, int maxDigits) throws ParseException {
316 int start = current;
317 int accumulator = 0;
318 while (current < endOffset) {
319 char ch = source.charAt(current);
320
321 if (!Character.isDigit(ch)) {
322 break;
323 }
324
325 accumulator = accumulator * 10 + Character.digit(ch, 10);
326 current++;
327 }
328
329 int fieldLength = current - start;
330 if (fieldLength < minDigits || fieldLength > maxDigits) {
331 parseError("Invalid number field");
332 }
333
334 return accumulator;
335 }
336
337
338
339
340
341
342
343
344
345 private void skipDateDelimiter() throws ParseException {
346 if (current >= endOffset) {
347 parseError("Invalid date field delimiter");
348 }
349
350 if (source.charAt(current) == '-') {
351 current++;
352 }
353 else {
354
355 skipRequiredWhiteSpace();
356 }
357 }
358
359
360
361
362
363
364
365
366
367 private int parseMonth() throws ParseException {
368 if ((endOffset - current) < 3) {
369 parseError("Invalid month");
370 }
371
372 int monthOffset = 0;
373 String month = source.substring(current, current + 3).toLowerCase();
374
375 if (month.equals("jan")) {
376 monthOffset = 0;
377 }
378 else if (month.equals("feb")) {
379 monthOffset = 1;
380 }
381 else if (month.equals("mar")) {
382 monthOffset = 2;
383 }
384 else if (month.equals("apr")) {
385 monthOffset = 3;
386 }
387 else if (month.equals("may")) {
388 monthOffset = 4;
389 }
390 else if (month.equals("jun")) {
391 monthOffset = 5;
392 }
393 else if (month.equals("jul")) {
394 monthOffset = 6;
395 }
396 else if (month.equals("aug")) {
397 monthOffset = 7;
398 }
399 else if (month.equals("sep")) {
400 monthOffset = 8;
401 }
402 else if (month.equals("oct")) {
403 monthOffset = 9;
404 }
405 else if (month.equals("nov")) {
406 monthOffset = 10;
407 }
408 else if (month.equals("dec")) {
409 monthOffset = 11;
410 }
411 else {
412 parseError("Invalid month");
413 }
414
415
416 current += 3;
417 return monthOffset;
418 }
419
420
421
422
423
424
425
426
427 private int parseYear() throws ParseException {
428
429 int year = parseNumber(2, 4);
430
431
432 if (year < 50) {
433 year += 2000;
434 }
435 else if (year < 100) {
436 year += 1990;
437 }
438 return year;
439 }
440
441
442
443
444
445
446
447
448 private int parseTimeZone() throws ParseException {
449 if (current >= endOffset) {
450 parseError("Missing time zone");
451 }
452
453
454
455 char sign = source.charAt(current);
456
457 if (sign == '-' || sign == '+') {
458
459 current++;
460
461
462
463
464 int zoneInfo = parseNumber(4, 4);
465
466 int offset = (zoneInfo / 100) * 60 + (zoneInfo % 100);
467
468 if (sign == '-') {
469 offset = -offset;
470 }
471 return offset;
472 }
473 else {
474
475
476
477 int start = current;
478 skipNonWhiteSpace();
479 String name = source.substring(start, current).toUpperCase();
480
481 if (name.length() == 1) {
482 return militaryZoneOffset(name);
483 }
484 else if (name.length() <= 3) {
485 return namedZoneOffset(name);
486 }
487 else {
488 parseError("Invalid time zone");
489 }
490 return 0;
491 }
492 }
493
494
495
496
497
498
499
500
501
502
503
504
505
506 private int namedZoneOffset(String name) throws ParseException {
507
508
509 if (name.equals("UT")) {
510 return 0;
511 }
512 else if (name.equals("GMT")) {
513 return 0;
514 }
515 else if (name.equals("EST")) {
516 return -300;
517 }
518 else if (name.equals("EDT")) {
519 return -240;
520 }
521 else if (name.equals("CST")) {
522 return -360;
523 }
524 else if (name.equals("CDT")) {
525 return -300;
526 }
527 else if (name.equals("MST")) {
528 return -420;
529 }
530 else if (name.equals("MDT")) {
531 return -360;
532 }
533 else if (name.equals("PST")) {
534 return -480;
535 }
536 else if (name.equals("PDT")) {
537 return -420;
538 }
539 else {
540 parseError("Invalid time zone");
541 return 0;
542 }
543 }
544
545
546
547
548
549
550
551
552
553 private int militaryZoneOffset(String name) throws ParseException {
554 switch (Character.toUpperCase(name.charAt(0))) {
555 case 'A':
556 return 60;
557 case 'B':
558 return 120;
559 case 'C':
560 return 180;
561 case 'D':
562 return 240;
563 case 'E':
564 return 300;
565 case 'F':
566 return 360;
567 case 'G':
568 return 420;
569 case 'H':
570 return 480;
571 case 'I':
572 return 540;
573 case 'K':
574 return 600;
575 case 'L':
576 return 660;
577 case 'M':
578 return 720;
579 case 'N':
580 return -60;
581 case 'O':
582 return -120;
583 case 'P':
584 return -180;
585 case 'Q':
586 return -240;
587 case 'R':
588 return -300;
589 case 'S':
590 return -360;
591 case 'T':
592 return -420;
593 case 'U':
594 return -480;
595 case 'V':
596 return -540;
597 case 'W':
598 return -600;
599 case 'X':
600 return -660;
601 case 'Y':
602 return -720;
603 case 'Z':
604 return 0;
605 default:
606 parseError("Invalid time zone");
607 return 0;
608 }
609 }
610 }
611 }