Source Code Cross Referenced for SimpleDateFormat.java in  » 6.0-JDK-Modules » j2me » java » text » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » j2me » java.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * 
0003:         * @(#)SimpleDateFormat.java	1.59 06/10/10
0004:         * 
0005:         * Portions Copyright  2000-2006 Sun Microsystems, Inc. All Rights
0006:         * Reserved.  Use is subject to license terms.
0007:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0008:         * 
0009:         * This program is free software; you can redistribute it and/or
0010:         * modify it under the terms of the GNU General Public License version
0011:         * 2 only, as published by the Free Software Foundation.
0012:         * 
0013:         * This program is distributed in the hope that it will be useful, but
0014:         * WITHOUT ANY WARRANTY; without even the implied warranty of
0015:         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0016:         * General Public License version 2 for more details (a copy is
0017:         * included at /legal/license.txt).
0018:         * 
0019:         * You should have received a copy of the GNU General Public License
0020:         * version 2 along with this work; if not, write to the Free Software
0021:         * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0022:         * 02110-1301 USA
0023:         * 
0024:         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0025:         * Clara, CA 95054 or visit www.sun.com if you need additional
0026:         * information or have any questions.
0027:         */
0028:
0029:        /*
0030:         * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
0031:         * (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved
0032:         *
0033:         *   The original version of this source code and documentation is copyrighted
0034:         * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
0035:         * materials are provided under terms of a License Agreement between Taligent
0036:         * and Sun. This technology is protected by multiple US and International
0037:         * patents. This notice and attribution to Taligent may not be removed.
0038:         *   Taligent is a registered trademark of Taligent, Inc.
0039:         *
0040:         */
0041:
0042:        package java.text;
0043:
0044:        import java.util.TimeZone;
0045:        import java.util.Calendar;
0046:        import java.util.Date;
0047:        import java.util.Locale;
0048:        import java.util.ResourceBundle;
0049:        import java.util.SimpleTimeZone;
0050:        import java.util.GregorianCalendar;
0051:        import java.io.ObjectInputStream;
0052:        import java.io.InvalidObjectException;
0053:        import java.io.IOException;
0054:        import java.lang.ClassNotFoundException;
0055:        import java.util.Hashtable;
0056:        import java.lang.StringIndexOutOfBoundsException;
0057:        import sun.text.resources.LocaleData;
0058:
0059:        /**
0060:         * <code>SimpleDateFormat</code> is a concrete class for formatting and
0061:         * parsing dates in a locale-sensitive manner. It allows for formatting
0062:         * (date -> text), parsing (text -> date), and normalization.
0063:         *
0064:         * <p>
0065:         * <code>SimpleDateFormat</code> allows you to start by choosing
0066:         * any user-defined patterns for date-time formatting. However, you
0067:         * are encouraged to create a date-time formatter with either
0068:         * <code>getTimeInstance</code>, <code>getDateInstance</code>, or
0069:         * <code>getDateTimeInstance</code> in <code>DateFormat</code>. Each
0070:         * of these class methods can return a date/time formatter initialized
0071:         * with a default format pattern. You may modify the format pattern
0072:         * using the <code>applyPattern</code> methods as desired.
0073:         * For more information on using these methods, see
0074:         * {@link DateFormat}.
0075:         *
0076:         * <h4>Date and Time Patterns</h4>
0077:         * <p>
0078:         * Date and time formats are specified by <em>date and time pattern</em>
0079:         * strings.
0080:         * Within date and time pattern strings, unquoted letters from
0081:         * <code>'A'</code> to <code>'Z'</code> and from <code>'a'</code> to
0082:         * <code>'z'</code> are interpreted as pattern letters representing the
0083:         * components of a date or time string.
0084:         * Text can be quoted using single quotes (<code>'</code>) to avoid
0085:         * interpretation.
0086:         * <code>"''"</code> represents a single quote.
0087:         * All other characters are not interpreted; they're simply copied into the
0088:         * output string during formatting or matched against the input string
0089:         * during parsing.
0090:         * <p>
0091:         * The following pattern letters are defined (all other characters from
0092:         * <code>'A'</code> to <code>'Z'</code> and from <code>'a'</code> to
0093:         * <code>'z'</code> are reserved):
0094:         * <blockquote>
0095:         * <table border=0 cellspacing=3 cellpadding=0 summary="Chart shows pattern letters, date/time component, presentation, and examples.">
0096:         *     <tr bgcolor="#ccccff">
0097:         *         <th align=left>Letter
0098:         *         <th align=left>Date or Time Component
0099:         *         <th align=left>Presentation
0100:         *         <th align=left>Examples
0101:         *     <tr>
0102:         *         <td><code>G</code>
0103:         *         <td>Era designator
0104:         *         <td><a href="#text">Text</a>
0105:         *         <td><code>AD</code>
0106:         *     <tr bgcolor="#eeeeff">
0107:         *         <td><code>y</code>
0108:         *         <td>Year
0109:         *         <td><a href="#year">Year</a>
0110:         *         <td><code>1996</code>; <code>96</code>
0111:         *     <tr>
0112:         *         <td><code>M</code>
0113:         *         <td>Month in year
0114:         *         <td><a href="#month">Month</a>
0115:         *         <td><code>July</code>; <code>Jul</code>; <code>07</code>
0116:         *     <tr bgcolor="#eeeeff">
0117:         *         <td><code>w</code>
0118:         *         <td>Week in year
0119:         *         <td><a href="#number">Number</a>
0120:         *         <td><code>27</code>
0121:         *     <tr>
0122:         *         <td><code>W</code>
0123:         *         <td>Week in month
0124:         *         <td><a href="#number">Number</a>
0125:         *         <td><code>2</code>
0126:         *     <tr bgcolor="#eeeeff">
0127:         *         <td><code>D</code>
0128:         *         <td>Day in year
0129:         *         <td><a href="#number">Number</a>
0130:         *         <td><code>189</code>
0131:         *     <tr>
0132:         *         <td><code>d</code>
0133:         *         <td>Day in month
0134:         *         <td><a href="#number">Number</a>
0135:         *         <td><code>10</code>
0136:         *     <tr bgcolor="#eeeeff">
0137:         *         <td><code>F</code>
0138:         *         <td>Day of week in month
0139:         *         <td><a href="#number">Number</a>
0140:         *         <td><code>2</code>
0141:         *     <tr>
0142:         *         <td><code>E</code>
0143:         *         <td>Day in week
0144:         *         <td><a href="#text">Text</a>
0145:         *         <td><code>Tuesday</code>; <code>Tue</code>
0146:         *     <tr bgcolor="#eeeeff">
0147:         *         <td><code>a</code>
0148:         *         <td>Am/pm marker
0149:         *         <td><a href="#text">Text</a>
0150:         *         <td><code>PM</code>
0151:         *     <tr>
0152:         *         <td><code>H</code>
0153:         *         <td>Hour in day (0-23)
0154:         *         <td><a href="#number">Number</a>
0155:         *         <td><code>0</code>
0156:         *     <tr bgcolor="#eeeeff">
0157:         *         <td><code>k</code>
0158:         *         <td>Hour in day (1-24)
0159:         *         <td><a href="#number">Number</a>
0160:         *         <td><code>24</code>
0161:         *     <tr>
0162:         *         <td><code>K</code>
0163:         *         <td>Hour in am/pm (0-11)
0164:         *         <td><a href="#number">Number</a>
0165:         *         <td><code>0</code>
0166:         *     <tr bgcolor="#eeeeff">
0167:         *         <td><code>h</code>
0168:         *         <td>Hour in am/pm (1-12)
0169:         *         <td><a href="#number">Number</a>
0170:         *         <td><code>12</code>
0171:         *     <tr>
0172:         *         <td><code>m</code>
0173:         *         <td>Minute in hour
0174:         *         <td><a href="#number">Number</a>
0175:         *         <td><code>30</code>
0176:         *     <tr bgcolor="#eeeeff">
0177:         *         <td><code>s</code>
0178:         *         <td>Second in minute
0179:         *         <td><a href="#number">Number</a>
0180:         *         <td><code>55</code>
0181:         *     <tr>
0182:         *         <td><code>S</code>
0183:         *         <td>Millisecond
0184:         *         <td><a href="#number">Number</a>
0185:         *         <td><code>978</code>
0186:         *     <tr bgcolor="#eeeeff">
0187:         *         <td><code>z</code>
0188:         *         <td>Time zone
0189:         *         <td><a href="#timezone">General time zone</a>
0190:         *         <td><code>Pacific Standard Time</code>; <code>PST</code>; <code>GMT-08:00</code>
0191:         *     <tr>
0192:         *         <td><code>Z</code>
0193:         *         <td>Time zone
0194:         *         <td><a href="#rfc822timezone">RFC 822 time zone</a>
0195:         *         <td><code>-0800</code>
0196:         * </table>
0197:         * </blockquote>
0198:         * Pattern letters are usually repeated, as their number determines the
0199:         * exact presentation:
0200:         * <ul>
0201:         * <li><strong><a name="text">Text:</a></strong>
0202:         *     For formatting, if the number of pattern letters is 4 or more,
0203:         *     the full form is used; otherwise a short or abbreviated form
0204:         *     is used if available.
0205:         *     For parsing, both forms are accepted, independent of the number
0206:         *     of pattern letters.
0207:         * <li><strong><a name="number">Number:</a></strong>
0208:         *     For formatting, the number of pattern letters is the minimum
0209:         *     number of digits, and shorter numbers are zero-padded to this amount.
0210:         *     For parsing, the number of pattern letters is ignored unless
0211:         *     it's needed to separate two adjacent fields.
0212:         * <li><strong><a name="year">Year:</a></strong>
0213:         *     For formatting, if the number of pattern letters is 2, the year
0214:         *     is truncated to 2 digits; otherwise it is interpreted as a
0215:         *     <a href="#number">number</a>.
0216:         *     <p>For parsing, if the number of pattern letters is more than 2,
0217:         *     the year is interpreted literally, regardless of the number of
0218:         *     digits. So using the pattern "MM/dd/yyyy", "01/11/12" parses to
0219:         *     Jan 11, 12 A.D.
0220:         *     <p>For parsing with the abbreviated year pattern ("y" or "yy"),
0221:         *     <code>SimpleDateFormat</code> must interpret the abbreviated year
0222:         *     relative to some century.  It does this by adjusting dates to be
0223:         *     within 80 years before and 20 years after the time the <code>SimpleDateFormat</code>
0224:         *     instance is created. For example, using a pattern of "MM/dd/yy" and a
0225:         *     <code>SimpleDateFormat</code> instance created on Jan 1, 1997,  the string
0226:         *     "01/11/12" would be interpreted as Jan 11, 2012 while the string "05/04/64"
0227:         *     would be interpreted as May 4, 1964.
0228:         *     During parsing, only strings consisting of exactly two digits, as defined by
0229:         *     {@link Character#isDigit(char)}, will be parsed into the default century.
0230:         *     Any other numeric string, such as a one digit string, a three or more digit
0231:         *     string, or a two digit string that isn't all digits (for example, "-1"), is
0232:         *     interpreted literally.  So "01/02/3" or "01/02/003" are parsed, using the
0233:         *     same pattern, as Jan 2, 3 AD.  Likewise, "01/02/-3" is parsed as Jan 2, 4 BC.
0234:         * <li><strong><a name="month">Month:</a></strong>
0235:         *     If the number of pattern letters is 3 or more, the month is
0236:         *     interpreted as <a href="#text">text</a>; otherwise,
0237:         *     it is interpreted as a <a href="#number">number</a>.
0238:         * <li><strong><a name="timezone">General time zone:</a></strong>
0239:         *     Time zones are interpreted as <a href="#text">text</a> if they have
0240:         *     names. For time zones representing a GMT offset value, the
0241:         *     following syntax is used:
0242:         *     <pre>
0243:         *     <a name="GMTOffsetTimeZone"><i>GMTOffsetTimeZone:</i></a>
0244:         *             <code>GMT</code> <i>Sign</i> <i>Hours</i> <code>:</code> <i>Minutes</i>
0245:         *     <i>Sign:</i> one of
0246:         *             <code>+ -</code>
0247:         *     <i>Hours:</i>
0248:         *             <i>Digit</i>
0249:         *             <i>Digit</i> <i>Digit</i>
0250:         *     <i>Minutes:</i>
0251:         *             <i>Digit</i> <i>Digit</i>
0252:         *     <i>Digit:</i> one of
0253:         *             <code>0 1 2 3 4 5 6 7 8 9</code></pre>
0254:         *     <i>Hours</i> must be between 0 and 23, and <i>Minutes</i> must be between
0255:         *     00 and 59. The format is locale independent and digits must be taken
0256:         *     from the Basic Latin block of the Unicode standard.
0257:         *     <p>For parsing, <a href="#rfc822timezone">RFC 822 time zones</a> are also
0258:         *     accepted.
0259:         * <li><strong><a name="rfc822timezone">RFC 822 time zone:</a></strong>
0260:         *     For formatting, the RFC 822 4-digit time zone format is used:
0261:         *     <pre>
0262:         *     <i>RFC822TimeZone:</i>
0263:         *             <i>Sign</i> <i>TwoDigitHours</i> <i>Minutes</i>
0264:         *     <i>TwoDigitHours:</i>
0265:         *             <i>Digit Digit</i></pre>
0266:         *     <i>TwoDigitHours</i> must be between 00 and 23. Other definitions
0267:         *     are as for <a href="#timezone">general time zones</a>.
0268:         *     <p>For parsing, <a href="#timezone">general time zones</a> are also
0269:         *     accepted.
0270:         * </ul>
0271:         * <code>SimpleDateFormat</code> also supports <em>localized date and time
0272:         * pattern</em> strings. In these strings, the pattern letters described above
0273:         * may be replaced with other, locale dependent, pattern letters.
0274:         * <code>SimpleDateFormat</code> does not deal with the localization of text
0275:         * other than the pattern letters; that's up to the client of the class.
0276:         * <p>
0277:         *
0278:         * <h4>Examples</h4>
0279:         *
0280:         * The following examples show how date and time patterns are interpreted in
0281:         * the U.S. locale. The given date and time are 2001-07-04 12:08:56 local time
0282:         * in the U.S. Pacific Time time zone.
0283:         * <blockquote>
0284:         * <table border=0 cellspacing=3 cellpadding=0 summary="Examples of date and time patterns interpreted in the U.S. locale">
0285:         *     <tr bgcolor="#ccccff">
0286:         *         <th align=left>Date and Time Pattern
0287:         *         <th align=left>Result
0288:         *     <tr>
0289:         *         <td><code>"yyyy.MM.dd G 'at' HH:mm:ss z"</code>
0290:         *         <td><code>2001.07.04 AD at 12:08:56 PDT</code>
0291:         *     <tr bgcolor="#eeeeff">
0292:         *         <td><code>"EEE, MMM d, ''yy"</code>
0293:         *         <td><code>Wed, Jul 4, '01</code>
0294:         *     <tr>
0295:         *         <td><code>"h:mm a"</code>
0296:         *         <td><code>12:08 PM</code>
0297:         *     <tr bgcolor="#eeeeff">
0298:         *         <td><code>"hh 'o''clock' a, zzzz"</code>
0299:         *         <td><code>12 o'clock PM, Pacific Daylight Time</code>
0300:         *     <tr>
0301:         *         <td><code>"K:mm a, z"</code>
0302:         *         <td><code>0:08 PM, PDT</code>
0303:         *     <tr bgcolor="#eeeeff">
0304:         *         <td><code>"yyyyy.MMMMM.dd GGG hh:mm aaa"</code>
0305:         *         <td><code>02001.July.04 AD 12:08 PM</code>
0306:         *     <tr>
0307:         *         <td><code>"EEE, d MMM yyyy HH:mm:ss Z"</code>
0308:         *         <td><code>Wed, 4 Jul 2001 12:08:56 -0700</code>
0309:         *     <tr bgcolor="#eeeeff">
0310:         *         <td><code>"yyMMddHHmmssZ"</code>
0311:         *         <td><code>010704120856-0700</code>
0312:         * </table>
0313:         * </blockquote>
0314:         *
0315:         * <h4><a name="synchronization">Synchronization</a></h4>
0316:         *
0317:         * <p>
0318:         * Date formats are not synchronized.
0319:         * It is recommended to create separate format instances for each thread.
0320:         * If multiple threads access a format concurrently, it must be synchronized
0321:         * externally.
0322:         *
0323:         * @see          <a href="http://java.sun.com/docs/books/tutorial/i18n/format/simpleDateFormat.html">Java Tutorial</a>
0324:         * @see          java.util.Calendar
0325:         * @see          java.util.TimeZone
0326:         * @see          DateFormat
0327:         * @see          DateFormatSymbols
0328:         * @version      1.59, 10/10/06
0329:         * @author       Mark Davis, Chen-Lieh Huang, Alan Liu
0330:         */
0331:        public class SimpleDateFormat extends DateFormat {
0332:
0333:            // the official serial version ID which says cryptically
0334:            // which version we're compatible with
0335:            static final long serialVersionUID = 4774881970558875024L;
0336:
0337:            // the internal serial version which says which version was written
0338:            // - 0 (default) for version up to JDK 1.1.3
0339:            // - 1 for version from JDK 1.1.4, which includes a new field
0340:            static final int currentSerialVersion = 1;
0341:
0342:            /**
0343:             * The version of the serialized data on the stream.  Possible values:
0344:             * <ul>
0345:             * <li><b>0</b> or not present on stream: JDK 1.1.3.  This version
0346:             * has no <code>defaultCenturyStart</code> on stream.
0347:             * <li><b>1</b> JDK 1.1.4 or later.  This version adds
0348:             * <code>defaultCenturyStart</code>.
0349:             * </ul>
0350:             * When streaming out this class, the most recent format
0351:             * and the highest allowable <code>serialVersionOnStream</code>
0352:             * is written.
0353:             * @serial
0354:             * @since JDK1.1.4
0355:             */
0356:            private int serialVersionOnStream = currentSerialVersion;
0357:
0358:            /**
0359:             * The pattern string of this formatter.  This is always a non-localized
0360:             * pattern.  May not be null.  See class documentation for details.
0361:             * @serial
0362:             */
0363:            private String pattern;
0364:
0365:            /**
0366:             * The compiled pattern.
0367:             */
0368:            transient private char[] compiledPattern;
0369:
0370:            /**
0371:             * Tags for the compiled pattern.
0372:             */
0373:            private final static int TAG_QUOTE_ASCII_CHAR = 100;
0374:            private final static int TAG_QUOTE_CHARS = 101;
0375:
0376:            /**
0377:             * Locale dependent digit zero.
0378:             * @see #zeroPaddingNumber
0379:             * @see java.text.DecimalFormatSymbols#getZeroDigit
0380:             */
0381:            transient private char zeroDigit;
0382:
0383:            /**
0384:             * The symbols used by this formatter for week names, month names,
0385:             * etc.  May not be null.
0386:             * @serial
0387:             * @see java.text.DateFormatSymbols
0388:             */
0389:            private DateFormatSymbols formatData;
0390:
0391:            /**
0392:             * We map dates with two-digit years into the century starting at
0393:             * <code>defaultCenturyStart</code>, which may be any date.  May
0394:             * not be null.
0395:             * @serial
0396:             * @since JDK1.1.4
0397:             */
0398:            private Date defaultCenturyStart;
0399:
0400:            transient private int defaultCenturyStartYear;
0401:
0402:            private static final int millisPerHour = 60 * 60 * 1000;
0403:            private static final int millisPerMinute = 60 * 1000;
0404:
0405:            // For time zones that have no names, use strings GMT+minutes and
0406:            // GMT-minutes. For instance, in France the time zone is GMT+60.
0407:            private static final String GMT_PLUS = "GMT+";
0408:            private static final String GMT_MINUS = "GMT-";
0409:            private static final String GMT = "GMT";
0410:
0411:            /**
0412:             * Cache to hold the DateTimePatterns of a Locale.
0413:             */
0414:            private static Hashtable cachedLocaleData = new Hashtable(3);
0415:
0416:            /**
0417:             * Cache NumberFormat instances with Locale key.
0418:             */
0419:            private static Hashtable cachedNumberFormatData = new Hashtable(3);
0420:
0421:            /**
0422:             * Constructs a <code>SimpleDateFormat</code> using the default pattern and
0423:             * date format symbols for the default locale.
0424:             * <b>Note:</b> This constructor may not support all locales.
0425:             * For full coverage, use the factory methods in the {@link DateFormat}
0426:             * class.
0427:             */
0428:            public SimpleDateFormat() {
0429:                this (SHORT, SHORT, Locale.getDefault());
0430:            }
0431:
0432:            /**
0433:             * Constructs a <code>SimpleDateFormat</code> using the given pattern and
0434:             * the default date format symbols for the default locale.
0435:             * <b>Note:</b> This constructor may not support all locales.
0436:             * For full coverage, use the factory methods in the {@link DateFormat}
0437:             * class.
0438:             *
0439:             * @param pattern the pattern describing the date and time format
0440:             * @exception NullPointerException if the given pattern is null
0441:             * @exception IllegalArgumentException if the given pattern is invalid
0442:             */
0443:            public SimpleDateFormat(String pattern) {
0444:                this (pattern, Locale.getDefault());
0445:            }
0446:
0447:            /**
0448:             * Constructs a <code>SimpleDateFormat</code> using the given pattern and
0449:             * the default date format symbols for the given locale.
0450:             * <b>Note:</b> This constructor may not support all locales.
0451:             * For full coverage, use the factory methods in the {@link DateFormat}
0452:             * class.
0453:             *
0454:             * @param pattern the pattern describing the date and time format
0455:             * @param locale the locale whose date format symbols should be used
0456:             * @exception NullPointerException if the given pattern is null
0457:             * @exception IllegalArgumentException if the given pattern is invalid
0458:             */
0459:            public SimpleDateFormat(String pattern, Locale locale) {
0460:                this .pattern = pattern;
0461:                this .formatData = new DateFormatSymbols(locale);
0462:                initialize(locale);
0463:            }
0464:
0465:            /**
0466:             * Constructs a <code>SimpleDateFormat</code> using the given pattern and
0467:             * date format symbols.
0468:             *
0469:             * @param pattern the pattern describing the date and time format
0470:             * @param formatSymbols the date format symbols to be used for formatting
0471:             * @exception NullPointerException if the given pattern or formatSymbols is null
0472:             * @exception IllegalArgumentException if the given pattern is invalid
0473:             */
0474:            public SimpleDateFormat(String pattern,
0475:                    DateFormatSymbols formatSymbols) {
0476:                this .pattern = pattern;
0477:                this .formatData = (DateFormatSymbols) formatSymbols.clone();
0478:                initialize(Locale.getDefault());
0479:            }
0480:
0481:            /* Package-private, called by DateFormat factory methods */
0482:            SimpleDateFormat(int timeStyle, int dateStyle, Locale loc) {
0483:                /* try the cache first */
0484:                String[] dateTimePatterns = (String[]) cachedLocaleData
0485:                        .get(loc);
0486:                if (dateTimePatterns == null) { /* cache miss */
0487:                    ResourceBundle r = LocaleData.getLocaleElements(loc);
0488:                    dateTimePatterns = r.getStringArray("DateTimePatterns");
0489:                    /* update cache */
0490:                    cachedLocaleData.put(loc, dateTimePatterns);
0491:                }
0492:                formatData = new DateFormatSymbols(loc);
0493:                if ((timeStyle >= 0) && (dateStyle >= 0)) {
0494:                    Object[] dateTimeArgs = { dateTimePatterns[timeStyle],
0495:                            dateTimePatterns[dateStyle + 4] };
0496:                    pattern = MessageFormat.format(dateTimePatterns[8],
0497:                            dateTimeArgs);
0498:                } else if (timeStyle >= 0) {
0499:                    pattern = dateTimePatterns[timeStyle];
0500:                } else if (dateStyle >= 0) {
0501:                    pattern = dateTimePatterns[dateStyle + 4];
0502:                } else {
0503:                    throw new IllegalArgumentException(
0504:                            "No date or time style specified");
0505:                }
0506:
0507:                initialize(loc);
0508:            }
0509:
0510:            /* Initialize calendar and numberFormat fields */
0511:            private void initialize(Locale loc) {
0512:                // Verify and compile the given pattern.
0513:                compiledPattern = compile(pattern);
0514:
0515:                // The format object must be constructed using the symbols for this zone.
0516:                // However, the calendar should use the current default TimeZone.
0517:                // If this is not contained in the locale zone strings, then the zone
0518:                // will be formatted using generic GMT+/-H:MM nomenclature.
0519:                calendar = Calendar.getInstance(TimeZone.getDefault(), loc);
0520:
0521:                /* try the cache first */
0522:                numberFormat = (NumberFormat) cachedNumberFormatData.get(loc);
0523:                if (numberFormat == null) { /* cache miss */
0524:                    numberFormat = NumberFormat.getIntegerInstance(loc);
0525:                    numberFormat.setGroupingUsed(false);
0526:
0527:                    /* update cache */
0528:                    cachedNumberFormatData.put(loc, numberFormat);
0529:                }
0530:                numberFormat = (NumberFormat) numberFormat.clone();
0531:
0532:                initializeDefaultCentury();
0533:            }
0534:
0535:            /**
0536:             * Returns the compiled form of the given pattern. The syntax of
0537:             * the compiled pattern is:
0538:             * <blockquote>
0539:             * CompiledPattern:
0540:             *     EntryList
0541:             * EntryList:
0542:             *     Entry
0543:             *     EntryList Entry
0544:             * Entry:
0545:             *     TagField
0546:             *     TagField data
0547:             * TagField:
0548:             *     Tag Length
0549:             *     TaggedData
0550:             * Tag:
0551:             *     pattern_char_index
0552:             *     TAG_QUOTE_CHARS
0553:             * Length:
0554:             *     short_length
0555:             *     long_length
0556:             * TaggedData:
0557:             *     TAG_QUOTE_ASCII_CHAR ascii_char
0558:             * 
0559:             * </blockquote>
0560:             * 
0561:             * where `short_length' is an 8-bit unsigned integer between 0 and
0562:             * 254.  `long_length' is a sequence of an 8-bit integer 255 and a
0563:             * 32-bit signed integer value which is split into upper and lower
0564:             * 16-bit fields in two char's. `pattern_char_index' is an 8-bit
0565:             * integer between 0 and 18. `ascii_char' is an 7-bit ASCII
0566:             * character value. `data' depends on its Tag value.
0567:             * <p>
0568:             * If Length is short_length, Tag and short_length are packed in a
0569:             * single char, as illustrated below.
0570:             * <blockquote>
0571:             *     char[0] = (Tag << 8) | short_length;
0572:             * </blockquote>
0573:             * 
0574:             * If Length is long_length, Tag and 255 are packed in the first
0575:             * char and a 32-bit integer, as illustrated below.
0576:             * <blockquote>
0577:             *     char[0] = (Tag << 8) | 255;
0578:             *     char[1] = (char) (long_length >>> 16);
0579:             *     char[2] = (char) (long_length & 0xffff);
0580:             * </blockquote>
0581:             * <p>
0582:             * If Tag is a pattern_char_index, its Length is the number of
0583:             * pattern characters. For example, if the given pattern is
0584:             * "yyyy", Tag is 1 and Length is 4, followed by no data.
0585:             * <p>
0586:             * If Tag is TAG_QUOTE_CHARS, its Length is the number of char's
0587:             * following the TagField. For example, if the given pattern is
0588:             * "'o''clock'", Length is 7 followed by a char sequence of
0589:             * <code>o&nbs;'&nbs;c&nbs;l&nbs;o&nbs;c&nbs;k</code>.
0590:             * <p>
0591:             * TAG_QUOTE_ASCII_CHAR is a special tag and has an ASCII
0592:             * character in place of Length. For example, if the given pattern
0593:             * is "'o'", the TaggedData entry is
0594:             * <code>((TAG_QUOTE_ASCII_CHAR&nbs;<<&nbs;8)&nbs;|&nbs;'o')</code>.
0595:             *
0596:             * @exception NullPointerException if the given pattern is null
0597:             * @exception IllegalArgumentException if the given pattern is invalid
0598:             */
0599:            private char[] compile(String pattern) {
0600:                int length = pattern.length();
0601:                boolean inQuote = false;
0602:                StringBuffer compiledPattern = new StringBuffer(length * 2);
0603:                StringBuffer tmpBuffer = null;
0604:                int count = 0;
0605:                int lastTag = -1;
0606:
0607:                for (int i = 0; i < length; i++) {
0608:                    char c = pattern.charAt(i);
0609:
0610:                    if (c == '\'') {
0611:                        // '' is treated as a single quote regardless of being
0612:                        // in a quoted section.
0613:                        if ((i + 1) < length) {
0614:                            c = pattern.charAt(i + 1);
0615:                            if (c == '\'') {
0616:                                i++;
0617:                                if (count != 0) {
0618:                                    encode(lastTag, count, compiledPattern);
0619:                                    lastTag = -1;
0620:                                    count = 0;
0621:                                }
0622:                                if (inQuote) {
0623:                                    tmpBuffer.append(c);
0624:                                } else {
0625:                                    compiledPattern
0626:                                            .append((char) (TAG_QUOTE_ASCII_CHAR << 8 | c));
0627:                                }
0628:                                continue;
0629:                            }
0630:                        }
0631:                        if (!inQuote) {
0632:                            if (count != 0) {
0633:                                encode(lastTag, count, compiledPattern);
0634:                                lastTag = -1;
0635:                                count = 0;
0636:                            }
0637:                            if (tmpBuffer == null) {
0638:                                tmpBuffer = new StringBuffer(length);
0639:                            } else {
0640:                                tmpBuffer.setLength(0);
0641:                            }
0642:                            inQuote = true;
0643:                        } else {
0644:                            int len = tmpBuffer.length();
0645:                            if (len == 1) {
0646:                                char ch = tmpBuffer.charAt(0);
0647:                                if (ch < 128) {
0648:                                    compiledPattern
0649:                                            .append((char) (TAG_QUOTE_ASCII_CHAR << 8 | ch));
0650:                                } else {
0651:                                    compiledPattern
0652:                                            .append((char) (TAG_QUOTE_CHARS << 8 | 1));
0653:                                    compiledPattern.append(ch);
0654:                                }
0655:                            } else {
0656:                                encode(TAG_QUOTE_CHARS, len, compiledPattern);
0657:                                compiledPattern.append(tmpBuffer);
0658:                            }
0659:                            inQuote = false;
0660:                        }
0661:                        continue;
0662:                    }
0663:                    if (inQuote) {
0664:                        tmpBuffer.append(c);
0665:                        continue;
0666:                    }
0667:                    if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
0668:                        if (count != 0) {
0669:                            encode(lastTag, count, compiledPattern);
0670:                            lastTag = -1;
0671:                            count = 0;
0672:                        }
0673:                        if (c < 128) {
0674:                            // In most cases, c would be a delimiter, such as ':'.
0675:                            compiledPattern
0676:                                    .append((char) (TAG_QUOTE_ASCII_CHAR << 8 | c));
0677:                        } else {
0678:                            // Take any contiguous non-ASCII alphabet characters and
0679:                            // put them in a single TAG_QUOTE_CHARS.
0680:                            int j;
0681:                            for (j = i + 1; j < length; j++) {
0682:                                char d = pattern.charAt(j);
0683:                                if (d == '\''
0684:                                        || (d >= 'a' && d <= 'z' || d >= 'A'
0685:                                                && d <= 'Z')) {
0686:                                    break;
0687:                                }
0688:                            }
0689:                            compiledPattern
0690:                                    .append((char) (TAG_QUOTE_CHARS << 8 | (j - i)));
0691:                            for (; i < j; i++) {
0692:                                compiledPattern.append(pattern.charAt(i));
0693:                            }
0694:                            i--;
0695:                        }
0696:                        continue;
0697:                    }
0698:
0699:                    int tag;
0700:                    if ((tag = formatData.patternChars.indexOf(c)) == -1) {
0701:                        throw new IllegalArgumentException(
0702:                                "Illegal pattern character " + "'" + c + "'");
0703:                    }
0704:                    if (lastTag == -1 || lastTag == tag) {
0705:                        lastTag = tag;
0706:                        count++;
0707:                        continue;
0708:                    }
0709:                    encode(lastTag, count, compiledPattern);
0710:                    lastTag = tag;
0711:                    count = 1;
0712:                }
0713:
0714:                if (inQuote) {
0715:                    throw new IllegalArgumentException("Unterminated quote");
0716:                }
0717:
0718:                if (count != 0) {
0719:                    encode(lastTag, count, compiledPattern);
0720:                }
0721:
0722:                // Copy the compiled pattern to a char array
0723:                int len = compiledPattern.length();
0724:                char[] r = new char[len];
0725:                compiledPattern.getChars(0, len, r, 0);
0726:                return r;
0727:            }
0728:
0729:            /**
0730:             * Encodes the given tag and length and puts encoded char(s) into buffer.
0731:             */
0732:            private static final void encode(int tag, int length,
0733:                    StringBuffer buffer) {
0734:                if (length < 255) {
0735:                    buffer.append((char) (tag << 8 | length));
0736:                } else {
0737:                    buffer.append((char) ((tag << 8) | 0xff));
0738:                    buffer.append((char) (length >>> 16));
0739:                    buffer.append((char) (length & 0xffff));
0740:                }
0741:            }
0742:
0743:            /* Initialize the fields we use to disambiguate ambiguous years. Separate
0744:             * so we can call it from readObject().
0745:             */
0746:            private void initializeDefaultCentury() {
0747:                calendar.setTime(new Date());
0748:                calendar.add(Calendar.YEAR, -80);
0749:                parseAmbiguousDatesAsAfter(calendar.getTime());
0750:            }
0751:
0752:            /* Define one-century window into which to disambiguate dates using
0753:             * two-digit years.
0754:             */
0755:            private void parseAmbiguousDatesAsAfter(Date startDate) {
0756:                defaultCenturyStart = startDate;
0757:                calendar.setTime(startDate);
0758:                defaultCenturyStartYear = calendar.get(Calendar.YEAR);
0759:            }
0760:
0761:            /**
0762:             * Sets the 100-year period 2-digit years will be interpreted as being in
0763:             * to begin on the date the user specifies.
0764:             *
0765:             * @param startDate During parsing, two digit years will be placed in the range
0766:             * <code>startDate</code> to <code>startDate + 100 years</code>.
0767:             * @see #get2DigitYearStart
0768:             * @since 1.2
0769:             */
0770:            public void set2DigitYearStart(Date startDate) {
0771:                parseAmbiguousDatesAsAfter(startDate);
0772:            }
0773:
0774:            /**
0775:             * Returns the beginning date of the 100-year period 2-digit years are interpreted
0776:             * as being within.
0777:             *
0778:             * @return the start of the 100-year period into which two digit years are
0779:             * parsed
0780:             * @see #set2DigitYearStart
0781:             * @since 1.2
0782:             */
0783:            public Date get2DigitYearStart() {
0784:                return defaultCenturyStart;
0785:            }
0786:
0787:            /**
0788:             * Formats the given <code>Date</code> into a date/time string and appends
0789:             * the result to the given <code>StringBuffer</code>.
0790:             *
0791:             * @param date the date-time value to be formatted into a date-time string.
0792:             * @param toAppendTo where the new date-time text is to be appended.
0793:             * @param pos the formatting position. On input: an alignment field,
0794:             * if desired. On output: the offsets of the alignment field.
0795:             * @return the formatted date-time string.
0796:             * @exception NullPointerException if the given date is null
0797:             */
0798:            public StringBuffer format(Date date, StringBuffer toAppendTo,
0799:                    FieldPosition pos) {
0800:                pos.beginIndex = pos.endIndex = 0;
0801:                return format(date, toAppendTo, pos.getFieldDelegate());
0802:            }
0803:
0804:            // Called from Format after creating a FieldDelegate
0805:            private StringBuffer format(Date date, StringBuffer toAppendTo,
0806:                    FieldDelegate delegate) {
0807:                // Convert input date to time field list
0808:                calendar.setTime(date);
0809:
0810:                for (int i = 0; i < compiledPattern.length;) {
0811:                    int tag = compiledPattern[i] >>> 8;
0812:                    int count = compiledPattern[i++] & 0xff;
0813:                    if (count == 255) {
0814:                        count = compiledPattern[i++] << 16;
0815:                        count |= compiledPattern[i++];
0816:                    }
0817:
0818:                    switch (tag) {
0819:                    case TAG_QUOTE_ASCII_CHAR:
0820:                        toAppendTo.append((char) count);
0821:                        break;
0822:
0823:                    case TAG_QUOTE_CHARS:
0824:                        toAppendTo.append(compiledPattern, i, count);
0825:                        i += count;
0826:                        break;
0827:
0828:                    default:
0829:                        subFormat(tag, count, delegate, toAppendTo);
0830:                        break;
0831:                    }
0832:                }
0833:                return toAppendTo;
0834:            }
0835:
0836:            /**
0837:             * Formats an Object producing an <code>AttributedCharacterIterator</code>.
0838:             * You can use the returned <code>AttributedCharacterIterator</code>
0839:             * to build the resulting String, as well as to determine information
0840:             * about the resulting String.
0841:             * <p>
0842:             * Each attribute key of the AttributedCharacterIterator will be of type
0843:             * <code>DateFormat.Field</code>, with the corresponding attribute value
0844:             * being the same as the attribute key.
0845:             *
0846:             * @exception NullPointerException if obj is null.
0847:             * @exception IllegalArgumentException if the Format cannot format the
0848:             *            given object, or if the Format's pattern string is invalid.
0849:             * @param obj The object to format
0850:             * @return AttributedCharacterIterator describing the formatted value.
0851:             * @since 1.4
0852:             */
0853:            public AttributedCharacterIterator formatToCharacterIterator(
0854:                    Object obj) {
0855:                StringBuffer sb = new StringBuffer();
0856:                CharacterIteratorFieldDelegate delegate = new CharacterIteratorFieldDelegate();
0857:
0858:                if (obj instanceof  Date) {
0859:                    format((Date) obj, sb, delegate);
0860:                } else if (obj instanceof  Number) {
0861:                    format(new Date(((Number) obj).longValue()), sb, delegate);
0862:                } else if (obj == null) {
0863:                    throw new NullPointerException(
0864:                            "formatToCharacterIterator must be passed non-null object");
0865:                } else {
0866:                    throw new IllegalArgumentException(
0867:                            "Cannot format given Object as a Date");
0868:                }
0869:                return delegate.getIterator(sb.toString());
0870:            }
0871:
0872:            // Map index into pattern character string to Calendar field number
0873:            private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD = {
0874:                    Calendar.ERA, Calendar.YEAR, Calendar.MONTH, Calendar.DATE,
0875:                    Calendar.HOUR_OF_DAY, Calendar.HOUR_OF_DAY,
0876:                    Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND,
0877:                    Calendar.DAY_OF_WEEK, Calendar.DAY_OF_YEAR,
0878:                    Calendar.DAY_OF_WEEK_IN_MONTH, Calendar.WEEK_OF_YEAR,
0879:                    Calendar.WEEK_OF_MONTH, Calendar.AM_PM, Calendar.HOUR,
0880:                    Calendar.HOUR, Calendar.ZONE_OFFSET, Calendar.ZONE_OFFSET };
0881:
0882:            // Map index into pattern character string to DateFormat field number
0883:            private static final int[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD = {
0884:                    DateFormat.ERA_FIELD, DateFormat.YEAR_FIELD,
0885:                    DateFormat.MONTH_FIELD, DateFormat.DATE_FIELD,
0886:                    DateFormat.HOUR_OF_DAY1_FIELD,
0887:                    DateFormat.HOUR_OF_DAY0_FIELD, DateFormat.MINUTE_FIELD,
0888:                    DateFormat.SECOND_FIELD, DateFormat.MILLISECOND_FIELD,
0889:                    DateFormat.DAY_OF_WEEK_FIELD, DateFormat.DAY_OF_YEAR_FIELD,
0890:                    DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD,
0891:                    DateFormat.WEEK_OF_YEAR_FIELD,
0892:                    DateFormat.WEEK_OF_MONTH_FIELD, DateFormat.AM_PM_FIELD,
0893:                    DateFormat.HOUR1_FIELD, DateFormat.HOUR0_FIELD,
0894:                    DateFormat.TIMEZONE_FIELD, DateFormat.TIMEZONE_FIELD, };
0895:
0896:            // Maps from DecimalFormatSymbols index to Field constant
0897:            private static final Field[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID = {
0898:                    Field.ERA, Field.YEAR, Field.MONTH, Field.DAY_OF_MONTH,
0899:                    Field.HOUR_OF_DAY1, Field.HOUR_OF_DAY0, Field.MINUTE,
0900:                    Field.SECOND, Field.MILLISECOND, Field.DAY_OF_WEEK,
0901:                    Field.DAY_OF_YEAR, Field.DAY_OF_WEEK_IN_MONTH,
0902:                    Field.WEEK_OF_YEAR, Field.WEEK_OF_MONTH, Field.AM_PM,
0903:                    Field.HOUR1, Field.HOUR0, Field.TIME_ZONE, Field.TIME_ZONE, };
0904:
0905:            /**
0906:             * Private member function that does the real date/time formatting.
0907:             */
0908:            private void subFormat(int patternCharIndex, int count,
0909:                    FieldDelegate delegate, StringBuffer buffer) {
0910:                int maxIntCount = Integer.MAX_VALUE;
0911:                String current = null;
0912:                int beginOffset = buffer.length();
0913:
0914:                int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
0915:                int value = calendar.get(field);
0916:
0917:                // Note: zeroPaddingNumber() assumes that maxDigits is either
0918:                // 2 or maxIntCount. If we make any changes to this,
0919:                // zeroPaddingNumber() must be fixed.
0920:
0921:                switch (patternCharIndex) {
0922:                case 0: // 'G' - ERA
0923:                    current = formatData.eras[value];
0924:                    break;
0925:                case 1: // 'y' - YEAR
0926:                    if (count >= 4)
0927:                        zeroPaddingNumber(value, count, maxIntCount, buffer);
0928:                    else
0929:                        // count < 4
0930:                        zeroPaddingNumber(value, 2, 2, buffer); // clip 1996 to 96
0931:                    break;
0932:                case 2: // 'M' - MONTH
0933:                    if (count >= 4)
0934:                        current = formatData.months[value];
0935:                    else if (count == 3)
0936:                        current = formatData.shortMonths[value];
0937:                    else
0938:                        zeroPaddingNumber(value + 1, count, maxIntCount, buffer);
0939:                    break;
0940:                case 4: // 'k' - HOUR_OF_DAY: 1-based.  eg, 23:59 + 1 hour =>> 24:59
0941:                    if (value == 0)
0942:                        zeroPaddingNumber(calendar
0943:                                .getMaximum(Calendar.HOUR_OF_DAY) + 1, count,
0944:                                maxIntCount, buffer);
0945:                    else
0946:                        zeroPaddingNumber(value, count, maxIntCount, buffer);
0947:                    break;
0948:                case 9: // 'E' - DAY_OF_WEEK
0949:                    if (count >= 4)
0950:                        current = formatData.weekdays[value];
0951:                    else
0952:                        // count < 4, use abbreviated form if exists
0953:                        current = formatData.shortWeekdays[value];
0954:                    break;
0955:                case 14: // 'a' - AM_PM
0956:                    current = formatData.ampms[value];
0957:                    break;
0958:                case 15: // 'h' - HOUR:1-based.  eg, 11PM + 1 hour =>> 12 AM
0959:                    if (value == 0)
0960:                        zeroPaddingNumber(calendar
0961:                                .getLeastMaximum(Calendar.HOUR) + 1, count,
0962:                                maxIntCount, buffer);
0963:                    else
0964:                        zeroPaddingNumber(value, count, maxIntCount, buffer);
0965:                    break;
0966:                case 17: // 'z' - ZONE_OFFSET
0967:                    int zoneIndex = formatData.getZoneIndex(calendar
0968:                            .getTimeZone().getID());
0969:                    if (zoneIndex == -1) {
0970:                        // For time zones that have no names, use strings
0971:                        // GMT+hours:minutes and GMT-hours:minutes.
0972:                        // For instance, France time zone uses GMT+01:00.
0973:
0974:                        value = calendar.get(Calendar.ZONE_OFFSET)
0975:                                + calendar.get(Calendar.DST_OFFSET);
0976:
0977:                        if (value < 0) {
0978:                            buffer.append(GMT_MINUS);
0979:                            value = -value; // suppress the '-' sign for text display.
0980:                        } else {
0981:                            buffer.append(GMT_PLUS);
0982:                        }
0983:                        int num = value / millisPerHour;
0984:                        if (num < 10) {
0985:                            buffer.append('0');
0986:                        }
0987:                        buffer.append(num);
0988:                        buffer.append(':');
0989:                        num = (value % millisPerHour) / millisPerMinute;
0990:                        if (num < 10) {
0991:                            buffer.append('0');
0992:                        }
0993:                        buffer.append(num);
0994:                    } else if (calendar.get(Calendar.DST_OFFSET) != 0) {
0995:                        if (count >= 4) {
0996:                            current = formatData.zoneStrings[zoneIndex][3];
0997:                        } else {
0998:                            // count < 4, use abbreviated form if exists
0999:                            current = formatData.zoneStrings[zoneIndex][4];
1000:                        }
1001:                    } else {
1002:                        if (count >= 4) {
1003:                            current = formatData.zoneStrings[zoneIndex][1];
1004:                        } else {
1005:                            current = formatData.zoneStrings[zoneIndex][2];
1006:                        }
1007:                    }
1008:                    break;
1009:                case 18: // 'Z' - ZONE_OFFSET ("-/+hhmm" form)
1010:                    value = calendar.get(Calendar.ZONE_OFFSET)
1011:                            + calendar.get(Calendar.DST_OFFSET);
1012:
1013:                    if (value < 0) {
1014:                        buffer.append('-');
1015:                        value = -value; // suppress the '-' sign for text display.
1016:                    } else {
1017:                        buffer.append('+');
1018:                    }
1019:
1020:                    int num = value / millisPerHour;
1021:                    if (num < 10) {
1022:                        buffer.append('0');
1023:                    }
1024:                    buffer.append(num);
1025:                    num = (value % millisPerHour) / millisPerMinute;
1026:                    if (num < 10) {
1027:                        buffer.append('0');
1028:                    }
1029:                    buffer.append(num);
1030:                    break;
1031:                default:
1032:                    // case 3: // 'd' - DATE
1033:                    // case 5: // 'H' - HOUR_OF_DAY:0-based.  eg, 23:59 + 1 hour =>> 00:59
1034:                    // case 6: // 'm' - MINUTE
1035:                    // case 7: // 's' - SECOND
1036:                    // case 8: // 'S' - MILLISECOND
1037:                    // case 10: // 'D' - DAY_OF_YEAR
1038:                    // case 11: // 'F' - DAY_OF_WEEK_IN_MONTH
1039:                    // case 12: // 'w' - WEEK_OF_YEAR
1040:                    // case 13: // 'W' - WEEK_OF_MONTH
1041:                    // case 16: // 'K' - HOUR: 0-based.  eg, 11PM + 1 hour =>> 0 AM
1042:                    zeroPaddingNumber(value, count, maxIntCount, buffer);
1043:                    break;
1044:                } // switch (patternCharIndex)
1045:
1046:                if (current != null) {
1047:                    buffer.append(current);
1048:                }
1049:
1050:                int fieldID = PATTERN_INDEX_TO_DATE_FORMAT_FIELD[patternCharIndex];
1051:                Field f = PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID[patternCharIndex];
1052:
1053:                delegate.formatted(fieldID, f, f, beginOffset, buffer.length(),
1054:                        buffer);
1055:            }
1056:
1057:            /**
1058:             * Formats a number with the specified minimum and maximum number of digits.
1059:             */
1060:            private final void zeroPaddingNumber(int value, int minDigits,
1061:                    int maxDigits, StringBuffer buffer) {
1062:                // Optimization for 1, 2 and 4 digit numbers. This should
1063:                // cover most cases of formatting date/time related items.
1064:                // Note: This optimization code assumes that maxDigits is
1065:                // either 2 or Integer.MAX_VALUE (maxIntCount in format()).
1066:                try {
1067:                    if (zeroDigit == 0) {
1068:                        zeroDigit = ((DecimalFormat) numberFormat)
1069:                                .getDecimalFormatSymbols().getZeroDigit();
1070:                    }
1071:                    if (value >= 0) {
1072:                        if (value < 100 && minDigits >= 1 && minDigits <= 2) {
1073:                            if (value < 10) {
1074:                                if (minDigits == 2) {
1075:                                    buffer.append(zeroDigit);
1076:                                }
1077:                                buffer.append((char) (zeroDigit + value));
1078:                            } else {
1079:                                buffer.append((char) (zeroDigit + value / 10));
1080:                                buffer.append((char) (zeroDigit + value % 10));
1081:                            }
1082:                            return;
1083:                        } else if (value >= 1000 && value < 10000) {
1084:                            if (minDigits == 4) {
1085:                                buffer
1086:                                        .append((char) (zeroDigit + value / 1000));
1087:                                value %= 1000;
1088:                                buffer.append((char) (zeroDigit + value / 100));
1089:                                value %= 100;
1090:                                buffer.append((char) (zeroDigit + value / 10));
1091:                                buffer.append((char) (zeroDigit + value % 10));
1092:                                return;
1093:                            }
1094:                            if (minDigits == 2 && maxDigits == 2) {
1095:                                zeroPaddingNumber(value % 100, 2, 2, buffer);
1096:                                return;
1097:                            }
1098:                        }
1099:                    }
1100:                } catch (Exception e) {
1101:                }
1102:
1103:                numberFormat.setMinimumIntegerDigits(minDigits);
1104:                numberFormat.setMaximumIntegerDigits(maxDigits);
1105:                numberFormat.format((long) value, buffer,
1106:                        DontCareFieldPosition.INSTANCE);
1107:            }
1108:
1109:            /**
1110:             * Parses text from a string to produce a <code>Date</code>.
1111:             * <p>
1112:             * The method attempts to parse text starting at the index given by
1113:             * <code>pos</code>.
1114:             * If parsing succeeds, then the index of <code>pos</code> is updated
1115:             * to the index after the last character used (parsing does not necessarily
1116:             * use all characters up to the end of the string), and the parsed
1117:             * date is returned. The updated <code>pos</code> can be used to
1118:             * indicate the starting point for the next call to this method.
1119:             * If an error occurs, then the index of <code>pos</code> is not
1120:             * changed, the error index of <code>pos</code> is set to the index of
1121:             * the character where the error occurred, and null is returned.
1122:             *
1123:             * @param text  A <code>String</code>, part of which should be parsed.
1124:             * @param pos   A <code>ParsePosition</code> object with index and error
1125:             *              index information as described above.
1126:             * @return A <code>Date</code> parsed from the string. In case of
1127:             *         error, returns null.
1128:             * @exception NullPointerException if <code>text</code> or <code>pos</code> is null.
1129:             */
1130:            public Date parse(String text, ParsePosition pos) {
1131:                int start = pos.index;
1132:                int oldStart = start;
1133:                int textLength = text.length();
1134:
1135:                boolean[] ambiguousYear = { false };
1136:
1137:                calendar.clear(); // Clears all the time fields
1138:
1139:                for (int i = 0; i < compiledPattern.length;) {
1140:                    int tag = compiledPattern[i] >>> 8;
1141:                    int count = compiledPattern[i++] & 0xff;
1142:                    if (count == 255) {
1143:                        count = compiledPattern[i++] << 16;
1144:                        count |= compiledPattern[i++];
1145:                    }
1146:
1147:                    switch (tag) {
1148:                    case TAG_QUOTE_ASCII_CHAR:
1149:                        if (start >= textLength
1150:                                || text.charAt(start) != (char) count) {
1151:                            pos.index = oldStart;
1152:                            pos.errorIndex = start;
1153:                            return null;
1154:                        }
1155:                        start++;
1156:                        break;
1157:
1158:                    case TAG_QUOTE_CHARS:
1159:                        while (count-- > 0) {
1160:                            if (start >= textLength
1161:                                    || text.charAt(start) != compiledPattern[i++]) {
1162:                                pos.index = oldStart;
1163:                                pos.errorIndex = start;
1164:                                return null;
1165:                            }
1166:                            start++;
1167:                        }
1168:                        break;
1169:
1170:                    default:
1171:                        // Peek the next pattern to determine if we need to
1172:                        // obey the number of pattern letters for
1173:                        // parsing. It's required when parsing contiguous
1174:                        // digit text (e.g., "20010704") with a pattern which
1175:                        // has no delimiters between fields, like "yyyyMMdd".
1176:                        boolean obeyCount = false;
1177:                        if (i < compiledPattern.length) {
1178:                            int nextTag = compiledPattern[i] >>> 8;
1179:                            if (!(nextTag == TAG_QUOTE_ASCII_CHAR || nextTag == TAG_QUOTE_CHARS)) {
1180:                                obeyCount = true;
1181:                            }
1182:                        }
1183:                        start = subParse(text, start, tag, count, obeyCount,
1184:                                ambiguousYear, pos);
1185:                        if (start < 0) {
1186:                            pos.index = oldStart;
1187:                            return null;
1188:                        }
1189:                    }
1190:                }
1191:
1192:                // If only AM_PM has been set without any hour value, then
1193:                // HOUR is set to 0 to force calendar to take the HOUR and
1194:                // AM_PM fields. (bugid: 4736959) (Changing GregorianCalendar
1195:                // to take a look at both the HOUR and AM_PM stamp values
1196:                // breaks JCK GregorianCalendar2057.)
1197:                if (calendar.isSet(Calendar.AM_PM)
1198:                        && !calendar.isSet(Calendar.HOUR)
1199:                        && !calendar.isSet(Calendar.HOUR_OF_DAY)) {
1200:                    calendar.set(Calendar.HOUR, 0); // force to update the stamp value
1201:                }
1202:
1203:                // At this point the fields of Calendar have been set.  Calendar
1204:                // will fill in default values for missing fields when the time
1205:                // is computed.
1206:
1207:                pos.index = start;
1208:
1209:                // This part is a problem:  When we call parsedDate.after, we compute the time.
1210:                // Take the date April 3 2004 at 2:30 am.  When this is first set up, the year
1211:                // will be wrong if we're parsing a 2-digit year pattern.  It will be 1904.
1212:                // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day.  2:30 am
1213:                // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
1214:                // on that day.  It is therefore parsed out to fields as 3:30 am.  Then we
1215:                // add 100 years, and get April 3 2004 at 3:30 am.  Note that April 3 2004 is
1216:                // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
1217:                /*
1218:                Date parsedDate = calendar.getTime();
1219:                if( ambiguousYear[0] && !parsedDate.after(defaultCenturyStart) ) {
1220:                    calendar.add(Calendar.YEAR, 100);
1221:                    parsedDate = calendar.getTime();
1222:                }
1223:                 */
1224:                // Because of the above condition, save off the fields in case we need to readjust.
1225:                // The procedure we use here is not particularly efficient, but there is no other
1226:                // way to do this given the API restrictions present in Calendar.  We minimize
1227:                // inefficiency by only performing this computation when it might apply, that is,
1228:                // when the two-digit year is equal to the start year, and thus might fall at the
1229:                // front or the back of the default century.  This only works because we adjust
1230:                // the year correctly to start with in other cases -- see subParse().
1231:                Date parsedDate;
1232:                try {
1233:                    if (ambiguousYear[0]) // If this is true then the two-digit year == the default start year
1234:                    {
1235:                        // We need a copy of the fields, and we need to avoid triggering a call to
1236:                        // complete(), which will recalculate the fields.  Since we can't access
1237:                        // the fields[] array in Calendar, we clone the entire object.  This will
1238:                        // stop working if Calendar.clone() is ever rewritten to call complete().
1239:                        Calendar savedCalendar = (Calendar) calendar.clone();
1240:                        parsedDate = calendar.getTime();
1241:                        if (parsedDate.before(defaultCenturyStart)) {
1242:                            // We can't use add here because that does a complete() first.
1243:                            savedCalendar.set(Calendar.YEAR,
1244:                                    defaultCenturyStartYear + 100);
1245:                            parsedDate = savedCalendar.getTime();
1246:                        }
1247:                    } else
1248:                        parsedDate = calendar.getTime();
1249:                }
1250:                // An IllegalArgumentException will be thrown by Calendar.getTime()
1251:                // if any fields are out of range, e.g., MONTH == 17.
1252:                catch (IllegalArgumentException e) {
1253:                    pos.errorIndex = start;
1254:                    pos.index = oldStart;
1255:                    return null;
1256:                }
1257:
1258:                return parsedDate;
1259:            }
1260:
1261:            /**
1262:             * Private code-size reduction function used by subParse.
1263:             * @param text the time text being parsed.
1264:             * @param start where to start parsing.
1265:             * @param field the date field being parsed.
1266:             * @param data the string array to parsed.
1267:             * @return the new start position if matching succeeded; a negative number
1268:             * indicating matching failure, otherwise.
1269:             */
1270:            private int matchString(String text, int start, int field,
1271:                    String[] data) {
1272:                int i = 0;
1273:                int count = data.length;
1274:
1275:                if (field == Calendar.DAY_OF_WEEK)
1276:                    i = 1;
1277:
1278:                // There may be multiple strings in the data[] array which begin with
1279:                // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
1280:                // We keep track of the longest match, and return that.  Note that this
1281:                // unfortunately requires us to test all array elements.
1282:                int bestMatchLength = 0, bestMatch = -1;
1283:                for (; i < count; ++i) {
1284:                    int length = data[i].length();
1285:                    // Always compare if we have no match yet; otherwise only compare
1286:                    // against potentially better matches (longer strings).
1287:                    if (length > bestMatchLength
1288:                            && text.regionMatches(true, start, data[i], 0,
1289:                                    length)) {
1290:                        bestMatch = i;
1291:                        bestMatchLength = length;
1292:                    }
1293:                }
1294:                if (bestMatch >= 0) {
1295:                    calendar.set(field, bestMatch);
1296:                    return start + bestMatchLength;
1297:                }
1298:                return -start;
1299:            }
1300:
1301:            private int matchZoneString(String text, int start, int zoneIndex) {
1302:                int j;
1303:                for (j = 1; j <= 4; ++j) {
1304:                    // Checking long and short zones [1 & 2],
1305:                    // and long and short daylight [3 & 4].
1306:                    if (text.regionMatches(true, start,
1307:                            formatData.zoneStrings[zoneIndex][j], 0,
1308:                            formatData.zoneStrings[zoneIndex][j].length())) {
1309:                        break;
1310:                    }
1311:                }
1312:                return (j > 4) ? -1 : j;
1313:            }
1314:
1315:            /**
1316:             * find time zone 'text' matched zoneStrings and set to internal
1317:             * calendar.
1318:             */
1319:            private int subParseZoneString(String text, int start) {
1320:                // At this point, check for named time zones by looking through
1321:                // the locale data from the DateFormatZoneData strings.
1322:                // Want to be able to parse both short and long forms.
1323:                int zoneIndex = formatData.getZoneIndex(getTimeZone().getID());
1324:                TimeZone tz = null;
1325:                int j = 0, i = 0;
1326:                if ((zoneIndex != -1)
1327:                        && ((j = matchZoneString(text, start, zoneIndex)) > 0)) {
1328:                    tz = TimeZone
1329:                            .getTimeZone(formatData.zoneStrings[zoneIndex][0]);
1330:                    i = zoneIndex;
1331:                }
1332:                if (tz == null) {
1333:                    zoneIndex = formatData.getZoneIndex(TimeZone.getDefault()
1334:                            .getID());
1335:                    if ((zoneIndex != -1)
1336:                            && ((j = matchZoneString(text, start, zoneIndex)) > 0)) {
1337:                        tz = TimeZone
1338:                                .getTimeZone(formatData.zoneStrings[zoneIndex][0]);
1339:                        i = zoneIndex;
1340:                    }
1341:                }
1342:
1343:                if (tz == null) {
1344:                    for (i = 0; i < formatData.zoneStrings.length; i++) {
1345:                        if ((j = matchZoneString(text, start, i)) > 0) {
1346:                            tz = TimeZone
1347:                                    .getTimeZone(formatData.zoneStrings[i][0]);
1348:                            break;
1349:                        }
1350:                    }
1351:                }
1352:                if (tz != null) { // Matched any ?
1353:                    calendar.set(Calendar.ZONE_OFFSET, tz.getRawOffset());
1354:                    calendar.set(Calendar.DST_OFFSET, j >= 3 ? tz
1355:                            .getDSTSavings() : 0);
1356:                    return (start + formatData.zoneStrings[i][j].length());
1357:                }
1358:                return 0;
1359:            }
1360:
1361:            /**
1362:             * Private member function that converts the parsed date strings into
1363:             * timeFields. Returns -start (for ParsePosition) if failed.
1364:             * @param text the time text to be parsed.
1365:             * @param start where to start parsing.
1366:             * @param ch the pattern character for the date field text to be parsed.
1367:             * @param count the count of a pattern character.
1368:             * @param obeyCount if true, then the next field directly abuts this one,
1369:             * and we should use the count to know when to stop parsing.
1370:             * @param ambiguousYear return parameter; upon return, if ambiguousYear[0]
1371:             * is true, then a two-digit year was parsed and may need to be readjusted.
1372:             * @param origPos origPos.errorIndex is used to return an error index
1373:             * at which a parse error occurred, if matching failure occurs.
1374:             * @return the new start position if matching succeeded; -1 indicating
1375:             * matching failure, otherwise. In case matching failure occurred,
1376:             * an error index is set to origPos.errorIndex.
1377:             */
1378:            private int subParse(String text, int start, int patternCharIndex,
1379:                    int count, boolean obeyCount, boolean[] ambiguousYear,
1380:                    ParsePosition origPos) {
1381:                Number number = null;
1382:                int value = 0;
1383:                ParsePosition pos = new ParsePosition(0);
1384:                pos.index = start;
1385:                int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
1386:
1387:                // If there are any spaces here, skip over them.  If we hit the end
1388:                // of the string, then fail.
1389:                for (;;) {
1390:                    if (pos.index >= text.length()) {
1391:                        origPos.errorIndex = start;
1392:                        return -1;
1393:                    }
1394:                    char c = text.charAt(pos.index);
1395:                    if (c != ' ' && c != '\t')
1396:                        break;
1397:                    ++pos.index;
1398:                }
1399:
1400:                // We handle a few special cases here where we need to parse
1401:                // a number value.  We handle further, more generic cases below.  We need
1402:                // to handle some of them here because some fields require extra processing on
1403:                // the parsed value.
1404:                if (patternCharIndex == 4 /*HOUR_OF_DAY1_FIELD*/
1405:                        || patternCharIndex == 15 /*HOUR1_FIELD*/
1406:                        || (patternCharIndex == 2 /*MONTH_FIELD*/&& count <= 2)
1407:                        || patternCharIndex == 1) {
1408:                    // It would be good to unify this with the obeyCount logic below,
1409:                    // but that's going to be difficult.
1410:                    if (obeyCount) {
1411:                        if ((start + count) > text.length()) {
1412:                            origPos.errorIndex = start;
1413:                            return -1;
1414:                        }
1415:                        number = numberFormat.parse(text.substring(0, start
1416:                                + count), pos);
1417:                    } else
1418:                        number = numberFormat.parse(text, pos);
1419:                    if (number == null) {
1420:                        origPos.errorIndex = pos.index;
1421:                        return -1;
1422:                    }
1423:                    value = number.intValue();
1424:                }
1425:
1426:                int index;
1427:                switch (patternCharIndex) {
1428:                case 0: // 'G' - ERA
1429:                    if ((index = matchString(text, start, Calendar.ERA,
1430:                            formatData.eras)) > 0) {
1431:                        return index;
1432:                    } else {
1433:                        origPos.errorIndex = pos.index;
1434:                        return -1;
1435:                    }
1436:                case 1: // 'y' - YEAR
1437:                    // If there are 3 or more YEAR pattern characters, this indicates
1438:                    // that the year value is to be treated literally, without any
1439:                    // two-digit year adjustments (e.g., from "01" to 2001).  Otherwise
1440:                    // we made adjustments to place the 2-digit year in the proper
1441:                    // century, for parsed strings from "00" to "99".  Any other string
1442:                    // is treated literally:  "2250", "-1", "1", "002".
1443:                    if (count <= 2 && (pos.index - start) == 2
1444:                            && Character.isDigit(text.charAt(start))
1445:                            && Character.isDigit(text.charAt(start + 1))) {
1446:                        // Assume for example that the defaultCenturyStart is 6/18/1903.
1447:                        // This means that two-digit years will be forced into the range
1448:                        // 6/18/1903 to 6/17/2003.  As a result, years 00, 01, and 02
1449:                        // correspond to 2000, 2001, and 2002.  Years 04, 05, etc. correspond
1450:                        // to 1904, 1905, etc.  If the year is 03, then it is 2003 if the
1451:                        // other fields specify a date before 6/18, or 1903 if they specify a
1452:                        // date afterwards.  As a result, 03 is an ambiguous year.  All other
1453:                        // two-digit years are unambiguous.
1454:                        int ambiguousTwoDigitYear = defaultCenturyStartYear % 100;
1455:                        ambiguousYear[0] = value == ambiguousTwoDigitYear;
1456:                        value += (defaultCenturyStartYear / 100) * 100
1457:                                + (value < ambiguousTwoDigitYear ? 100 : 0);
1458:                    }
1459:                    calendar.set(Calendar.YEAR, value);
1460:                    return pos.index;
1461:                case 2: // 'M' - MONTH
1462:                    if (count <= 2) // i.e., M or MM.
1463:                    {
1464:                        // Don't want to parse the month if it is a string
1465:                        // while pattern uses numeric style: M or MM.
1466:                        // [We computed 'value' above.]
1467:                        calendar.set(Calendar.MONTH, value - 1);
1468:                        return pos.index;
1469:                    } else {
1470:                        // count >= 3 // i.e., MMM or MMMM
1471:                        // Want to be able to parse both short and long forms.
1472:                        // Try count == 4 first:
1473:                        int newStart = 0;
1474:                        if ((newStart = matchString(text, start,
1475:                                Calendar.MONTH, formatData.months)) > 0)
1476:                            return newStart;
1477:                        else // count == 4 failed, now try count == 3
1478:                        if ((index = matchString(text, start, Calendar.MONTH,
1479:                                formatData.shortMonths)) > 0) {
1480:                            return index;
1481:                        } else {
1482:                            origPos.errorIndex = pos.index;
1483:                            return -1;
1484:                        }
1485:                    }
1486:                case 4: // 'k' - HOUR_OF_DAY: 1-based.  eg, 23:59 + 1 hour =>> 24:59
1487:                    // [We computed 'value' above.]
1488:                    if (value == calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1)
1489:                        value = 0;
1490:                    calendar.set(Calendar.HOUR_OF_DAY, value);
1491:                    return pos.index;
1492:                case 9: { // 'E' - DAY_OF_WEEK
1493:                    // Want to be able to parse both short and long forms.
1494:                    // Try count == 4 (DDDD) first:
1495:                    int newStart = 0;
1496:                    if ((newStart = matchString(text, start,
1497:                            Calendar.DAY_OF_WEEK, formatData.weekdays)) > 0)
1498:                        return newStart;
1499:                    else // DDDD failed, now try DDD
1500:                    if ((index = matchString(text, start, Calendar.DAY_OF_WEEK,
1501:                            formatData.shortWeekdays)) > 0) {
1502:                        return index;
1503:                    } else {
1504:                        origPos.errorIndex = pos.index;
1505:                        return -1;
1506:                    }
1507:                }
1508:                case 14: // 'a' - AM_PM
1509:                    if ((index = matchString(text, start, Calendar.AM_PM,
1510:                            formatData.ampms)) > 0) {
1511:                        return index;
1512:                    } else {
1513:                        origPos.errorIndex = pos.index;
1514:                        return -1;
1515:                    }
1516:
1517:                case 15: // 'h' - HOUR:1-based.  eg, 11PM + 1 hour =>> 12 AM
1518:                    // [We computed 'value' above.]
1519:                    if (value == calendar.getLeastMaximum(Calendar.HOUR) + 1)
1520:                        value = 0;
1521:                    calendar.set(Calendar.HOUR, value);
1522:                    return pos.index;
1523:                case 17: // 'z' - ZONE_OFFSET
1524:                case 18: // 'Z' - ZONE_OFFSET
1525:                    // First try to parse generic forms such as GMT-07:00. Do this first
1526:                    // in case localized DateFormatZoneData contains the string "GMT"
1527:                    // for a zone; in that case, we don't want to match the first three
1528:                    // characters of GMT+/-hh:mm etc.
1529:                {
1530:                    int sign = 0;
1531:                    int offset;
1532:
1533:                    // For time zones that have no known names, look for strings
1534:                    // of the form:
1535:                    //    GMT[+-]hours:minutes or
1536:                    //    GMT.
1537:                    if ((text.length() - start) >= GMT.length()
1538:                            && text.regionMatches(true, start, GMT, 0, GMT
1539:                                    .length())) {
1540:                        int num;
1541:                        calendar.set(Calendar.DST_OFFSET, 0);
1542:                        pos.index = start + GMT.length();
1543:
1544:                        try { // try-catch for "GMT" only time zone string
1545:                            if (text.charAt(pos.index) == '+') {
1546:                                sign = 1;
1547:                            } else if (text.charAt(pos.index) == '-') {
1548:                                sign = -1;
1549:                            }
1550:                        } catch (StringIndexOutOfBoundsException e) {
1551:                        }
1552:
1553:                        if (sign == 0) { /* "GMT" without offset */
1554:                            calendar.set(Calendar.ZONE_OFFSET, 0);
1555:                            return pos.index;
1556:                        }
1557:
1558:                        // Look for hours.
1559:                        try {
1560:                            char c = text.charAt(++pos.index);
1561:                            if (c < '0' || c > '9') { /* must be from '0' to '9'. */
1562:                                origPos.errorIndex = pos.index;
1563:                                return -1; // Wasn't actually a number.
1564:                            } else {
1565:                                num = c - '0';
1566:                            }
1567:                            if (text.charAt(++pos.index) != ':') {
1568:                                c = text.charAt(pos.index);
1569:                                if (c < '0' || c > '9') { /* must be from '0' to '9'. */
1570:                                    origPos.errorIndex = pos.index;
1571:                                    return -1; // Wasn't actually a number.
1572:                                } else {
1573:                                    num *= 10;
1574:                                    num += c - '0';
1575:                                    pos.index++;
1576:                                }
1577:                            }
1578:                            if (num > 23) {
1579:                                origPos.errorIndex = pos.index - 1;
1580:                                return -1; // Wasn't actually a number.
1581:                            }
1582:                            if (text.charAt(pos.index) != ':') {
1583:                                origPos.errorIndex = pos.index;
1584:                                return -1; // Wasn't actually a number.
1585:                            }
1586:                        } catch (StringIndexOutOfBoundsException e) {
1587:                            origPos.errorIndex = pos.index;
1588:                            return -1; // Wasn't actually a number.
1589:                        }
1590:
1591:                        // Look for minutes.
1592:                        offset = num * 60;
1593:                        try {
1594:                            char c = text.charAt(++pos.index);
1595:                            if (c < '0' || c > '9') { /* must be from '0' to '9'. */
1596:                                origPos.errorIndex = pos.index;
1597:                                return -1; // Wasn't actually a number.
1598:                            } else {
1599:                                num = c - '0';
1600:                                c = text.charAt(++pos.index);
1601:                                if (c < '0' || c > '9') { /* must be from '0' to '9'. */
1602:                                    origPos.errorIndex = pos.index;
1603:                                    return -1; // Wasn't actually a number.
1604:                                } else {
1605:                                    num *= 10;
1606:                                    num += c - '0';
1607:                                }
1608:                            }
1609:
1610:                            if (num > 59) {
1611:                                origPos.errorIndex = pos.index;
1612:                                return -1; // Wasn't actually a number.
1613:                            }
1614:                        } catch (StringIndexOutOfBoundsException e) {
1615:                            origPos.errorIndex = pos.index;
1616:                            return -1; // Wasn't actually a number.
1617:                        }
1618:                        offset += num;
1619:
1620:                        // Fall through for final processing below of 'offset' and 'sign'.
1621:                    } else {
1622:                        // At this point, check for named time zones by looking through
1623:                        // the locale data from the DateFormatZoneData strings.
1624:                        // Want to be able to parse both short and long forms.
1625:                        int i = subParseZoneString(text, pos.index);
1626:                        if (i != 0) {
1627:                            return i;
1628:                        }
1629:
1630:                        // As a last resort, look for numeric timezones of the form
1631:                        // [+-]hhmm as specified by RFC 822.  This code is actually
1632:                        // a little more permissive than RFC 822.  It will try to do
1633:                        // its best with numbers that aren't strictly 4 digits long.
1634:                        try {
1635:                            if (text.charAt(pos.index) == '+') {
1636:                                sign = 1;
1637:                            } else if (text.charAt(pos.index) == '-') {
1638:                                sign = -1;
1639:                            }
1640:                            if (sign == 0) {
1641:                                origPos.errorIndex = pos.index;
1642:                                return -1;
1643:                            }
1644:
1645:                            // Look for hh.
1646:                            int hours = 0;
1647:                            char c = text.charAt(++pos.index);
1648:                            if (c < '0' || c > '9') { /* must be from '0' to '9'. */
1649:                                origPos.errorIndex = pos.index;
1650:                                return -1; // Wasn't actually a number.
1651:                            } else {
1652:                                hours = c - '0';
1653:                                c = text.charAt(++pos.index);
1654:                                if (c < '0' || c > '9') { /* must be from '0' to '9'. */
1655:                                    origPos.errorIndex = pos.index;
1656:                                    return -1; // Wasn't actually a number.
1657:                                } else {
1658:                                    hours *= 10;
1659:                                    hours += c - '0';
1660:                                }
1661:                            }
1662:                            if (hours > 23) {
1663:                                origPos.errorIndex = pos.index;
1664:                                return -1; // Wasn't actually a number.
1665:                            }
1666:
1667:                            // Look for mm.
1668:                            int minutes = 0;
1669:                            c = text.charAt(++pos.index);
1670:                            if (c < '0' || c > '9') { /* must be from '0' to '9'. */
1671:                                origPos.errorIndex = pos.index;
1672:                                return -1; // Wasn't actually a number.
1673:                            } else {
1674:                                minutes = c - '0';
1675:                                c = text.charAt(++pos.index);
1676:                                if (c < '0' || c > '9') { /* must be from '0' to '9'. */
1677:                                    origPos.errorIndex = pos.index;
1678:                                    return -1; // Wasn't actually a number.
1679:                                } else {
1680:                                    minutes *= 10;
1681:                                    minutes += c - '0';
1682:                                }
1683:                            }
1684:
1685:                            if (minutes > 59) {
1686:                                origPos.errorIndex = pos.index;
1687:                                return -1; // Wasn't actually a number.
1688:                            }
1689:
1690:                            offset = hours * 60 + minutes;
1691:                        } catch (StringIndexOutOfBoundsException e) {
1692:                            origPos.errorIndex = pos.index;
1693:                            return -1; // Wasn't actually a number.
1694:                        }
1695:                    }
1696:
1697:                    // Do the final processing for both of the above cases.  We only
1698:                    // arrive here if the form GMT+/-... or an RFC 822 form was seen.
1699:                    if (sign != 0) {
1700:                        offset *= millisPerMinute * sign;
1701:                        calendar.set(Calendar.ZONE_OFFSET, offset);
1702:                        calendar.set(Calendar.DST_OFFSET, 0);
1703:                        return ++pos.index;
1704:                    }
1705:                }
1706:
1707:                    // All efforts to parse a zone failed.
1708:                    origPos.errorIndex = pos.index;
1709:                    return -1;
1710:
1711:                default:
1712:                    // case 3: // 'd' - DATE
1713:                    // case 5: // 'H' - HOUR_OF_DAY:0-based.  eg, 23:59 + 1 hour =>> 00:59
1714:                    // case 6: // 'm' - MINUTE
1715:                    // case 7: // 's' - SECOND
1716:                    // case 8: // 'S' - MILLISECOND
1717:                    // case 10: // 'D' - DAY_OF_YEAR
1718:                    // case 11: // 'F' - DAY_OF_WEEK_IN_MONTH
1719:                    // case 12: // 'w' - WEEK_OF_YEAR
1720:                    // case 13: // 'W' - WEEK_OF_MONTH
1721:                    // case 16: // 'K' - HOUR: 0-based.  eg, 11PM + 1 hour =>> 0 AM
1722:
1723:                    // Handle "generic" fields
1724:                    if (obeyCount) {
1725:                        if ((start + count) > text.length()) {
1726:                            origPos.errorIndex = pos.index;
1727:                            return -1;
1728:                        }
1729:                        number = numberFormat.parse(text.substring(0, start
1730:                                + count), pos);
1731:                    } else
1732:                        number = numberFormat.parse(text, pos);
1733:                    if (number != null) {
1734:                        calendar.set(field, number.intValue());
1735:                        return pos.index;
1736:                    }
1737:                    origPos.errorIndex = pos.index;
1738:                    return -1;
1739:                }
1740:            }
1741:
1742:            /**
1743:             * Translates a pattern, mapping each character in the from string to the
1744:             * corresponding character in the to string.
1745:             *
1746:             * @exception IllegalArgumentException if the given pattern is invalid
1747:             */
1748:            private String translatePattern(String pattern, String from,
1749:                    String to) {
1750:                StringBuffer result = new StringBuffer();
1751:                boolean inQuote = false;
1752:                for (int i = 0; i < pattern.length(); ++i) {
1753:                    char c = pattern.charAt(i);
1754:                    if (inQuote) {
1755:                        if (c == '\'')
1756:                            inQuote = false;
1757:                    } else {
1758:                        if (c == '\'')
1759:                            inQuote = true;
1760:                        else if ((c >= 'a' && c <= 'z')
1761:                                || (c >= 'A' && c <= 'Z')) {
1762:                            int ci = from.indexOf(c);
1763:                            if (ci == -1)
1764:                                throw new IllegalArgumentException(
1765:                                        "Illegal pattern " + " character '" + c
1766:                                                + "'");
1767:                            c = to.charAt(ci);
1768:                        }
1769:                    }
1770:                    result.append(c);
1771:                }
1772:                if (inQuote)
1773:                    throw new IllegalArgumentException(
1774:                            "Unfinished quote in pattern");
1775:                return result.toString();
1776:            }
1777:
1778:            /**
1779:             * Returns a pattern string describing this date format.
1780:             *
1781:             * @return a pattern string describing this date format.
1782:             */
1783:            public String toPattern() {
1784:                return pattern;
1785:            }
1786:
1787:            /**
1788:             * Returns a localized pattern string describing this date format.
1789:             *
1790:             * @return a localized pattern string describing this date format.
1791:             */
1792:            public String toLocalizedPattern() {
1793:                return translatePattern(pattern, formatData.patternChars,
1794:                        formatData.localPatternChars);
1795:            }
1796:
1797:            /**
1798:             * Applies the given pattern string to this date format.
1799:             *
1800:             * @param pattern the new date and time pattern for this date format
1801:             * @exception NullPointerException if the given pattern is null
1802:             * @exception IllegalArgumentException if the given pattern is invalid
1803:             */
1804:            public void applyPattern(String pattern) {
1805:                compiledPattern = compile(pattern);
1806:                this .pattern = pattern;
1807:            }
1808:
1809:            /**
1810:             * Applies the given localized pattern string to this date format.
1811:             *
1812:             * @param pattern a String to be mapped to the new date and time format
1813:             *        pattern for this format
1814:             * @exception NullPointerException if the given pattern is null
1815:             * @exception IllegalArgumentException if the given pattern is invalid
1816:             */
1817:            public void applyLocalizedPattern(String pattern) {
1818:                String p = translatePattern(pattern,
1819:                        formatData.localPatternChars, formatData.patternChars);
1820:                compiledPattern = compile(p);
1821:                this .pattern = p;
1822:            }
1823:
1824:            /**
1825:             * Gets a copy of the date and time format symbols of this date format.
1826:             *
1827:             * @return the date and time format symbols of this date format
1828:             * @see #setDateFormatSymbols
1829:             */
1830:            public DateFormatSymbols getDateFormatSymbols() {
1831:                return (DateFormatSymbols) formatData.clone();
1832:            }
1833:
1834:            /**
1835:             * Sets the date and time format symbols of this date format.
1836:             *
1837:             * @param newFormatSymbols the new date and time format symbols
1838:             * @exception NullPointerException if the given newFormatSymbols is null
1839:             * @see #getDateFormatSymbols
1840:             */
1841:            public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols) {
1842:                this .formatData = (DateFormatSymbols) newFormatSymbols.clone();
1843:            }
1844:
1845:            /**
1846:             * Creates a copy of this <code>SimpleDateFormat</code>. This also
1847:             * clones the format's date format symbols.
1848:             *
1849:             * @return a clone of this <code>SimpleDateFormat</code>
1850:             */
1851:            public Object clone() {
1852:                SimpleDateFormat other = (SimpleDateFormat) super .clone();
1853:                other.formatData = (DateFormatSymbols) formatData.clone();
1854:                return other;
1855:            }
1856:
1857:            /**
1858:             * Returns the hash code value for this <code>SimpleDateFormat</code> object.
1859:             *
1860:             * @return the hash code value for this <code>SimpleDateFormat</code> object.
1861:             */
1862:            public int hashCode() {
1863:                return pattern.hashCode();
1864:                // just enough fields for a reasonable distribution
1865:            }
1866:
1867:            /**
1868:             * Compares the given object with this <code>SimpleDateFormat</code> for
1869:             * equality.
1870:             *
1871:             * @return true if the given object is equal to this
1872:             * <code>SimpleDateFormat</code>
1873:             */
1874:            public boolean equals(Object obj) {
1875:                if (!super .equals(obj))
1876:                    return false; // super does class check
1877:                SimpleDateFormat that = (SimpleDateFormat) obj;
1878:                return (pattern.equals(that.pattern) && formatData
1879:                        .equals(that.formatData));
1880:            }
1881:
1882:            /**
1883:             * After reading an object from the input stream, the format
1884:             * pattern in the object is verified.
1885:             * <p>
1886:             * @exception InvalidObjectException if the pattern is invalid
1887:             */
1888:            private void readObject(ObjectInputStream stream)
1889:                    throws IOException, ClassNotFoundException {
1890:                stream.defaultReadObject();
1891:
1892:                try {
1893:                    compiledPattern = compile(pattern);
1894:                } catch (Exception e) {
1895:                    throw new InvalidObjectException("invalid pattern");
1896:                }
1897:
1898:                if (serialVersionOnStream < 1) {
1899:                    // didn't have defaultCenturyStart field
1900:                    initializeDefaultCentury();
1901:                } else {
1902:                    // fill in dependent transient field
1903:                    parseAmbiguousDatesAsAfter(defaultCenturyStart);
1904:                }
1905:                serialVersionOnStream = currentSerialVersion;
1906:
1907:                // If the deserialized object has a SimpleTimeZone, try
1908:                // to replace it with a ZoneInfo equivalent in order to
1909:                // be compatible with the SimpleTimeZone-based
1910:                // implementation as much as possible.
1911:                TimeZone tz = getTimeZone();
1912:                if (tz instanceof  SimpleTimeZone) {
1913:                    String id = tz.getID();
1914:                    TimeZone zi = TimeZone.getTimeZone(id);
1915:                    if (zi != null && zi.hasSameRules(tz)
1916:                            && zi.getID().equals(id)) {
1917:                        setTimeZone(zi);
1918:                    }
1919:                }
1920:            }
1921:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.