1. /*
  2. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  3. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  4. */
  5. package javax.mail.internet;
  6. import java.util.Date;
  7. import java.util.Calendar;
  8. import java.util.GregorianCalendar;
  9. import java.util.Locale;
  10. import java.util.TimeZone;
  11. import java.text.DateFormat;
  12. import java.text.SimpleDateFormat;
  13. import java.text.NumberFormat;
  14. import java.text.FieldPosition;
  15. import java.text.ParsePosition;
  16. import java.text.ParseException;
  17. /**
  18. * Formats and parses date specification based on the
  19. * draft-ietf-drums-msg-fmt-08 dated January 26, 2000. This is a followup
  20. * spec to RFC822.<p>
  21. *
  22. * This class does not take pattern strings. It always formats the
  23. * date based on the specification below.<p>
  24. *
  25. * 3.3 Date and Time Specification<p>
  26. *
  27. * Date and time occur in several header fields of a message. This section
  28. * specifies the syntax for a full date and time specification. Though folding
  29. * whitespace is permitted throughout the date-time specification, it is
  30. * recommended that only a single space be used where FWS is required and no
  31. * space be used where FWS is optional in the date-time specification; some
  32. * older implementations may not interpret other occurrences of folding
  33. * whitespace correctly.<p>
  34. *
  35. * date-time = [ day-of-week "," ] date FWS time [CFWS]<p>
  36. *
  37. * day-of-week = ([FWS] day-name) / obs-day-of-week<p>
  38. *
  39. * day-name = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun"<p>
  40. *
  41. * date = day month year<p>
  42. *
  43. * year = 4*DIGIT / obs-year<p>
  44. *
  45. * month = (FWS month-name FWS) / obs-month<p>
  46. *
  47. *<pre>month-name = "Jan" / "Feb" / "Mar" / "Apr" /
  48. * "May" / "Jun" / "Jul" / "Aug" /
  49. * "Sep" / "Oct" / "Nov" / "Dec"
  50. * </pre><p>
  51. * day = ([FWS] 1*2DIGIT) / obs-day<p>
  52. *
  53. * time = time-of-day FWS zone<p>
  54. *
  55. * time-of-day = hour ":" minute [ ":" second ]<p>
  56. *
  57. * hour = 2DIGIT / obs-hour<p>
  58. *
  59. * minute = 2DIGIT / obs-minute<p>
  60. *
  61. * second = 2DIGIT / obs-second<p>
  62. *
  63. * zone = (( "+" / "-" ) 4DIGIT) / obs-zone<p>
  64. *
  65. *
  66. * The day is the numeric day of the month. The year is any numeric year in
  67. * the common era.<p>
  68. *
  69. * The time-of-day specifies the number of hours, minutes, and optionally
  70. * seconds since midnight of the date indicated.<p>
  71. *
  72. * The date and time-of-day SHOULD express local time.<p>
  73. *
  74. * The zone specifies the offset from Coordinated Universal Time (UTC,
  75. * formerly referred to as "Greenwich Mean Time") that the date and
  76. * time-of-day represent. The "+" or "-" indicates whether the time-of-day is
  77. * ahead of or behind Universal Time. The first two digits indicate the number
  78. * of hours difference from Universal Time, and the last two digits indicate
  79. * the number of minutes difference from Universal Time. (Hence, +hhmm means
  80. * +(hh * 60 + mm) minutes, and -hhmm means -(hh * 60 + mm) minutes). The form
  81. * "+0000" SHOULD be used to indicate a time zone at Universal Time. Though
  82. * "-0000" also indicates Universal Time, it is used to indicate that the time
  83. * was generated on a system that may be in a local time zone other than
  84. * Universal Time.<p>
  85. *
  86. * A date-time specification MUST be semantically valid. That is, the
  87. * day-of-the week (if included) MUST be the day implied by the date, the
  88. * numeric day-of-month MUST be between 1 and the number of days allowed for
  89. * the specified month (in the specified year), the time-of-day MUST be in the
  90. * range 00:00:00 through 23:59:60 (the number of seconds allowing for a leap
  91. * second; see [STD-12]), and the zone MUST be within the range -9959 through
  92. * +9959.<p>
  93. *
  94. * @author Max Spivak
  95. * @since JavaMail 1.2
  96. */
  97. public class MailDateFormat extends SimpleDateFormat {
  98. public MailDateFormat() {
  99. super("EEE, d MMM yyyy HH:mm:ss 'XXXXX' (z)", Locale.US);
  100. }
  101. /**
  102. * Formats the given date in the format specified by
  103. * draft-ietf-drums-msg-fmt-08 in the current TimeZone.
  104. *
  105. * @param date the Date object
  106. * @param dateStrBuf the formatted string
  107. * @param fieldPosition the current field position
  108. * @returns StringBuffer the formatted String
  109. * @since JavaMail 1.2
  110. */
  111. public StringBuffer format(Date date, StringBuffer dateStrBuf,
  112. FieldPosition fieldPosition) {
  113. /* How this method works: First format the date with the
  114. * format specified in the constructor inserting string 'XXXXX'
  115. * where the timezone offset goes. Find where in the string the
  116. * string 'XXXXX' appears and remember that in var "pos".
  117. * Calculate the offset, taking the DST into account and insert
  118. * it into the stringbuffer at position pos.
  119. */
  120. int start = dateStrBuf.length();
  121. super.format(date, dateStrBuf, fieldPosition);
  122. int pos = 0;
  123. // find the beginning of the 'XXXXX' string in the formatted date
  124. // 25 is the first position that we expect to find XXXXX at
  125. for (pos = start + 25; dateStrBuf.charAt(pos) != 'X'; pos++)
  126. ;
  127. // set the timezone to +HHMM or -HHMM
  128. calendar.clear();
  129. calendar.setTime(date);
  130. int offset = calendar.get(Calendar.ZONE_OFFSET) +
  131. calendar.get(Calendar.DST_OFFSET);
  132. // take care of the sign
  133. if (offset < 0) {
  134. dateStrBuf.setCharAt(pos++, '-');
  135. offset = (-offset);
  136. } else
  137. dateStrBuf.setCharAt(pos++, '+');
  138. int rawOffsetInMins = offset / 60 / 1000; // offset from GMT in mins
  139. int offsetInHrs = rawOffsetInMins / 60;
  140. int offsetInMins = rawOffsetInMins % 60;
  141. dateStrBuf.setCharAt(pos++, Character.forDigit((offsetInHrs10), 10));
  142. dateStrBuf.setCharAt(pos++, Character.forDigit((offsetInHrs%10), 10));
  143. dateStrBuf.setCharAt(pos++, Character.forDigit((offsetInMins10), 10));
  144. dateStrBuf.setCharAt(pos++, Character.forDigit((offsetInMins%10), 10));
  145. // done with timezone
  146. return dateStrBuf;
  147. }
  148. ////////////////////////////////////////////////////////////
  149. /**
  150. * Parses the given date in the format specified by
  151. * draft-ietf-drums-msg-fmt-08 in the current TimeZone.
  152. *
  153. * @param text the formatted date to be parsed
  154. * @param pos the current parse position
  155. * @returns Date the parsed date in a Date object
  156. * @since JavaMail 1.2
  157. */
  158. public Date parse(String text, ParsePosition pos) {
  159. return parseDate(text.toCharArray(), pos);
  160. }
  161. /*
  162. Valid Examples:
  163. Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)
  164. Date:
  165. Date: Mon, 22 Mar 1993 09:41:09 -0800 (PST)
  166. Date: 26 Aug 76 14:29 EDT
  167. */
  168. /**
  169. * method of what to look for:
  170. *
  171. *
  172. * skip WS
  173. * skip day "," (this is "Mon", "Tue")
  174. * skip WS
  175. *
  176. * parse number (until WS) ==> 1*2DIGIT (day of month)
  177. *
  178. * skip WS
  179. *
  180. * parse alpha chars (until WS) ==> find month
  181. *
  182. * skip WS
  183. *
  184. * parse number (until WS) ==> 2*4DIGIT (year)
  185. *
  186. * skip WS
  187. *
  188. * // now looking for time
  189. * parse number (until ':') ==> hours
  190. * parse number (until ':') ==> minutes
  191. * parse number (until WS) ==> seconds
  192. *
  193. * // now look for Time Zone
  194. * skip WS
  195. * if ('+' or '-') then numerical time zone offset
  196. * if (alpha) then alpha time zone offset
  197. */
  198. static boolean debug = false;
  199. /**
  200. * create a Date by parsing the char array
  201. */
  202. static private Date parseDate(char[] orig, ParsePosition pos) {
  203. try {
  204. int day = -1;
  205. int month = -1;
  206. int year = -1;
  207. int hours = 0;
  208. int minutes = 0;
  209. int seconds = 0;
  210. int offset = 0;
  211. MailDateParser p = new MailDateParser(orig);
  212. // get the day
  213. p.skipUntilNumber();
  214. day = p.parseNumber();
  215. if (!p.skipIfChar('-')) { // for IMAP internal Date
  216. p.skipWhiteSpace();
  217. }
  218. // get the month
  219. month = p.parseMonth();
  220. if (!p.skipIfChar('-')) { // for IMAP internal Date
  221. p.skipWhiteSpace();
  222. }
  223. // get the year
  224. year = p.parseNumber(); // should not return a negative number
  225. if (year < 50) {
  226. year += 2000;
  227. } else if (year < 100) {
  228. year += 1900;
  229. } // otherwise the year is correct (and should be 4 digits)
  230. // get the time
  231. // first get hours
  232. p.skipWhiteSpace();
  233. hours = p.parseNumber();
  234. // get minutes
  235. p.skipChar(':');
  236. minutes = p.parseNumber();
  237. // get seconds (may be no seconds)
  238. if (p.skipIfChar(':')) {
  239. seconds = p.parseNumber();
  240. }
  241. // try to get a Time Zone
  242. try {
  243. p.skipWhiteSpace();
  244. offset = p.parseTimeZone();
  245. } catch (ParseException pe) {
  246. if (debug) {
  247. System.out.println("No timezone? : '" + orig + "'");
  248. }
  249. }
  250. pos.setIndex(p.getIndex());
  251. Date d = ourUTC(year, month, day, hours, minutes, seconds, offset);
  252. return d;
  253. } catch (Exception e) {
  254. // Catch *all* exceptions, including RuntimeExceptions like
  255. // ArrayIndexOutofBoundsException ... we need to be
  256. // extra tolerant of all those bogus dates that might screw
  257. // up our parser. Sigh.
  258. if (debug) {
  259. System.out.println("Bad date: '" + orig + "'");
  260. e.printStackTrace();
  261. }
  262. pos.setIndex(1); // to prevent DateFormat.parse() from throwing ex
  263. return null;
  264. }
  265. }
  266. private static TimeZone tz = TimeZone.getTimeZone("GMT");
  267. private static Calendar cal = new GregorianCalendar(tz);
  268. private synchronized static Date ourUTC(int year, int mon, int mday,
  269. int hour, int min, int sec,
  270. int tzoffset) {
  271. // clear the time and then set all the values
  272. cal.clear();
  273. cal.set(Calendar.YEAR, year);
  274. cal.set(Calendar.MONTH, mon);
  275. cal.set(Calendar.DATE, mday);
  276. cal.set(Calendar.HOUR_OF_DAY, hour);
  277. cal.set(Calendar.MINUTE, min + tzoffset); // adjusted for the timezone
  278. cal.set(Calendar.SECOND, sec);
  279. return cal.getTime();
  280. }
  281. ////////////////////////////////////////////////////////////
  282. /** Don't allow setting the calendar */
  283. public void setCalendar(Calendar newCalendar) {
  284. throw new RuntimeException("Method setCalendar() shouldn't be called");
  285. }
  286. /** Don't allow setting the NumberFormat */
  287. public void setNumberFormat(NumberFormat newNumberFormat) {
  288. throw new RuntimeException("Method setCalendar() shouldn't be called");
  289. }
  290. /* test code for MailDateFormat */
  291. /*
  292. public static void main(String[] args) {
  293. DateFormat df = new MailDateFormat();
  294. Date d = new Date();
  295. // test output in all the timezones
  296. System.out.println("------- test all timezones ---------------");
  297. System.out.println("Current date: " + d);
  298. String[] allIDs = TimeZone.getAvailableIDs();
  299. for (int i = 0; i < allIDs.length; i++) {
  300. TimeZone tz = TimeZone.getTimeZone(allIDs[i]);
  301. df.setTimeZone(tz);
  302. System.out.println("Date in " + tz.getID() + ": " +
  303. df.format(new Date()));
  304. }
  305. try {
  306. System.out.println(df.parse("Sun, 21 Mar 1993 23:56:48 -0800 (PST)"));
  307. System.out.println(df.parse("Mon, 22 Mar 1994 13:34:51 +0000"));
  308. System.out.println(df.parse("26 Aug 76 14:29 EDT"));
  309. System.out.println(df.parse("15 Apr 11 23:49 EST"));
  310. System.out.println(df.parse("15 Apr 11 23:49 ABC"));
  311. } catch (ParseException pex) {
  312. pex.printStackTrace();
  313. }
  314. // reset DateFormat TZ
  315. df.setTimeZone(TimeZone.getDefault());
  316. // test all days in a month
  317. System.out.println();
  318. System.out.println("------- test all days in a month ---------------");
  319. Calendar cal = Calendar.getInstance();
  320. cal.set(Calendar.YEAR, 1972);
  321. cal.set(Calendar.MONTH, Calendar.OCTOBER);
  322. cal.set(Calendar.DATE, 1);
  323. cal.set(Calendar.HOUR, 10);
  324. cal.set(Calendar.MINUTE, 50);
  325. cal.set(Calendar.AM_PM, Calendar.PM);
  326. System.out.println("Initial Date: " + cal.getTime());
  327. System.out.println("Current Date: " + df.format(cal.getTime()));
  328. for (int i = 0; i < 30; i++) {
  329. cal.roll(Calendar.DATE, true);
  330. System.out.println("Current Date: " + df.format(cal.getTime()));
  331. }
  332. // test all months
  333. System.out.println();
  334. System.out.println("------- test all months in a year -----------");
  335. cal.set(Calendar.MONTH, Calendar.JANUARY);
  336. cal.set(Calendar.DATE, 7);
  337. System.out.println("Initial Date: " + cal.getTime());
  338. System.out.println("Current Date: " + df.format(cal.getTime()));
  339. for (int i = 1; i < 12; i++) {
  340. cal.roll(Calendar.MONTH, true);
  341. System.out.println("Current Date: " + df.format(cal.getTime()));
  342. }
  343. // test leap years
  344. System.out.println();
  345. System.out.println("------- test leap years -----------");
  346. cal.set(Calendar.YEAR, 1999);
  347. cal.set(Calendar.MONTH, Calendar.JANUARY);
  348. cal.set(Calendar.DATE, 31);
  349. cal.roll(Calendar.MONTH, true);
  350. System.out.println("Initial Date: " + cal.getTime());
  351. System.out.println("Current Date: " + df.format(cal.getTime()));
  352. for (int i = 1; i < 12; i++) {
  353. cal.set(Calendar.MONTH, Calendar.JANUARY);
  354. cal.set(Calendar.DATE, 31);
  355. cal.roll(Calendar.YEAR, true);
  356. cal.roll(Calendar.MONTH, true);
  357. System.out.println("Current Date: " + df.format(cal.getTime()));
  358. }
  359. }
  360. */
  361. }
  362. /**
  363. * Helper class to deal with parsing the characters
  364. */
  365. class MailDateParser {
  366. int index = 0;
  367. char[] orig = null;
  368. public MailDateParser(char[] orig) {
  369. this.orig = orig;
  370. }
  371. /**
  372. * skips chars until it finds a number (0-9)
  373. *
  374. * if it does not find a number, it will get throw
  375. * an ArrayIndexOutOfBoundsException
  376. */
  377. public void skipUntilNumber() throws ParseException {
  378. try {
  379. while (true) {
  380. switch ( orig[index] ) {
  381. case '0':
  382. case '1':
  383. case '2':
  384. case '3':
  385. case '4':
  386. case '5':
  387. case '6':
  388. case '7':
  389. case '8':
  390. case '9':
  391. return;
  392. default:
  393. index++;
  394. break;
  395. }
  396. }
  397. } catch (ArrayIndexOutOfBoundsException e) {
  398. throw new ParseException("No Number Found", index);
  399. }
  400. }
  401. /**
  402. * skips any number of tabs and spaces
  403. */
  404. public void skipWhiteSpace() {
  405. int len = orig.length;
  406. while (index < len) {
  407. switch (orig[index]) {
  408. case ' ': // space
  409. case '\t': // tab
  410. index++;
  411. break;
  412. default:
  413. return;
  414. }
  415. }
  416. }
  417. /**
  418. * used to look at the next character without "parsing" that
  419. * character.
  420. */
  421. public int peekChar() throws ParseException {
  422. if (index < orig.length)
  423. return orig[index];
  424. else
  425. throw new ParseException("No more characters", index);
  426. }
  427. /**
  428. * skips the given character. if the current char does not
  429. * match a ParseException will be thrown
  430. */
  431. public void skipChar(char c) throws ParseException {
  432. if (index < orig.length) {
  433. if (orig[index] == c) {
  434. index++;
  435. } else {
  436. throw new ParseException("Wrong char", index);
  437. }
  438. } else {
  439. throw new ParseException("No more characters", index);
  440. }
  441. }
  442. /**
  443. * will only skip the current char if it matches the given
  444. * char
  445. */
  446. public boolean skipIfChar(char c) throws ParseException {
  447. if (index < orig.length) {
  448. if (orig[index] == c) {
  449. index++;
  450. return true;
  451. } else {
  452. return false;
  453. }
  454. } else {
  455. throw new ParseException("No more characters", index);
  456. }
  457. }
  458. /**
  459. * current char must point to a number. the number will be
  460. * parsed and the resulting number will be returned. if a
  461. * number is not found, a ParseException will be thrown
  462. */
  463. public int parseNumber() throws ParseException {
  464. int length = orig.length;
  465. boolean gotNum = false;
  466. int result = 0;
  467. while (index < length) {
  468. switch( orig[index] ) {
  469. case '0':
  470. result *= 10;
  471. gotNum = true;
  472. break;
  473. case '1':
  474. result = result * 10 + 1;
  475. gotNum = true;
  476. break;
  477. case '2':
  478. result = result * 10 + 2;
  479. gotNum = true;
  480. break;
  481. case '3':
  482. result = result * 10 + 3;
  483. gotNum = true;
  484. break;
  485. case '4':
  486. result = result * 10 + 4;
  487. gotNum = true;
  488. break;
  489. case '5':
  490. result = result * 10 + 5;
  491. gotNum = true;
  492. break;
  493. case '6':
  494. result = result * 10 + 6;
  495. gotNum = true;
  496. break;
  497. case '7':
  498. result = result * 10 + 7;
  499. gotNum = true;
  500. break;
  501. case '8':
  502. result = result * 10 + 8;
  503. gotNum = true;
  504. break;
  505. case '9':
  506. result = result * 10 + 9;
  507. gotNum = true;
  508. break;
  509. default:
  510. if (gotNum)
  511. return result;
  512. else
  513. throw new ParseException("No Number found", index);
  514. }
  515. index++;
  516. }
  517. // check the result
  518. if (gotNum)
  519. return result;
  520. // else, throw a parse error
  521. throw new ParseException("No Number found", index);
  522. }
  523. /**
  524. * will look for one of "Jan/Feb/Mar/Apr/May/Jun/Jul/Aug/Sep/Oct/Nov/Dev"
  525. * and return the numerical version of the month. (0-11). a ParseException
  526. * error is thrown if a month cannot be found.
  527. */
  528. public int parseMonth() throws ParseException {
  529. char curr;
  530. try {
  531. switch(orig[index++]) {
  532. case 'J':
  533. case 'j': // "Jan" (0) / "Jun" (5) / "Jul" (6)
  534. // check next char
  535. switch(orig[index++]) {
  536. case 'A':
  537. case 'a':
  538. curr = orig[index++];
  539. if (curr == 'N' || curr == 'n') {
  540. return 0;
  541. }
  542. break;
  543. case 'U':
  544. case 'u':
  545. curr = orig[index++];
  546. if (curr == 'N' || curr == 'n') {
  547. return 5;
  548. } else if (curr == 'L' || curr == 'l') {
  549. return 6;
  550. }
  551. break;
  552. }
  553. break;
  554. case 'F':
  555. case 'f': // "Feb"
  556. curr = orig[index++];
  557. if (curr == 'E' || curr == 'e') {
  558. curr = orig[index++];
  559. if (curr == 'B' || curr == 'b') {
  560. return 1;
  561. }
  562. }
  563. break;
  564. case 'M':
  565. case 'm': // "Mar" (2) / "May" (4)
  566. curr = orig[index++];
  567. if (curr == 'A' || curr == 'a') {
  568. curr = orig[index++];
  569. if (curr == 'R' || curr == 'r') {
  570. return 2;
  571. } else if (curr == 'Y' || curr == 'y') {
  572. return 4;
  573. }
  574. }
  575. break;
  576. case 'A':
  577. case 'a': // "Apr" (3) / "Aug" (7)
  578. curr = orig[index++];
  579. if (curr == 'P' || curr == 'p') {
  580. curr = orig[index++];
  581. if (curr == 'R' || curr == 'r') {
  582. return 3;
  583. }
  584. } else if (curr == 'U' || curr == 'u') {
  585. curr = orig[index++];
  586. if (curr == 'G' || curr == 'g') {
  587. return 7;
  588. }
  589. }
  590. break;
  591. case 'S':
  592. case 's': // "Sep" (8)
  593. curr = orig[index++];
  594. if (curr == 'E' || curr == 'e') {
  595. curr = orig[index++];
  596. if (curr == 'P' || curr == 'p') {
  597. return 8;
  598. }
  599. }
  600. break;
  601. case 'O':
  602. case 'o': // "Oct"
  603. curr = orig[index++];
  604. if (curr == 'C' || curr == 'c') {
  605. curr = orig[index++];
  606. if (curr == 'T' || curr == 't') {
  607. return 9;
  608. }
  609. }
  610. break;
  611. case 'N':
  612. case 'n': // "Nov"
  613. curr = orig[index++];
  614. if (curr == 'O' || curr == 'o') {
  615. curr = orig[index++];
  616. if (curr == 'V' || curr == 'v') {
  617. return 10;
  618. }
  619. }
  620. break;
  621. case 'D':
  622. case 'd': // "Dec"
  623. curr = orig[index++];
  624. if (curr == 'E' || curr == 'e') {
  625. curr = orig[index++];
  626. if (curr == 'C' || curr == 'c') {
  627. return 11;
  628. }
  629. }
  630. break;
  631. }
  632. } catch (ArrayIndexOutOfBoundsException e) {
  633. }
  634. throw new ParseException("Bad Month", index);
  635. }
  636. /**
  637. * will parse the timezone - either Numerical version (e.g. +0800, -0500)
  638. * or the alpha version (e.g. PDT, PST). the result will be returned in
  639. * minutes needed to be added to the date to bring it to GMT.
  640. */
  641. public int parseTimeZone() throws ParseException {
  642. if (index >= orig.length)
  643. throw new ParseException("No more characters", index);
  644. char test = orig[index];
  645. if ( test == '+' || test == '-' ) {
  646. return parseNumericTimeZone();
  647. } else {
  648. return parseAlphaTimeZone();
  649. }
  650. }
  651. /**
  652. * will parse the Numerical time zone version (e.g. +0800, -0500)
  653. * the result will be returned in minutes needed to be added
  654. * to the date to bring it to GMT.
  655. */
  656. public int parseNumericTimeZone() throws ParseException {
  657. // we switch the sign if it is a '+'
  658. // since the time in the string we are
  659. // parsing is off from GMT by that amount.
  660. // and we want to get the time back into
  661. // GMT, so we substract it.
  662. boolean switchSign = false;
  663. char first = orig[index++];
  664. if (first == '+') {
  665. switchSign = true;
  666. } else if (first != '-') {
  667. throw new ParseException("Bad Numeric TimeZone", index);
  668. }
  669. int tz = parseNumber();
  670. int offset = (tz / 100) * 60 + (tz % 100);
  671. if (switchSign) {
  672. return -offset;
  673. } else {
  674. return offset;
  675. }
  676. }
  677. /**
  678. * will parse the alpha time zone version (e.g. PDT, PST).
  679. * the result will be returned in minutes needed to be added
  680. * to the date to bring it to GMT.
  681. */
  682. public int parseAlphaTimeZone() throws ParseException {
  683. int result = 0;
  684. boolean foundCommon = false;
  685. char curr;
  686. try {
  687. switch(orig[index++]) {
  688. case 'U':
  689. case 'u': // "UT" / Universal Time
  690. curr = orig[index++];
  691. if (curr == 'T' || curr == 't') {
  692. result = 0;
  693. break;
  694. }
  695. throw new ParseException("Bad Alpha TimeZone", index);
  696. case 'G':
  697. case 'g': // "GMT" ; Universal Time
  698. curr = orig[index++];
  699. if (curr == 'M' || curr == 'm') {
  700. curr = orig[index++];
  701. if (curr == 'T' || curr == 't') {
  702. result = 0;
  703. break;
  704. }
  705. }
  706. throw new ParseException("Bad Alpha TimeZone", index);
  707. case 'E':
  708. case 'e': // "EST" / "EDT" ; Eastern: - 5/ - 4
  709. result = 300;
  710. foundCommon = true;
  711. break;
  712. case 'C':
  713. case 'c': // "CST" / "CDT" ; Central: - 6/ - 5
  714. result = 360;
  715. foundCommon = true;
  716. break;
  717. case 'M':
  718. case 'm': // "MST" / "MDT" ; Mountain: - 7/ - 6
  719. result = 420;
  720. foundCommon = true;
  721. break;
  722. case 'P':
  723. case 'p': // "PST" / "PDT" ; Pacific: - 8/ - 7
  724. result = 480;
  725. foundCommon = true;
  726. break;
  727. default:
  728. throw new ParseException("Bad Alpha TimeZone", index);
  729. }
  730. } catch (ArrayIndexOutOfBoundsException e) {
  731. throw new ParseException("Bad Alpha TimeZone", index);
  732. }
  733. if (foundCommon) {
  734. curr = orig[index++];
  735. if (curr == 'S' || curr == 's') {
  736. curr = orig[index++];
  737. if (curr != 'T' && curr != 't') {
  738. throw new ParseException("Bad Alpha TimeZone", index);
  739. }
  740. } else if (curr == 'D' || curr == 'd') {
  741. curr = orig[index++];
  742. if (curr == 'T' || curr != 't') {
  743. // for daylight time
  744. result -= 60;
  745. } else {
  746. throw new ParseException("Bad Alpha TimeZone", index);
  747. }
  748. }
  749. }
  750. return result;
  751. }
  752. int getIndex() {
  753. return index;
  754. }
  755. }