Source for java.util.Date

   1: /* java.util.Date
   2:    Copyright (C) 1998, 1999, 2000, 2001, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package java.util;
  39: 
  40: import java.io.IOException;
  41: import java.io.ObjectInputStream;
  42: import java.io.ObjectOutputStream;
  43: import java.io.Serializable;
  44: import java.text.DateFormat;
  45: import java.text.SimpleDateFormat;
  46: 
  47: /**
  48:  * <p>
  49:  * This class represents a specific time in milliseconds since the epoch.
  50:  * The epoch is 1970, January 1 00:00:00.0000 UTC.  
  51:  * </p>
  52:  * <p>
  53:  * <code>Date</code> is intended to reflect universal time coordinate (UTC),
  54:  * but this depends on the underlying host environment.  Most operating systems 
  55:  * don't handle the leap second, which occurs about once every year or
  56:  * so.  The leap second is added to the last minute of the day on either
  57:  * the 30th of June or the 31st of December, creating a minute 61 seconds
  58:  * in length.
  59:  * </p>
  60:  * <p>
  61:  * The representations of the date fields are as follows:
  62:  * <ul>
  63:  * <li>
  64:  * Years are specified as the difference between the year
  65:  * and 1900.  Thus, the final year used is equal to
  66:  * 1900 + y, where y is the input value.
  67:  * </li>
  68:  * <li>
  69:  * Months are represented using zero-based indexing,
  70:  * making 0 January and 11 December.
  71:  * </li>
  72:  * <li>
  73:  * Dates are represented with the usual values of
  74:  * 1 through to 31.
  75:  * </li>
  76:  * <li>
  77:  * Hours are represented in the twenty-four hour clock,
  78:  * with integer values from 0 to 23.  12am is 0, and
  79:  * 12pm is 12.
  80:  * </li>
  81:  * <li>
  82:  * Minutes are again as usual, with values from 0 to 59.
  83:  * </li>
  84:  * <li>
  85:  * Seconds are represented with the values 0 through to 61,
  86:  * with 60 and 61 being leap seconds (as per the ISO C standard).
  87:  * </li>
  88:  * </ul>
  89:  * </p>
  90:  * <p>
  91:  * Prior to JDK 1.1, this class was the sole class handling date and time
  92:  * related functionality.  However, this particular solution was not
  93:  * amenable to internationalization.  The new <code>Calendar</code>
  94:  * class should now be used to handle dates and times, with <code>Date</code>
  95:  * being used only for values in milliseconds since the epoch.  The
  96:  * <code>Calendar</code> class, and its concrete implementations, handle
  97:  * the interpretation of these values into minutes, hours, days, months
  98:  * and years.  The formatting and parsing of dates is left to the
  99:  * <code>DateFormat</code> class, which is able to handle the different
 100:  * types of date format which occur in different locales.
 101:  * </p>
 102:  *
 103:  * @see Calendar
 104:  * @see GregorianCalendar
 105:  * @see java.text.DateFormat
 106:  * @author Jochen Hoenicke
 107:  * @author Per Bothner (bothner@cygnus.com)
 108:  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
 109:  */
 110: public class Date
 111:     implements Cloneable, Comparable<Date>, Serializable
 112: {
 113:   /**
 114:    * This is the serialization UID for this class
 115:    * for compatability with Sun's JDK.
 116:    */
 117:   private static final long serialVersionUID = 7523967970034938905L;
 118: 
 119:   /**
 120:    * The time in milliseconds since the epoch.
 121:    */
 122:   private transient long time;
 123: 
 124:   /**
 125:    * An array of week names used to map names to integer values.
 126:    */
 127:   private static final String[] weekNames = { "Sun", "Mon", "Tue", "Wed",
 128:                           "Thu", "Fri", "Sat" };
 129:   /**
 130:    * An array of month names used to map names to integer values.
 131:    */
 132:   private static final String[] monthNames = { "Jan", "Feb", "Mar", "Apr",
 133:                            "May", "Jun", "Jul", "Aug",
 134:                            "Sep", "Oct", "Nov", "Dec" };
 135:   /**
 136:    * Creates a new Date Object representing the current time.
 137:    */
 138:   public Date()
 139:   {
 140:     time = System.currentTimeMillis();
 141:   }
 142: 
 143:   /**
 144:    * Creates a new Date Object representing the given time.
 145:    *
 146:    * @param time the time in milliseconds since the epoch.
 147:    */
 148:   public Date(long time)
 149:   {
 150:     this.time = time;
 151:   }
 152: 
 153:   /**
 154:    * Creates a new Date Object representing the given time.
 155:    *
 156:    * @deprecated use <code>new GregorianCalendar(year+1900, month,
 157:    * day)</code> instead.
 158:    * @param year the difference between the required year and 1900.
 159:    * @param month the month as a value between 0 and 11.
 160:    * @param day the day as a value between 0 and 31.
 161:    */
 162:   public Date(int year, int month, int day)
 163:   {
 164:     this(year, month, day, 0, 0, 0);
 165:   }
 166: 
 167:   /**
 168:    * Creates a new Date Object representing the given time.
 169:    *
 170:    * @deprecated use <code>new GregorianCalendar(year+1900, month,
 171:    * day, hour, min)</code> instead.
 172:    * @param year the difference between the required year and 1900.
 173:    * @param month the month as a value between 0 and 11.
 174:    * @param day the day as a value between 0 and 31.
 175:    * @param hour the hour as a value between 0 and 23, in 24-hour
 176:    *        clock notation.
 177:    * @param min the minute as a value between 0 and 59.
 178:    */
 179:   public Date(int year, int month, int day, int hour, int min)
 180:   {
 181:     this(year, month, day, hour, min, 0);
 182:   }
 183: 
 184:   /**
 185:    * Creates a new Date Object representing the given time.
 186:    *
 187:    * @deprecated use <code>new GregorianCalendar(year+1900, month,
 188:    * day, hour, min, sec)</code> instead. 
 189:    * @param year the difference between the required year and 1900.
 190:    * @param month the month as a value between 0 and 11.
 191:    * @param day the day as a value between 0 and 31.
 192:    * @param hour the hour as a value between 0 and 23, in 24-hour
 193:    *        clock notation.
 194:    * @param min the minute as a value between 0 and 59.
 195:    * @param sec the second as a value between 0 and 61 (with 60
 196:    *        and 61 being leap seconds).
 197:    */
 198:   public Date(int year, int month, int day, int hour, int min, int sec)
 199:   {
 200:     GregorianCalendar cal =
 201:     new GregorianCalendar(year + 1900, month, day, hour, min, sec);
 202:     time = cal.getTimeInMillis();
 203:   }
 204: 
 205:   /**
 206:    * Creates a new Date from the given string representation.  This
 207:    * does the same as <code>new Date(Date.parse(s))</code>
 208:    * @see #parse
 209:    * @deprecated use <code>java.text.DateFormat.parse(s)</code> instead.  
 210:    */
 211:   public Date(String s)
 212:   {
 213:     time = parse(s);
 214:   }
 215: 
 216:   /**
 217:    * Returns a copy of this <code>Date</code> object.
 218:    *
 219:    * @return a copy, or null if the object couldn't be
 220:    *         cloned.
 221:    * @see Object#clone()
 222:    */
 223:   public Object clone()
 224:   {
 225:     try
 226:       {
 227:     return super.clone();
 228:       }
 229:     catch (CloneNotSupportedException ex)
 230:       {
 231:     return null;
 232:       }
 233:   }
 234: 
 235:   /**
 236:    * Returns the number of milliseconds since the epoch
 237:    * specified by the given arguments.  The arguments are
 238:    * interpreted relative to UTC rather than the local
 239:    * time zone.
 240:    *
 241:    * @deprecated Use <code>Calendar</code> with a UTC
 242:    *             <code>TimeZone</code> instead.
 243:    * @param year the difference between the required year and 1900.
 244:    * @param month the month as a value between 0 and 11.
 245:    * @param date the day as a value between 0 and 31.
 246:    * @param hrs the hour as a value between 0 and 23, in 24-hour
 247:    *        clock notation.
 248:    * @param min the minute as a value between 0 and 59.
 249:    * @param sec the second as a value between 0 and 61 (with 60
 250:    *        and 61 being leap seconds).
 251:    * @return the time in milliseconds since the epoch.
 252:    */
 253:   public static long UTC(int year, int month, int date,
 254:              int hrs, int min, int sec)
 255:   {
 256:     GregorianCalendar cal =
 257:       new GregorianCalendar(year + 1900, month, date, hrs, min, sec);
 258:     cal.set(Calendar.ZONE_OFFSET, 0);
 259:     cal.set(Calendar.DST_OFFSET, 0);
 260:     return cal.getTimeInMillis();
 261:   }
 262: 
 263:   /**
 264:    * Gets the time represented by this object.
 265:    *
 266:    * @return the time in milliseconds since the epoch.
 267:    */
 268:   public long getTime()
 269:   {
 270:     return time;
 271:   }
 272: 
 273:   /**
 274:    * Returns the number of minutes offset used with UTC to give the time
 275:    * represented by this object in the current time zone.  The date information
 276:    * from this object is also used to determine whether or not daylight savings
 277:    * time is in effect.  For example, the offset for the UK would be 0 if the
 278:    * month of the date object was January, and 1 if the month was August.
 279:    * 
 280:    * @deprecated use
 281:    * <code>Calendar.get(Calendar.ZONE_OFFSET)+Calendar.get(Calendar.DST_OFFSET)</code>
 282:    * instead.
 283:    * @return The time zone offset in minutes of the local time zone
 284:    * relative to UTC.  The time represented by this object is used to
 285:    * determine if we should use daylight savings.
 286:    */
 287:   public int getTimezoneOffset()
 288:   {
 289:     Calendar cal = Calendar.getInstance();
 290:     cal.setTimeInMillis(time);
 291:     return - (cal.get(Calendar.ZONE_OFFSET)
 292:         + cal.get(Calendar.DST_OFFSET)) / (60 * 1000);
 293:   }
 294: 
 295:   /**
 296:    * Sets the time which this object should represent.
 297:    *
 298:    * @param time the time in milliseconds since the epoch.  
 299:    */
 300:   public void setTime(long time)
 301:   {
 302:     this.time = time;
 303:   }
 304: 
 305:   /**
 306:    * Tests if this date is after the specified date.
 307:    *
 308:    * @param when the other date
 309:    * @return true, if the date represented by this object is
 310:    * strictly later than the time represented by when.  
 311:    */
 312:   public boolean after(Date when)
 313:   {
 314:     return time > when.time;
 315:   }
 316: 
 317:   /**
 318:    * Tests if this date is before the specified date.
 319:    *
 320:    * @param when the other date
 321:    * @return true, if the date represented by when is strictly later
 322:    * than the time represented by this object.
 323:    */
 324:   public boolean before(Date when)
 325:   {
 326:     return time < when.time;
 327:   }
 328: 
 329:   /**
 330:    * Compares two dates for equality.
 331:    *
 332:    * @param obj the object to compare.
 333:    * @return true, if obj is a Date object and the time represented
 334:    * by obj is exactly the same as the time represented by this
 335:    * object.  
 336:    */
 337:   public boolean equals(Object obj)
 338:   {
 339:     return (obj instanceof Date && time == ((Date) obj).time);
 340:   }
 341: 
 342:   /**
 343:    * Compares two dates.
 344:    *
 345:    * @param when the other date.
 346:    * @return 0, if the date represented
 347:    * by obj is exactly the same as the time represented by this
 348:    * object, a negative if this Date is before the other Date, and
 349:    * a positive value otherwise.  
 350:    */
 351:   public int compareTo(Date when)
 352:   {
 353:     return (time < when.time) ? -1 : (time == when.time) ? 0 : 1;
 354:   }
 355: 
 356:   /**
 357:    * Computes the hash code of this <code>Date</code> as the
 358:    * XOR of the most significant and the least significant
 359:    * 32 bits of the 64 bit milliseconds value.
 360:    *
 361:    * @return the hash code.
 362:    */
 363:   public int hashCode()
 364:   {
 365:     return (int) time ^ (int) (time >>> 32);
 366:   }
 367: 
 368:   /**
 369:    * <p>
 370:    * Returns a string representation of this date using
 371:    * the following date format:
 372:    * </p>
 373:    * <p>
 374:    * <code>day mon dd hh:mm:ss zz yyyy</code>
 375:    * </p>
 376:    * <p>where the fields used here are:
 377:    * <ul>
 378:    * <li>
 379:    * <code>day</code> -- the day of the week
 380:    * (Sunday through to Saturday).
 381:    * </li>
 382:    * <li>
 383:    * <code>mon</code> -- the month (Jan to Dec).
 384:    * </li>
 385:    * <li>
 386:    * <code>dd</code> -- the day of the month
 387:    * as two decimal digits (01 to 31).
 388:    * </li>
 389:    * <li>
 390:    * <code>hh</code> -- the hour of the day
 391:    * as two decimal digits in 24-hour clock notation
 392:    * (01 to 23).
 393:    * </li>
 394:    * <li>
 395:    * <code>mm</code> -- the minute of the day
 396:    * as two decimal digits (01 to 59).
 397:    * </li>
 398:    * <li>
 399:    * <code>ss</code> -- the second of the day
 400:    * as two decimal digits (01 to 61).
 401:    * </li>
 402:    * <li>
 403:    * <code>zz</code> -- the time zone information if available.
 404:    * The possible time zones used include the abbreviations
 405:    * recognised by <code>parse()</code> (e.g. GMT, CET, etc.)
 406:    * and may reflect the fact that daylight savings time is in
 407:    * effect.  The empty string is used if there is no time zone
 408:    * information.
 409:    * </li>
 410:    * <li>
 411:    * <code>yyyy</code> -- the year as four decimal digits.
 412:    * </li>
 413:    * </ul>
 414:    * <p>
 415:    * The <code>DateFormat</code> class should now be 
 416:    * preferred over using this method.
 417:    * </p>
 418:    *
 419:    * @return A string of the form 'day mon dd hh:mm:ss zz yyyy'
 420:    * @see #parse(String)
 421:    * @see DateFormat
 422:    */
 423:   public String toString()
 424:   {
 425:     Calendar cal = Calendar.getInstance();
 426:     cal.setTimeInMillis(time);
 427:     String day = "0" + cal.get(Calendar.DATE);
 428:     String hour = "0" + cal.get(Calendar.HOUR_OF_DAY);
 429:     String min = "0" + cal.get(Calendar.MINUTE);
 430:     String sec = "0" + cal.get(Calendar.SECOND);
 431:     String year = "000" + cal.get(Calendar.YEAR);
 432:     return weekNames[cal.get(Calendar.DAY_OF_WEEK) - 1] + " "
 433:       + monthNames[cal.get(Calendar.MONTH)] + " "
 434:       + day.substring(day.length() - 2) + " "
 435:       + hour.substring(hour.length() - 2) + ":"
 436:       + min.substring(min.length() - 2) + ":"
 437:       + sec.substring(sec.length() - 2) + " "
 438:       +
 439:       cal.getTimeZone().getDisplayName(cal.getTimeZone().inDaylightTime(this),
 440:                        TimeZone.SHORT) + " " +
 441:       year.substring(year.length() - 4);
 442:   }
 443: 
 444:   /** 
 445:    * Returns a locale-dependent string representation of this
 446:    * <code>Date</code> object.
 447:    *
 448:    * @deprecated Use DateFormat.format(Date)
 449:    * @return A locale-dependent string representation.
 450:    * @see #parse(String)
 451:    * @see DateFormat
 452:    */
 453:   public String toLocaleString()
 454:   {
 455:     return java.text.DateFormat.getInstance().format(this);
 456:   }
 457: 
 458:   /** 
 459:    * <p>
 460:    * Returns a string representation of this <code>Date</code>
 461:    * object using GMT rather than the local timezone.
 462:    * The following date format is used:
 463:    * </p>
 464:    * <p>
 465:    * <code>d mon yyyy hh:mm:ss GMT</code>
 466:    * </p>
 467:    * <p>where the fields used here are:
 468:    * <ul>
 469:    * <li>
 470:    * <code>d</code> -- the day of the month
 471:    * as one or two decimal digits (1 to 31).
 472:    * </li>
 473:    * <li>
 474:    * <code>mon</code> -- the month (Jan to Dec).
 475:    * </li>
 476:    * <li>
 477:    * <code>yyyy</code> -- the year as four decimal digits.
 478:    * </li>
 479:    * <li>
 480:    * <code>hh</code> -- the hour of the day
 481:    * as two decimal digits in 24-hour clock notation
 482:    * (01 to 23).
 483:    * </li>
 484:    * <li>
 485:    * <code>mm</code> -- the minute of the day
 486:    * as two decimal digits (01 to 59).
 487:    * </li>
 488:    * <li>
 489:    * <code>ss</code> -- the second of the day
 490:    * as two decimal digits (01 to 61).
 491:    * </li>
 492:    * <li>
 493:    * <code>GMT</code> -- the literal string "GMT"
 494:    * indicating Greenwich Mean Time as opposed to
 495:    * the local timezone.
 496:    * </li>
 497:    * </ul>
 498:    * 
 499:    * @deprecated Use DateFormat.format(Date) with a GMT TimeZone.
 500:    * @return A string of the form 'd mon yyyy hh:mm:ss GMT' using
 501:    *         GMT as opposed to the local timezone.
 502:    * @see #parse(String)
 503:    * @see DateFormat
 504:    */
 505:   public String toGMTString()
 506:   {
 507:     java.text.DateFormat format = java.text.DateFormat.getInstance();
 508:     format.setTimeZone(TimeZone.getTimeZone("GMT"));
 509:     return format.format(this);
 510:   }
 511: 
 512:   /**
 513:    * Parses the time zone string.
 514:    *
 515:    * @param tok The token containing the time zone.
 516:    * @param sign The sign (+ or -) used by the time zone.
 517:    * @return An integer representing the number of minutes offset
 518:    *         from GMT for the time zone.
 519:    */
 520:   private static int parseTz(String tok, char sign)
 521:     throws IllegalArgumentException
 522:   {
 523:     int num;
 524: 
 525:     try
 526:       {
 527:     // parseInt doesn't handle '+' so strip off sign.
 528:     num = Integer.parseInt(tok.substring(1));
 529:       }
 530:     catch (NumberFormatException ex)
 531:       {
 532:     throw new IllegalArgumentException(tok);
 533:       }
 534: 
 535:     // Convert hours to minutes.
 536:     if (num < 24)
 537:       num *= 60;
 538:     else
 539:       num = (num / 100) * 60 + num % 100;
 540: 
 541:     return sign == '-' ? -num : num;
 542:   }
 543: 
 544:   /**
 545:    * Parses the month string.
 546:    *
 547:    * @param tok the token containing the month.
 548:    * @return An integer between 0 and 11, representing
 549:    *         a month from January (0) to December (11),
 550:    *         or -1 if parsing failed.
 551:    */
 552:   private static int parseMonth(String tok)
 553:   {
 554:     // Initialize strings for month names.
 555:     // We could possibly use the fields of DateFormatSymbols but that is
 556:     // localized and thus might not match the English words specified.
 557:     String months[] = { "JANUARY", "FEBRUARY", "MARCH", "APRIL", "MAY",
 558:             "JUNE", "JULY", "AUGUST", "SEPTEMBER", "OCTOBER",
 559:             "NOVEMBER", "DECEMBER" };
 560: 
 561:     int i;
 562:     for (i = 0; i < 12; i++)
 563:       if (months[i].startsWith(tok))
 564:         return i;
 565: 
 566:     // Return -1 if not found.
 567:     return -1;
 568:   }
 569: 
 570:   /**
 571:    * Parses the day of the week string.
 572:    *
 573:    * @param tok the token containing the day of the week.
 574:    * @return true if the token was parsed successfully.
 575:    */
 576:   private static boolean parseDayOfWeek(String tok)
 577:   {
 578:     // Initialize strings for days of the week names.
 579:     // We could possibly use the fields of DateFormatSymbols but that is
 580:     // localized and thus might not match the English words specified.
 581:     String daysOfWeek[] = { "SUNDAY", "MONDAY", "TUESDAY", "WEDNESDAY",
 582:                 "THURSDAY", "FRIDAY", "SATURDAY" };
 583: 
 584:     int i;
 585:     for (i = 0; i < 7; i++)
 586:       if (daysOfWeek[i].startsWith(tok))
 587:         return true;
 588: 
 589:     return false;
 590:   }
 591: 
 592:   /** 
 593:    * <p>
 594:    * Parses a String and returns the time, in milliseconds since the
 595:    * epoch, it represents.  Most syntaxes are handled, including
 596:    * the IETF date standard "day, dd mon yyyy hh:mm:ss zz" (see
 597:    * <code>toString()</code> for definitions of these fields).
 598:    * Standard U.S. time zone abbreviations are recognised, in
 599:    * addition to time zone offsets in positive or negative minutes.
 600:    * If a time zone is specified, the specified time is assumed to
 601:    * be in UTC and the appropriate conversion is applied, following
 602:    * parsing, to convert this to the local time zone.  If no zone
 603:    * is specified, the time is assumed to already be in the local
 604:    * time zone.
 605:    * </p>
 606:    * <p>
 607:    * The method parses the string progressively from left to right.
 608:    * At the end of the parsing process, either a time is returned
 609:    * or an <code>IllegalArgumentException</code> is thrown to signify
 610:    * failure.  The ASCII characters A-Z, a-z, 0-9, and ',', '+', '-',
 611:    * ':' and '/' are the only characters permitted within the string,
 612:    * besides whitespace and characters enclosed within parantheses
 613:    * '(' and ')'.  
 614:    * </p>
 615:    * <p>
 616:    * A sequence of consecutive digits are recognised as a number,
 617:    * and interpreted as follows:
 618:    * <ul>
 619:    * <li>
 620:    * A number preceded by a sign (+ or -) is taken to be a time zone
 621:    * offset.  The time zone offset can be specified in either hours
 622:    * or minutes.  The former is assumed if the number is less than 24.
 623:    * Otherwise, the offset is assumed to be in minutes.  A - indicates
 624:    * a time zone west of GMT, while a + represents a time zone to the
 625:    * east of GMT.  The time zones are always assumed to be relative
 626:    * to GMT, and a (redundant) specification of this can be included
 627:    * with the time zone.  For example, '-9', 'utc-9' and 'GMT-9' all
 628:    * represent a time zone nine hours west of GMT.  Similarly,
 629:    * '+4', 'ut+4' and 'UTC+4' all give 4 hours east of GMT.
 630:    * </li>
 631:    * <li>
 632:    * A number equal to or greater than 70 is regarded as a year specification.
 633:    * Values lower than 70 are only assumed to indicate a year if both the
 634:    * day of the month and the month itself have already been recognised.
 635:    * Year values less than 100 are interpreted as being relative to the current
 636:    * century when the <code>Date</code> class is initialised..  Given a century,
 637:    * x, the year is assumed to be within the range x - 80 to x + 19.  The value
 638:    * itself is then used as a match against the two last digits of one of these
 639:    * years.  For example, take x to be 2004.  A two-digit year is assumed to fall
 640:    * within the range x - 80 (1924) and x + 19 (2023).  Thus, any intepreted value
 641:    * between 0 and 23 is assumed to be 2000 to 2023 and values between 24 and 99
 642:    * are taken as being 1924 to 1999.  This only applies for the case of 2004.
 643:    * With a different year, the values will be interpreted differently. 2005
 644:    * will used 0 to 24 as 2000 to 2024 and 25 to 99 as 1925 to 1999, for example.
 645:    * This behaviour differs from that of <code>SimpleDateFormat</code> and is
 646:    * time-dependent (a two-digit year will be interpreted differently depending
 647:    * on the time the code is run).
 648:    * </li>
 649:    * <li>
 650:    * Numbers followed by a colon are interpreted by first an hour, and then
 651:    * as a minute, once an hour has been found.
 652:    * </li>
 653:    * <li>
 654:    * <li>
 655:    * Numbers followed by a slash are regarded first as a month, and then as
 656:    * a day of the month once the month has been found.  This follows the
 657:    * U.S. date format of mm/dd, rather than the European dd/mm.  Months
 658:    * are converted to the recognised value - 1 before storage, in order
 659:    * to put the number within the range 0 to 11.
 660:    * </li>
 661:    * <li>
 662:    * Numbers followed by commas, whitespace, hyphens or the end of the string
 663:    * are interpreted in the following order: hour, minute, second, day of month.
 664:    * The first type not already recognised in the current string being parsed is
 665:    * assumed.
 666:    * </li>
 667:    * </ul>
 668:    * </p>
 669:    * <p>
 670:    * A sequence of consecutive alphabetic characters is recognised as a word,
 671:    * and interpreted as follows, in a case-insentive fashion:
 672:    * <ul>
 673:    * <li>
 674:    * The characters 'AM' or 'PM' restrict the hour value to a value between 0
 675:    * and 12.  In the latter case, 12 is added to the hour value before storage.
 676:    * </li>
 677:    * <li>
 678:    * Any words which match any prefix of one of the days of the week ('Monday',
 679:    * 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' and 'Sunday'),
 680:    * are simply ignored.
 681:    * </li>
 682:    * <li>
 683:    * Any words which match any prefix of one of the months of the year ('January',
 684:    * 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September',
 685:    * 'October', 'November', 'December') are recognised and interpreted as the
 686:    * appropriate value between 0 and 11.  The first match made against a
 687:    * month is the one used, in the order specified here.  For example, 'Ma' is
 688:    * intepreted as 'March' (2) and not as 'May' (4).  Similarly, 'Ju' is 'June',
 689:    * and not 'July'.
 690:    * </li>
 691:    * <li>
 692:    * The words 'GMT', 'UT' and 'UTC' are interpreted as specifying UTC as the
 693:    * time zone in use for this date.
 694:    * </li>
 695:    * <li>
 696:    * The word pairs 'EST'/'EDT', 'CST'/'CDT', 'MST'/'MDT' and 'PST'/'PDT' are
 697:    * interpreted as the appropriate U.S. time zone abbreviation.  Each pair
 698:    * is the standard and daylight savings time zone specification, respectively,
 699:    * for each zone within the U.S, these being Eastern Standard/Daylight Time
 700:    * (-5), Central Standard/Daylight Time (-6), Mountain Standard/Daylight Time
 701:    * (-7) and Pacific Standard/Daylight Time (-8).
 702:    * </li>
 703:    * </ul>
 704:    *
 705:    * @param string The String to parse.
 706:    * @return The time in milliseconds since the epoch.
 707:    * @throws IllegalArgumentException if the string fails to parse.
 708:    * @deprecated Use DateFormat.parse(String)
 709:    * @see #toString()
 710:    * @see SimpleDateFormat
 711:    */
 712:   public static long parse(String string)
 713:   {
 714:     // Initialize date/time fields before parsing begins.
 715:     int year = -1;
 716:     int month = -1;
 717:     int day = -1;
 718:     int hour = -1;
 719:     int minute = -1;
 720:     int second = -1;
 721:     int timezone = 0;
 722:     boolean localTimezone = true;
 723: 
 724:     // Trim out any nested stuff in parentheses now to make parsing easier.
 725:     StringBuffer buf = new StringBuffer();
 726:     int parenNesting = 0;
 727:     int len = string.length();
 728:     for (int i = 0;  i < len;  i++)
 729:       {
 730:     char ch = string.charAt(i);
 731:     if (ch >= 'a' && ch <= 'z')
 732:       ch -= 'a' - 'A';
 733:     if (ch == '(')
 734:       parenNesting++;
 735:     else if (parenNesting == 0)
 736:       buf.append(ch);
 737:     else if (ch == ')')
 738:       parenNesting--;
 739:       }
 740:     int tmpMonth;
 741: 
 742:     // Make all chars upper case to simplify comparisons later.
 743:     // Also ignore commas; treat them as delimiters.
 744:     StringTokenizer strtok = new StringTokenizer(buf.toString(), " \t\n\r,");
 745: 
 746:     while (strtok.hasMoreTokens())
 747:       {
 748:     String tok = strtok.nextToken();
 749:     char firstch = tok.charAt(0);
 750:     if ((firstch == '+' || firstch == '-') && year >= 0)
 751:       {
 752:         timezone = parseTz(tok, firstch);
 753:         localTimezone = false;
 754:       }
 755:     else if (firstch >= '0' && firstch <= '9')
 756:       {
 757:         int lastPunct = -1;
 758:         while (tok != null && tok.length() > 0)
 759:           {
 760:         int punctOffset = tok.length();
 761:         int num = 0;
 762:         int punct;
 763:         for (int i = 0;  ;  i++)
 764:           {
 765:             if (i >= punctOffset)
 766:               {
 767:             punct = -1;
 768:             break;
 769:               }
 770:             else
 771:               {
 772:             punct = tok.charAt(i);
 773:             if (punct >= '0' && punct <= '9')
 774:               {
 775:                 if (num > 999999999) // in case of overflow
 776:                   throw new IllegalArgumentException(tok);
 777:                 num = 10 * num + (punct - '0');
 778:               }
 779:             else
 780:               {
 781:                 punctOffset = i;
 782:                 break;
 783:               }
 784:               }
 785:               
 786:           }
 787: 
 788:         if (punct == ':')
 789:           {
 790:             if (hour < 0)
 791:               hour = num;
 792:             else
 793:               minute = num;
 794:           }
 795:         else if (lastPunct == ':' && hour >= 0 && (minute < 0 || second < 0))
 796:           {
 797:             if (minute < 0)
 798:               minute = num;
 799:             else
 800:               second = num;
 801:           }
 802:             else if ((num >= 70
 803:               && (punct == ' ' || punct == ','
 804:                   || punct == '/' || punct < 0))
 805:              || (num < 70 && day >= 0 && month >= 0 && year < 0))
 806:           {
 807:             if (num >= 100)
 808:               year = num;
 809:             else
 810:               {
 811:             int curYear = 1900 + new Date().getYear();
 812:             int firstYear = curYear - 80;
 813:             year = firstYear / 100 * 100 + num;
 814:             if (year < firstYear)
 815:               year += 100;
 816:               }
 817:           }
 818:         else if (punct == '/')
 819:           {
 820:             if (month < 0)
 821:               month = num - 1;
 822:             else
 823:               day = num;
 824:           }
 825:         else if (hour >= 0 && minute < 0)
 826:           minute = num;
 827:         else if (minute >= 0 && second < 0)
 828:           second = num;
 829:         else if (day < 0)
 830:           day = num;
 831:         else
 832:           throw new IllegalArgumentException(tok);
 833: 
 834:         // Advance string if there's more to process in this token.
 835:         if (punct < 0 || punctOffset + 1 >= tok.length())
 836:           tok = null;
 837:         else
 838:           tok = tok.substring(punctOffset + 1);
 839:         lastPunct = punct;
 840:           }
 841:       }
 842:     else if (firstch >= 'A' && firstch <= 'Z')
 843:       {
 844:         if (tok.equals("AM"))
 845:           {
 846:         if (hour < 1 || hour > 12)
 847:           throw new IllegalArgumentException(tok);
 848:         if (hour == 12)
 849:           hour = 0;
 850:           }
 851:         else if (tok.equals("PM"))
 852:           {
 853:         if (hour < 1 || hour > 12)
 854:           throw new IllegalArgumentException(tok);
 855:         if (hour < 12)
 856:           hour += 12;
 857:           }
 858:         else if (parseDayOfWeek(tok))
 859:           { /* Ignore it; throw the token away. */ }
 860:         else if (tok.equals("UT") || tok.equals("UTC") || tok.equals("GMT"))
 861:           localTimezone = false;
 862:         else if (tok.startsWith("UT") || tok.startsWith("GMT"))
 863:           {
 864:         int signOffset = 3;
 865:         if (tok.charAt(1) == 'T' && tok.charAt(2) != 'C')
 866:           signOffset = 2;
 867: 
 868:             char sign = tok.charAt(signOffset);
 869:         if (sign != '+' && sign != '-')
 870:           throw new IllegalArgumentException(tok);
 871: 
 872:             timezone = parseTz(tok.substring(signOffset), sign);
 873:             localTimezone = false;
 874:           }
 875:         else if ((tmpMonth = parseMonth(tok)) >= 0)
 876:           month = tmpMonth;
 877:         else if (tok.length() == 3 && tok.charAt(2) == 'T')
 878:           {
 879:         // Convert timezone offset from hours to minutes.
 880:         char ch = tok.charAt(0);
 881:         if (ch == 'E')
 882:           timezone = -5 * 60;
 883:         else if (ch == 'C')
 884:           timezone = -6 * 60;
 885:         else if (ch == 'M')
 886:           timezone = -7 * 60;
 887:         else if (ch == 'P')
 888:           timezone = -8 * 60;
 889:         else
 890:           throw new IllegalArgumentException(tok);
 891: 
 892:         // Shift 60 minutes for Daylight Savings Time.