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

Home
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
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » text » java.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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