SimpleDateFormat.java revision 12745:f068a4ffddd2
1/*
2 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved
29 *
30 *   The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 *   Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.io.IOException;
42import java.io.InvalidObjectException;
43import java.io.ObjectInputStream;
44import static java.text.DateFormatSymbols.*;
45import java.util.Calendar;
46import java.util.Date;
47import java.util.GregorianCalendar;
48import java.util.Locale;
49import java.util.Map;
50import java.util.SimpleTimeZone;
51import java.util.SortedMap;
52import java.util.TimeZone;
53import java.util.concurrent.ConcurrentHashMap;
54import java.util.concurrent.ConcurrentMap;
55import sun.util.calendar.CalendarUtils;
56import sun.util.calendar.ZoneInfoFile;
57import sun.util.locale.provider.LocaleProviderAdapter;
58
59/**
60 * <code>SimpleDateFormat</code> is a concrete class for formatting and
61 * parsing dates in a locale-sensitive manner. It allows for formatting
62 * (date &rarr; text), parsing (text &rarr; date), and normalization.
63 *
64 * <p>
65 * <code>SimpleDateFormat</code> allows you to start by choosing
66 * any user-defined patterns for date-time formatting. However, you
67 * are encouraged to create a date-time formatter with either
68 * <code>getTimeInstance</code>, <code>getDateInstance</code>, or
69 * <code>getDateTimeInstance</code> in <code>DateFormat</code>. Each
70 * of these class methods can return a date/time formatter initialized
71 * with a default format pattern. You may modify the format pattern
72 * using the <code>applyPattern</code> methods as desired.
73 * For more information on using these methods, see
74 * {@link DateFormat}.
75 *
76 * <h3>Date and Time Patterns</h3>
77 * <p>
78 * Date and time formats are specified by <em>date and time pattern</em>
79 * strings.
80 * Within date and time pattern strings, unquoted letters from
81 * <code>'A'</code> to <code>'Z'</code> and from <code>'a'</code> to
82 * <code>'z'</code> are interpreted as pattern letters representing the
83 * components of a date or time string.
84 * Text can be quoted using single quotes (<code>'</code>) to avoid
85 * interpretation.
86 * <code>"''"</code> represents a single quote.
87 * All other characters are not interpreted; they're simply copied into the
88 * output string during formatting or matched against the input string
89 * during parsing.
90 * <p>
91 * The following pattern letters are defined (all other characters from
92 * <code>'A'</code> to <code>'Z'</code> and from <code>'a'</code> to
93 * <code>'z'</code> are reserved):
94 * <blockquote>
95 * <table border=0 cellspacing=3 cellpadding=0 summary="Chart shows pattern letters, date/time component, presentation, and examples.">
96 *     <tr style="background-color: rgb(204, 204, 255);">
97 *         <th align=left>Letter
98 *         <th align=left>Date or Time Component
99 *         <th align=left>Presentation
100 *         <th align=left>Examples
101 *     <tr>
102 *         <td><code>G</code>
103 *         <td>Era designator
104 *         <td><a href="#text">Text</a>
105 *         <td><code>AD</code>
106 *     <tr style="background-color: rgb(238, 238, 255);">
107 *         <td><code>y</code>
108 *         <td>Year
109 *         <td><a href="#year">Year</a>
110 *         <td><code>1996</code>; <code>96</code>
111 *     <tr>
112 *         <td><code>Y</code>
113 *         <td>Week year
114 *         <td><a href="#year">Year</a>
115 *         <td><code>2009</code>; <code>09</code>
116 *     <tr style="background-color: rgb(238, 238, 255);">
117 *         <td><code>M</code>
118 *         <td>Month in year (context sensitive)
119 *         <td><a href="#month">Month</a>
120 *         <td><code>July</code>; <code>Jul</code>; <code>07</code>
121 *     <tr>
122 *         <td><code>L</code>
123 *         <td>Month in year (standalone form)
124 *         <td><a href="#month">Month</a>
125 *         <td><code>July</code>; <code>Jul</code>; <code>07</code>
126 *     <tr style="background-color: rgb(238, 238, 255);">
127 *         <td><code>w</code>
128 *         <td>Week in year
129 *         <td><a href="#number">Number</a>
130 *         <td><code>27</code>
131 *     <tr>
132 *         <td><code>W</code>
133 *         <td>Week in month
134 *         <td><a href="#number">Number</a>
135 *         <td><code>2</code>
136 *     <tr style="background-color: rgb(238, 238, 255);">
137 *         <td><code>D</code>
138 *         <td>Day in year
139 *         <td><a href="#number">Number</a>
140 *         <td><code>189</code>
141 *     <tr>
142 *         <td><code>d</code>
143 *         <td>Day in month
144 *         <td><a href="#number">Number</a>
145 *         <td><code>10</code>
146 *     <tr style="background-color: rgb(238, 238, 255);">
147 *         <td><code>F</code>
148 *         <td>Day of week in month
149 *         <td><a href="#number">Number</a>
150 *         <td><code>2</code>
151 *     <tr>
152 *         <td><code>E</code>
153 *         <td>Day name in week
154 *         <td><a href="#text">Text</a>
155 *         <td><code>Tuesday</code>; <code>Tue</code>
156 *     <tr style="background-color: rgb(238, 238, 255);">
157 *         <td><code>u</code>
158 *         <td>Day number of week (1 = Monday, ..., 7 = Sunday)
159 *         <td><a href="#number">Number</a>
160 *         <td><code>1</code>
161 *     <tr>
162 *         <td><code>a</code>
163 *         <td>Am/pm marker
164 *         <td><a href="#text">Text</a>
165 *         <td><code>PM</code>
166 *     <tr style="background-color: rgb(238, 238, 255);">
167 *         <td><code>H</code>
168 *         <td>Hour in day (0-23)
169 *         <td><a href="#number">Number</a>
170 *         <td><code>0</code>
171 *     <tr>
172 *         <td><code>k</code>
173 *         <td>Hour in day (1-24)
174 *         <td><a href="#number">Number</a>
175 *         <td><code>24</code>
176 *     <tr style="background-color: rgb(238, 238, 255);">
177 *         <td><code>K</code>
178 *         <td>Hour in am/pm (0-11)
179 *         <td><a href="#number">Number</a>
180 *         <td><code>0</code>
181 *     <tr>
182 *         <td><code>h</code>
183 *         <td>Hour in am/pm (1-12)
184 *         <td><a href="#number">Number</a>
185 *         <td><code>12</code>
186 *     <tr style="background-color: rgb(238, 238, 255);">
187 *         <td><code>m</code>
188 *         <td>Minute in hour
189 *         <td><a href="#number">Number</a>
190 *         <td><code>30</code>
191 *     <tr>
192 *         <td><code>s</code>
193 *         <td>Second in minute
194 *         <td><a href="#number">Number</a>
195 *         <td><code>55</code>
196 *     <tr style="background-color: rgb(238, 238, 255);">
197 *         <td><code>S</code>
198 *         <td>Millisecond
199 *         <td><a href="#number">Number</a>
200 *         <td><code>978</code>
201 *     <tr>
202 *         <td><code>z</code>
203 *         <td>Time zone
204 *         <td><a href="#timezone">General time zone</a>
205 *         <td><code>Pacific Standard Time</code>; <code>PST</code>; <code>GMT-08:00</code>
206 *     <tr style="background-color: rgb(238, 238, 255);">
207 *         <td><code>Z</code>
208 *         <td>Time zone
209 *         <td><a href="#rfc822timezone">RFC 822 time zone</a>
210 *         <td><code>-0800</code>
211 *     <tr>
212 *         <td><code>X</code>
213 *         <td>Time zone
214 *         <td><a href="#iso8601timezone">ISO 8601 time zone</a>
215 *         <td><code>-08</code>; <code>-0800</code>;  <code>-08:00</code>
216 * </table>
217 * </blockquote>
218 * Pattern letters are usually repeated, as their number determines the
219 * exact presentation:
220 * <ul>
221 * <li><strong><a name="text">Text:</a></strong>
222 *     For formatting, if the number of pattern letters is 4 or more,
223 *     the full form is used; otherwise a short or abbreviated form
224 *     is used if available.
225 *     For parsing, both forms are accepted, independent of the number
226 *     of pattern letters.<br><br></li>
227 * <li><strong><a name="number">Number:</a></strong>
228 *     For formatting, the number of pattern letters is the minimum
229 *     number of digits, and shorter numbers are zero-padded to this amount.
230 *     For parsing, the number of pattern letters is ignored unless
231 *     it's needed to separate two adjacent fields.<br><br></li>
232 * <li><strong><a name="year">Year:</a></strong>
233 *     If the formatter's {@link #getCalendar() Calendar} is the Gregorian
234 *     calendar, the following rules are applied.<br>
235 *     <ul>
236 *     <li>For formatting, if the number of pattern letters is 2, the year
237 *         is truncated to 2 digits; otherwise it is interpreted as a
238 *         <a href="#number">number</a>.
239 *     <li>For parsing, if the number of pattern letters is more than 2,
240 *         the year is interpreted literally, regardless of the number of
241 *         digits. So using the pattern "MM/dd/yyyy", "01/11/12" parses to
242 *         Jan 11, 12 A.D.
243 *     <li>For parsing with the abbreviated year pattern ("y" or "yy"),
244 *         <code>SimpleDateFormat</code> must interpret the abbreviated year
245 *         relative to some century.  It does this by adjusting dates to be
246 *         within 80 years before and 20 years after the time the <code>SimpleDateFormat</code>
247 *         instance is created. For example, using a pattern of "MM/dd/yy" and a
248 *         <code>SimpleDateFormat</code> instance created on Jan 1, 1997,  the string
249 *         "01/11/12" would be interpreted as Jan 11, 2012 while the string "05/04/64"
250 *         would be interpreted as May 4, 1964.
251 *         During parsing, only strings consisting of exactly two digits, as defined by
252 *         {@link Character#isDigit(char)}, will be parsed into the default century.
253 *         Any other numeric string, such as a one digit string, a three or more digit
254 *         string, or a two digit string that isn't all digits (for example, "-1"), is
255 *         interpreted literally.  So "01/02/3" or "01/02/003" are parsed, using the
256 *         same pattern, as Jan 2, 3 AD.  Likewise, "01/02/-3" is parsed as Jan 2, 4 BC.
257 *     </ul>
258 *     Otherwise, calendar system specific forms are applied.
259 *     For both formatting and parsing, if the number of pattern
260 *     letters is 4 or more, a calendar specific {@linkplain
261 *     Calendar#LONG long form} is used. Otherwise, a calendar
262 *     specific {@linkplain Calendar#SHORT short or abbreviated form}
263 *     is used.<br>
264 *     <br>
265 *     If week year {@code 'Y'} is specified and the {@linkplain
266 *     #getCalendar() calendar} doesn't support any <a
267 *     href="../util/GregorianCalendar.html#week_year"> week
268 *     years</a>, the calendar year ({@code 'y'}) is used instead. The
269 *     support of week years can be tested with a call to {@link
270 *     DateFormat#getCalendar() getCalendar()}.{@link
271 *     java.util.Calendar#isWeekDateSupported()
272 *     isWeekDateSupported()}.<br><br></li>
273 * <li><strong><a name="month">Month:</a></strong>
274 *     If the number of pattern letters is 3 or more, the month is
275 *     interpreted as <a href="#text">text</a>; otherwise,
276 *     it is interpreted as a <a href="#number">number</a>.<br>
277 *     <ul>
278 *     <li>Letter <em>M</em> produces context-sensitive month names, such as the
279 *         embedded form of names. If a {@code DateFormatSymbols} has been set
280 *         explicitly with constructor {@link #SimpleDateFormat(String,
281 *         DateFormatSymbols)} or method {@link
282 *         #setDateFormatSymbols(DateFormatSymbols)}, the month names given by
283 *         the {@code DateFormatSymbols} are used.</li>
284 *     <li>Letter <em>L</em> produces the standalone form of month names.</li>
285 *     </ul>
286 *     <br></li>
287 * <li><strong><a name="timezone">General time zone:</a></strong>
288 *     Time zones are interpreted as <a href="#text">text</a> if they have
289 *     names. For time zones representing a GMT offset value, the
290 *     following syntax is used:
291 *     <pre>
292 *     <a name="GMTOffsetTimeZone"><i>GMTOffsetTimeZone:</i></a>
293 *             <code>GMT</code> <i>Sign</i> <i>Hours</i> <code>:</code> <i>Minutes</i>
294 *     <i>Sign:</i> one of
295 *             <code>+ -</code>
296 *     <i>Hours:</i>
297 *             <i>Digit</i>
298 *             <i>Digit</i> <i>Digit</i>
299 *     <i>Minutes:</i>
300 *             <i>Digit</i> <i>Digit</i>
301 *     <i>Digit:</i> one of
302 *             <code>0 1 2 3 4 5 6 7 8 9</code></pre>
303 *     <i>Hours</i> must be between 0 and 23, and <i>Minutes</i> must be between
304 *     00 and 59. The format is locale independent and digits must be taken
305 *     from the Basic Latin block of the Unicode standard.
306 *     <p>For parsing, <a href="#rfc822timezone">RFC 822 time zones</a> are also
307 *     accepted.<br><br></li>
308 * <li><strong><a name="rfc822timezone">RFC 822 time zone:</a></strong>
309 *     For formatting, the RFC 822 4-digit time zone format is used:
310 *
311 *     <pre>
312 *     <i>RFC822TimeZone:</i>
313 *             <i>Sign</i> <i>TwoDigitHours</i> <i>Minutes</i>
314 *     <i>TwoDigitHours:</i>
315 *             <i>Digit Digit</i></pre>
316 *     <i>TwoDigitHours</i> must be between 00 and 23. Other definitions
317 *     are as for <a href="#timezone">general time zones</a>.
318 *
319 *     <p>For parsing, <a href="#timezone">general time zones</a> are also
320 *     accepted.
321 * <li><strong><a name="iso8601timezone">ISO 8601 Time zone:</a></strong>
322 *     The number of pattern letters designates the format for both formatting
323 *     and parsing as follows:
324 *     <pre>
325 *     <i>ISO8601TimeZone:</i>
326 *             <i>OneLetterISO8601TimeZone</i>
327 *             <i>TwoLetterISO8601TimeZone</i>
328 *             <i>ThreeLetterISO8601TimeZone</i>
329 *     <i>OneLetterISO8601TimeZone:</i>
330 *             <i>Sign</i> <i>TwoDigitHours</i>
331 *             {@code Z}
332 *     <i>TwoLetterISO8601TimeZone:</i>
333 *             <i>Sign</i> <i>TwoDigitHours</i> <i>Minutes</i>
334 *             {@code Z}
335 *     <i>ThreeLetterISO8601TimeZone:</i>
336 *             <i>Sign</i> <i>TwoDigitHours</i> {@code :} <i>Minutes</i>
337 *             {@code Z}</pre>
338 *     Other definitions are as for <a href="#timezone">general time zones</a> or
339 *     <a href="#rfc822timezone">RFC 822 time zones</a>.
340 *
341 *     <p>For formatting, if the offset value from GMT is 0, {@code "Z"} is
342 *     produced. If the number of pattern letters is 1, any fraction of an hour
343 *     is ignored. For example, if the pattern is {@code "X"} and the time zone is
344 *     {@code "GMT+05:30"}, {@code "+05"} is produced.
345 *
346 *     <p>For parsing, {@code "Z"} is parsed as the UTC time zone designator.
347 *     <a href="#timezone">General time zones</a> are <em>not</em> accepted.
348 *
349 *     <p>If the number of pattern letters is 4 or more, {@link
350 *     IllegalArgumentException} is thrown when constructing a {@code
351 *     SimpleDateFormat} or {@linkplain #applyPattern(String) applying a
352 *     pattern}.
353 * </ul>
354 * <code>SimpleDateFormat</code> also supports <em>localized date and time
355 * pattern</em> strings. In these strings, the pattern letters described above
356 * may be replaced with other, locale dependent, pattern letters.
357 * <code>SimpleDateFormat</code> does not deal with the localization of text
358 * other than the pattern letters; that's up to the client of the class.
359 *
360 * <h4>Examples</h4>
361 *
362 * The following examples show how date and time patterns are interpreted in
363 * the U.S. locale. The given date and time are 2001-07-04 12:08:56 local time
364 * in the U.S. Pacific Time time zone.
365 * <blockquote>
366 * <table border=0 cellspacing=3 cellpadding=0 summary="Examples of date and time patterns interpreted in the U.S. locale">
367 *     <tr style="background-color: rgb(204, 204, 255);">
368 *         <th align=left>Date and Time Pattern
369 *         <th align=left>Result
370 *     <tr>
371 *         <td><code>"yyyy.MM.dd G 'at' HH:mm:ss z"</code>
372 *         <td><code>2001.07.04 AD at 12:08:56 PDT</code>
373 *     <tr style="background-color: rgb(238, 238, 255);">
374 *         <td><code>"EEE, MMM d, ''yy"</code>
375 *         <td><code>Wed, Jul 4, '01</code>
376 *     <tr>
377 *         <td><code>"h:mm a"</code>
378 *         <td><code>12:08 PM</code>
379 *     <tr style="background-color: rgb(238, 238, 255);">
380 *         <td><code>"hh 'o''clock' a, zzzz"</code>
381 *         <td><code>12 o'clock PM, Pacific Daylight Time</code>
382 *     <tr>
383 *         <td><code>"K:mm a, z"</code>
384 *         <td><code>0:08 PM, PDT</code>
385 *     <tr style="background-color: rgb(238, 238, 255);">
386 *         <td><code>"yyyyy.MMMMM.dd GGG hh:mm aaa"</code>
387 *         <td><code>02001.July.04 AD 12:08 PM</code>
388 *     <tr>
389 *         <td><code>"EEE, d MMM yyyy HH:mm:ss Z"</code>
390 *         <td><code>Wed, 4 Jul 2001 12:08:56 -0700</code>
391 *     <tr style="background-color: rgb(238, 238, 255);">
392 *         <td><code>"yyMMddHHmmssZ"</code>
393 *         <td><code>010704120856-0700</code>
394 *     <tr>
395 *         <td><code>"yyyy-MM-dd'T'HH:mm:ss.SSSZ"</code>
396 *         <td><code>2001-07-04T12:08:56.235-0700</code>
397 *     <tr style="background-color: rgb(238, 238, 255);">
398 *         <td><code>"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"</code>
399 *         <td><code>2001-07-04T12:08:56.235-07:00</code>
400 *     <tr>
401 *         <td><code>"YYYY-'W'ww-u"</code>
402 *         <td><code>2001-W27-3</code>
403 * </table>
404 * </blockquote>
405 *
406 * <h4><a name="synchronization">Synchronization</a></h4>
407 *
408 * <p>
409 * Date formats are not synchronized.
410 * It is recommended to create separate format instances for each thread.
411 * If multiple threads access a format concurrently, it must be synchronized
412 * externally.
413 *
414 * @see          <a href="http://docs.oracle.com/javase/tutorial/i18n/format/simpleDateFormat.html">Java Tutorial</a>
415 * @see          java.util.Calendar
416 * @see          java.util.TimeZone
417 * @see          DateFormat
418 * @see          DateFormatSymbols
419 * @author       Mark Davis, Chen-Lieh Huang, Alan Liu
420 */
421public class SimpleDateFormat extends DateFormat {
422
423    // the official serial version ID which says cryptically
424    // which version we're compatible with
425    static final long serialVersionUID = 4774881970558875024L;
426
427    // the internal serial version which says which version was written
428    // - 0 (default) for version up to JDK 1.1.3
429    // - 1 for version from JDK 1.1.4, which includes a new field
430    static final int currentSerialVersion = 1;
431
432    /**
433     * The version of the serialized data on the stream.  Possible values:
434     * <ul>
435     * <li><b>0</b> or not present on stream: JDK 1.1.3.  This version
436     * has no <code>defaultCenturyStart</code> on stream.
437     * <li><b>1</b> JDK 1.1.4 or later.  This version adds
438     * <code>defaultCenturyStart</code>.
439     * </ul>
440     * When streaming out this class, the most recent format
441     * and the highest allowable <code>serialVersionOnStream</code>
442     * is written.
443     * @serial
444     * @since 1.1.4
445     */
446    private int serialVersionOnStream = currentSerialVersion;
447
448    /**
449     * The pattern string of this formatter.  This is always a non-localized
450     * pattern.  May not be null.  See class documentation for details.
451     * @serial
452     */
453    private String pattern;
454
455    /**
456     * Saved numberFormat and pattern.
457     * @see SimpleDateFormat#checkNegativeNumberExpression
458     */
459    private transient NumberFormat originalNumberFormat;
460    private transient String originalNumberPattern;
461
462    /**
463     * The minus sign to be used with format and parse.
464     */
465    private transient char minusSign = '-';
466
467    /**
468     * True when a negative sign follows a number.
469     * (True as default in Arabic.)
470     */
471    private transient boolean hasFollowingMinusSign = false;
472
473    /**
474     * True if standalone form needs to be used.
475     */
476    private transient boolean forceStandaloneForm = false;
477
478    /**
479     * The compiled pattern.
480     */
481    private transient char[] compiledPattern;
482
483    /**
484     * Tags for the compiled pattern.
485     */
486    private static final int TAG_QUOTE_ASCII_CHAR       = 100;
487    private static final int TAG_QUOTE_CHARS            = 101;
488
489    /**
490     * Locale dependent digit zero.
491     * @see #zeroPaddingNumber
492     * @see java.text.DecimalFormatSymbols#getZeroDigit
493     */
494    private transient char zeroDigit;
495
496    /**
497     * The symbols used by this formatter for week names, month names,
498     * etc.  May not be null.
499     * @serial
500     * @see java.text.DateFormatSymbols
501     */
502    private DateFormatSymbols formatData;
503
504    /**
505     * We map dates with two-digit years into the century starting at
506     * <code>defaultCenturyStart</code>, which may be any date.  May
507     * not be null.
508     * @serial
509     * @since 1.1.4
510     */
511    private Date defaultCenturyStart;
512
513    private transient int defaultCenturyStartYear;
514
515    private static final int MILLIS_PER_MINUTE = 60 * 1000;
516
517    // For time zones that have no names, use strings GMT+minutes and
518    // GMT-minutes. For instance, in France the time zone is GMT+60.
519    private static final String GMT = "GMT";
520
521    /**
522     * Cache NumberFormat instances with Locale key.
523     */
524    private static final ConcurrentMap<Locale, NumberFormat> cachedNumberFormatData
525        = new ConcurrentHashMap<>(3);
526
527    /**
528     * The Locale used to instantiate this
529     * <code>SimpleDateFormat</code>. The value may be null if this object
530     * has been created by an older <code>SimpleDateFormat</code> and
531     * deserialized.
532     *
533     * @serial
534     * @since 1.6
535     */
536    private Locale locale;
537
538    /**
539     * Indicates whether this <code>SimpleDateFormat</code> should use
540     * the DateFormatSymbols. If true, the format and parse methods
541     * use the DateFormatSymbols values. If false, the format and
542     * parse methods call Calendar.getDisplayName or
543     * Calendar.getDisplayNames.
544     */
545    transient boolean useDateFormatSymbols;
546
547    /**
548     * Constructs a <code>SimpleDateFormat</code> using the default pattern and
549     * date format symbols for the default
550     * {@link java.util.Locale.Category#FORMAT FORMAT} locale.
551     * <b>Note:</b> This constructor may not support all locales.
552     * For full coverage, use the factory methods in the {@link DateFormat}
553     * class.
554     */
555    public SimpleDateFormat() {
556        this("", Locale.getDefault(Locale.Category.FORMAT));
557        applyPatternImpl(LocaleProviderAdapter.getResourceBundleBased().getLocaleResources(locale)
558                         .getDateTimePattern(SHORT, SHORT, calendar));
559    }
560
561    /**
562     * Constructs a <code>SimpleDateFormat</code> using the given pattern and
563     * the default date format symbols for the default
564     * {@link java.util.Locale.Category#FORMAT FORMAT} locale.
565     * <b>Note:</b> This constructor may not support all locales.
566     * For full coverage, use the factory methods in the {@link DateFormat}
567     * class.
568     * <p>This is equivalent to calling
569     * {@link #SimpleDateFormat(String, Locale)
570     *     SimpleDateFormat(pattern, Locale.getDefault(Locale.Category.FORMAT))}.
571     *
572     * @see java.util.Locale#getDefault(java.util.Locale.Category)
573     * @see java.util.Locale.Category#FORMAT
574     * @param pattern the pattern describing the date and time format
575     * @exception NullPointerException if the given pattern is null
576     * @exception IllegalArgumentException if the given pattern is invalid
577     */
578    public SimpleDateFormat(String pattern)
579    {
580        this(pattern, Locale.getDefault(Locale.Category.FORMAT));
581    }
582
583    /**
584     * Constructs a <code>SimpleDateFormat</code> using the given pattern and
585     * the default date format symbols for the given locale.
586     * <b>Note:</b> This constructor may not support all locales.
587     * For full coverage, use the factory methods in the {@link DateFormat}
588     * class.
589     *
590     * @param pattern the pattern describing the date and time format
591     * @param locale the locale whose date format symbols should be used
592     * @exception NullPointerException if the given pattern or locale is null
593     * @exception IllegalArgumentException if the given pattern is invalid
594     */
595    public SimpleDateFormat(String pattern, Locale locale)
596    {
597        if (pattern == null || locale == null) {
598            throw new NullPointerException();
599        }
600
601        initializeCalendar(locale);
602        this.pattern = pattern;
603        this.formatData = DateFormatSymbols.getInstanceRef(locale);
604        this.locale = locale;
605        initialize(locale);
606    }
607
608    /**
609     * Constructs a <code>SimpleDateFormat</code> using the given pattern and
610     * date format symbols.
611     *
612     * @param pattern the pattern describing the date and time format
613     * @param formatSymbols the date format symbols to be used for formatting
614     * @exception NullPointerException if the given pattern or formatSymbols is null
615     * @exception IllegalArgumentException if the given pattern is invalid
616     */
617    public SimpleDateFormat(String pattern, DateFormatSymbols formatSymbols)
618    {
619        if (pattern == null || formatSymbols == null) {
620            throw new NullPointerException();
621        }
622
623        this.pattern = pattern;
624        this.formatData = (DateFormatSymbols) formatSymbols.clone();
625        this.locale = Locale.getDefault(Locale.Category.FORMAT);
626        initializeCalendar(this.locale);
627        initialize(this.locale);
628        useDateFormatSymbols = true;
629    }
630
631    /* Initialize compiledPattern and numberFormat fields */
632    private void initialize(Locale loc) {
633        // Verify and compile the given pattern.
634        compiledPattern = compile(pattern);
635
636        /* try the cache first */
637        numberFormat = cachedNumberFormatData.get(loc);
638        if (numberFormat == null) { /* cache miss */
639            numberFormat = NumberFormat.getIntegerInstance(loc);
640            numberFormat.setGroupingUsed(false);
641
642            /* update cache */
643            cachedNumberFormatData.putIfAbsent(loc, numberFormat);
644        }
645        numberFormat = (NumberFormat) numberFormat.clone();
646
647        initializeDefaultCentury();
648    }
649
650    private void initializeCalendar(Locale loc) {
651        if (calendar == null) {
652            assert loc != null;
653            // The format object must be constructed using the symbols for this zone.
654            // However, the calendar should use the current default TimeZone.
655            // If this is not contained in the locale zone strings, then the zone
656            // will be formatted using generic GMT+/-H:MM nomenclature.
657            calendar = Calendar.getInstance(TimeZone.getDefault(), loc);
658        }
659    }
660
661    /**
662     * Returns the compiled form of the given pattern. The syntax of
663     * the compiled pattern is:
664     * <blockquote>
665     * CompiledPattern:
666     *     EntryList
667     * EntryList:
668     *     Entry
669     *     EntryList Entry
670     * Entry:
671     *     TagField
672     *     TagField data
673     * TagField:
674     *     Tag Length
675     *     TaggedData
676     * Tag:
677     *     pattern_char_index
678     *     TAG_QUOTE_CHARS
679     * Length:
680     *     short_length
681     *     long_length
682     * TaggedData:
683     *     TAG_QUOTE_ASCII_CHAR ascii_char
684     *
685     * </blockquote>
686     *
687     * where `short_length' is an 8-bit unsigned integer between 0 and
688     * 254.  `long_length' is a sequence of an 8-bit integer 255 and a
689     * 32-bit signed integer value which is split into upper and lower
690     * 16-bit fields in two char's. `pattern_char_index' is an 8-bit
691     * integer between 0 and 18. `ascii_char' is an 7-bit ASCII
692     * character value. `data' depends on its Tag value.
693     * <p>
694     * If Length is short_length, Tag and short_length are packed in a
695     * single char, as illustrated below.
696     * <blockquote>
697     *     char[0] = (Tag << 8) | short_length;
698     * </blockquote>
699     *
700     * If Length is long_length, Tag and 255 are packed in the first
701     * char and a 32-bit integer, as illustrated below.
702     * <blockquote>
703     *     char[0] = (Tag << 8) | 255;
704     *     char[1] = (char) (long_length >>> 16);
705     *     char[2] = (char) (long_length & 0xffff);
706     * </blockquote>
707     * <p>
708     * If Tag is a pattern_char_index, its Length is the number of
709     * pattern characters. For example, if the given pattern is
710     * "yyyy", Tag is 1 and Length is 4, followed by no data.
711     * <p>
712     * If Tag is TAG_QUOTE_CHARS, its Length is the number of char's
713     * following the TagField. For example, if the given pattern is
714     * "'o''clock'", Length is 7 followed by a char sequence of
715     * <code>o&nbs;'&nbs;c&nbs;l&nbs;o&nbs;c&nbs;k</code>.
716     * <p>
717     * TAG_QUOTE_ASCII_CHAR is a special tag and has an ASCII
718     * character in place of Length. For example, if the given pattern
719     * is "'o'", the TaggedData entry is
720     * <code>((TAG_QUOTE_ASCII_CHAR&nbs;<<&nbs;8)&nbs;|&nbs;'o')</code>.
721     *
722     * @exception NullPointerException if the given pattern is null
723     * @exception IllegalArgumentException if the given pattern is invalid
724     */
725    private char[] compile(String pattern) {
726        int length = pattern.length();
727        boolean inQuote = false;
728        StringBuilder compiledCode = new StringBuilder(length * 2);
729        StringBuilder tmpBuffer = null;
730        int count = 0, tagcount = 0;
731        int lastTag = -1, prevTag = -1;
732
733        for (int i = 0; i < length; i++) {
734            char c = pattern.charAt(i);
735
736            if (c == '\'') {
737                // '' is treated as a single quote regardless of being
738                // in a quoted section.
739                if ((i + 1) < length) {
740                    c = pattern.charAt(i + 1);
741                    if (c == '\'') {
742                        i++;
743                        if (count != 0) {
744                            encode(lastTag, count, compiledCode);
745                            tagcount++;
746                            prevTag = lastTag;
747                            lastTag = -1;
748                            count = 0;
749                        }
750                        if (inQuote) {
751                            tmpBuffer.append(c);
752                        } else {
753                            compiledCode.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
754                        }
755                        continue;
756                    }
757                }
758                if (!inQuote) {
759                    if (count != 0) {
760                        encode(lastTag, count, compiledCode);
761                        tagcount++;
762                        prevTag = lastTag;
763                        lastTag = -1;
764                        count = 0;
765                    }
766                    if (tmpBuffer == null) {
767                        tmpBuffer = new StringBuilder(length);
768                    } else {
769                        tmpBuffer.setLength(0);
770                    }
771                    inQuote = true;
772                } else {
773                    int len = tmpBuffer.length();
774                    if (len == 1) {
775                        char ch = tmpBuffer.charAt(0);
776                        if (ch < 128) {
777                            compiledCode.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | ch));
778                        } else {
779                            compiledCode.append((char)(TAG_QUOTE_CHARS << 8 | 1));
780                            compiledCode.append(ch);
781                        }
782                    } else {
783                        encode(TAG_QUOTE_CHARS, len, compiledCode);
784                        compiledCode.append(tmpBuffer);
785                    }
786                    inQuote = false;
787                }
788                continue;
789            }
790            if (inQuote) {
791                tmpBuffer.append(c);
792                continue;
793            }
794            if (!(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) {
795                if (count != 0) {
796                    encode(lastTag, count, compiledCode);
797                    tagcount++;
798                    prevTag = lastTag;
799                    lastTag = -1;
800                    count = 0;
801                }
802                if (c < 128) {
803                    // In most cases, c would be a delimiter, such as ':'.
804                    compiledCode.append((char)(TAG_QUOTE_ASCII_CHAR << 8 | c));
805                } else {
806                    // Take any contiguous non-ASCII alphabet characters and
807                    // put them in a single TAG_QUOTE_CHARS.
808                    int j;
809                    for (j = i + 1; j < length; j++) {
810                        char d = pattern.charAt(j);
811                        if (d == '\'' || (d >= 'a' && d <= 'z' || d >= 'A' && d <= 'Z')) {
812                            break;
813                        }
814                    }
815                    compiledCode.append((char)(TAG_QUOTE_CHARS << 8 | (j - i)));
816                    for (; i < j; i++) {
817                        compiledCode.append(pattern.charAt(i));
818                    }
819                    i--;
820                }
821                continue;
822            }
823
824            int tag;
825            if ((tag = DateFormatSymbols.patternChars.indexOf(c)) == -1) {
826                throw new IllegalArgumentException("Illegal pattern character " +
827                                                   "'" + c + "'");
828            }
829            if (lastTag == -1 || lastTag == tag) {
830                lastTag = tag;
831                count++;
832                continue;
833            }
834            encode(lastTag, count, compiledCode);
835            tagcount++;
836            prevTag = lastTag;
837            lastTag = tag;
838            count = 1;
839        }
840
841        if (inQuote) {
842            throw new IllegalArgumentException("Unterminated quote");
843        }
844
845        if (count != 0) {
846            encode(lastTag, count, compiledCode);
847            tagcount++;
848            prevTag = lastTag;
849        }
850
851        forceStandaloneForm = (tagcount == 1 && prevTag == PATTERN_MONTH);
852
853        // Copy the compiled pattern to a char array
854        int len = compiledCode.length();
855        char[] r = new char[len];
856        compiledCode.getChars(0, len, r, 0);
857        return r;
858    }
859
860    /**
861     * Encodes the given tag and length and puts encoded char(s) into buffer.
862     */
863    private static void encode(int tag, int length, StringBuilder buffer) {
864        if (tag == PATTERN_ISO_ZONE && length >= 4) {
865            throw new IllegalArgumentException("invalid ISO 8601 format: length=" + length);
866        }
867        if (length < 255) {
868            buffer.append((char)(tag << 8 | length));
869        } else {
870            buffer.append((char)((tag << 8) | 0xff));
871            buffer.append((char)(length >>> 16));
872            buffer.append((char)(length & 0xffff));
873        }
874    }
875
876    /* Initialize the fields we use to disambiguate ambiguous years. Separate
877     * so we can call it from readObject().
878     */
879    private void initializeDefaultCentury() {
880        calendar.setTimeInMillis(System.currentTimeMillis());
881        calendar.add( Calendar.YEAR, -80 );
882        parseAmbiguousDatesAsAfter(calendar.getTime());
883    }
884
885    /* Define one-century window into which to disambiguate dates using
886     * two-digit years.
887     */
888    private void parseAmbiguousDatesAsAfter(Date startDate) {
889        defaultCenturyStart = startDate;
890        calendar.setTime(startDate);
891        defaultCenturyStartYear = calendar.get(Calendar.YEAR);
892    }
893
894    /**
895     * Sets the 100-year period 2-digit years will be interpreted as being in
896     * to begin on the date the user specifies.
897     *
898     * @param startDate During parsing, two digit years will be placed in the range
899     * <code>startDate</code> to <code>startDate + 100 years</code>.
900     * @see #get2DigitYearStart
901     * @since 1.2
902     */
903    public void set2DigitYearStart(Date startDate) {
904        parseAmbiguousDatesAsAfter(new Date(startDate.getTime()));
905    }
906
907    /**
908     * Returns the beginning date of the 100-year period 2-digit years are interpreted
909     * as being within.
910     *
911     * @return the start of the 100-year period into which two digit years are
912     * parsed
913     * @see #set2DigitYearStart
914     * @since 1.2
915     */
916    public Date get2DigitYearStart() {
917        return (Date) defaultCenturyStart.clone();
918    }
919
920    /**
921     * Formats the given <code>Date</code> into a date/time string and appends
922     * the result to the given <code>StringBuffer</code>.
923     *
924     * @param date the date-time value to be formatted into a date-time string.
925     * @param toAppendTo where the new date-time text is to be appended.
926     * @param pos the formatting position. On input: an alignment field,
927     * if desired. On output: the offsets of the alignment field.
928     * @return the formatted date-time string.
929     * @exception NullPointerException if the given {@code date} is {@code null}.
930     */
931    @Override
932    public StringBuffer format(Date date, StringBuffer toAppendTo,
933                               FieldPosition pos)
934    {
935        pos.beginIndex = pos.endIndex = 0;
936        return format(date, toAppendTo, pos.getFieldDelegate());
937    }
938
939    // Called from Format after creating a FieldDelegate
940    private StringBuffer format(Date date, StringBuffer toAppendTo,
941                                FieldDelegate delegate) {
942        // Convert input date to time field list
943        calendar.setTime(date);
944
945        boolean useDateFormatSymbols = useDateFormatSymbols();
946
947        for (int i = 0; i < compiledPattern.length; ) {
948            int tag = compiledPattern[i] >>> 8;
949            int count = compiledPattern[i++] & 0xff;
950            if (count == 255) {
951                count = compiledPattern[i++] << 16;
952                count |= compiledPattern[i++];
953            }
954
955            switch (tag) {
956            case TAG_QUOTE_ASCII_CHAR:
957                toAppendTo.append((char)count);
958                break;
959
960            case TAG_QUOTE_CHARS:
961                toAppendTo.append(compiledPattern, i, count);
962                i += count;
963                break;
964
965            default:
966                subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
967                break;
968            }
969        }
970        return toAppendTo;
971    }
972
973    /**
974     * Formats an Object producing an <code>AttributedCharacterIterator</code>.
975     * You can use the returned <code>AttributedCharacterIterator</code>
976     * to build the resulting String, as well as to determine information
977     * about the resulting String.
978     * <p>
979     * Each attribute key of the AttributedCharacterIterator will be of type
980     * <code>DateFormat.Field</code>, with the corresponding attribute value
981     * being the same as the attribute key.
982     *
983     * @exception NullPointerException if obj is null.
984     * @exception IllegalArgumentException if the Format cannot format the
985     *            given object, or if the Format's pattern string is invalid.
986     * @param obj The object to format
987     * @return AttributedCharacterIterator describing the formatted value.
988     * @since 1.4
989     */
990    @Override
991    public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
992        StringBuffer sb = new StringBuffer();
993        CharacterIteratorFieldDelegate delegate = new
994                         CharacterIteratorFieldDelegate();
995
996        if (obj instanceof Date) {
997            format((Date)obj, sb, delegate);
998        }
999        else if (obj instanceof Number) {
1000            format(new Date(((Number)obj).longValue()), sb, delegate);
1001        }
1002        else if (obj == null) {
1003            throw new NullPointerException(
1004                   "formatToCharacterIterator must be passed non-null object");
1005        }
1006        else {
1007            throw new IllegalArgumentException(
1008                             "Cannot format given Object as a Date");
1009        }
1010        return delegate.getIterator(sb.toString());
1011    }
1012
1013    // Map index into pattern character string to Calendar field number
1014    private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD = {
1015        Calendar.ERA,
1016        Calendar.YEAR,
1017        Calendar.MONTH,
1018        Calendar.DATE,
1019        Calendar.HOUR_OF_DAY,
1020        Calendar.HOUR_OF_DAY,
1021        Calendar.MINUTE,
1022        Calendar.SECOND,
1023        Calendar.MILLISECOND,
1024        Calendar.DAY_OF_WEEK,
1025        Calendar.DAY_OF_YEAR,
1026        Calendar.DAY_OF_WEEK_IN_MONTH,
1027        Calendar.WEEK_OF_YEAR,
1028        Calendar.WEEK_OF_MONTH,
1029        Calendar.AM_PM,
1030        Calendar.HOUR,
1031        Calendar.HOUR,
1032        Calendar.ZONE_OFFSET,
1033        Calendar.ZONE_OFFSET,
1034        CalendarBuilder.WEEK_YEAR,         // Pseudo Calendar field
1035        CalendarBuilder.ISO_DAY_OF_WEEK,   // Pseudo Calendar field
1036        Calendar.ZONE_OFFSET,
1037        Calendar.MONTH
1038    };
1039
1040    // Map index into pattern character string to DateFormat field number
1041    private static final int[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD = {
1042        DateFormat.ERA_FIELD,
1043        DateFormat.YEAR_FIELD,
1044        DateFormat.MONTH_FIELD,
1045        DateFormat.DATE_FIELD,
1046        DateFormat.HOUR_OF_DAY1_FIELD,
1047        DateFormat.HOUR_OF_DAY0_FIELD,
1048        DateFormat.MINUTE_FIELD,
1049        DateFormat.SECOND_FIELD,
1050        DateFormat.MILLISECOND_FIELD,
1051        DateFormat.DAY_OF_WEEK_FIELD,
1052        DateFormat.DAY_OF_YEAR_FIELD,
1053        DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD,
1054        DateFormat.WEEK_OF_YEAR_FIELD,
1055        DateFormat.WEEK_OF_MONTH_FIELD,
1056        DateFormat.AM_PM_FIELD,
1057        DateFormat.HOUR1_FIELD,
1058        DateFormat.HOUR0_FIELD,
1059        DateFormat.TIMEZONE_FIELD,
1060        DateFormat.TIMEZONE_FIELD,
1061        DateFormat.YEAR_FIELD,
1062        DateFormat.DAY_OF_WEEK_FIELD,
1063        DateFormat.TIMEZONE_FIELD,
1064        DateFormat.MONTH_FIELD
1065    };
1066
1067    // Maps from DecimalFormatSymbols index to Field constant
1068    private static final Field[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID = {
1069        Field.ERA,
1070        Field.YEAR,
1071        Field.MONTH,
1072        Field.DAY_OF_MONTH,
1073        Field.HOUR_OF_DAY1,
1074        Field.HOUR_OF_DAY0,
1075        Field.MINUTE,
1076        Field.SECOND,
1077        Field.MILLISECOND,
1078        Field.DAY_OF_WEEK,
1079        Field.DAY_OF_YEAR,
1080        Field.DAY_OF_WEEK_IN_MONTH,
1081        Field.WEEK_OF_YEAR,
1082        Field.WEEK_OF_MONTH,
1083        Field.AM_PM,
1084        Field.HOUR1,
1085        Field.HOUR0,
1086        Field.TIME_ZONE,
1087        Field.TIME_ZONE,
1088        Field.YEAR,
1089        Field.DAY_OF_WEEK,
1090        Field.TIME_ZONE,
1091        Field.MONTH
1092    };
1093
1094    /**
1095     * Private member function that does the real date/time formatting.
1096     */
1097    private void subFormat(int patternCharIndex, int count,
1098                           FieldDelegate delegate, StringBuffer buffer,
1099                           boolean useDateFormatSymbols)
1100    {
1101        int     maxIntCount = Integer.MAX_VALUE;
1102        String  current = null;
1103        int     beginOffset = buffer.length();
1104
1105        int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
1106        int value;
1107        if (field == CalendarBuilder.WEEK_YEAR) {
1108            if (calendar.isWeekDateSupported()) {
1109                value = calendar.getWeekYear();
1110            } else {
1111                // use calendar year 'y' instead
1112                patternCharIndex = PATTERN_YEAR;
1113                field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
1114                value = calendar.get(field);
1115            }
1116        } else if (field == CalendarBuilder.ISO_DAY_OF_WEEK) {
1117            value = CalendarBuilder.toISODayOfWeek(calendar.get(Calendar.DAY_OF_WEEK));
1118        } else {
1119            value = calendar.get(field);
1120        }
1121
1122        int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT;
1123        if (!useDateFormatSymbols && field < Calendar.ZONE_OFFSET
1124            && patternCharIndex != PATTERN_MONTH_STANDALONE) {
1125            current = calendar.getDisplayName(field, style, locale);
1126        }
1127
1128        // Note: zeroPaddingNumber() assumes that maxDigits is either
1129        // 2 or maxIntCount. If we make any changes to this,
1130        // zeroPaddingNumber() must be fixed.
1131
1132        switch (patternCharIndex) {
1133        case PATTERN_ERA: // 'G'
1134            if (useDateFormatSymbols) {
1135                String[] eras = formatData.getEras();
1136                if (value < eras.length) {
1137                    current = eras[value];
1138                }
1139            }
1140            if (current == null) {
1141                current = "";
1142            }
1143            break;
1144
1145        case PATTERN_WEEK_YEAR: // 'Y'
1146        case PATTERN_YEAR:      // 'y'
1147            if (calendar instanceof GregorianCalendar) {
1148                if (count != 2) {
1149                    zeroPaddingNumber(value, count, maxIntCount, buffer);
1150                } else {
1151                    zeroPaddingNumber(value, 2, 2, buffer);
1152                } // clip 1996 to 96
1153            } else {
1154                if (current == null) {
1155                    zeroPaddingNumber(value, style == Calendar.LONG ? 1 : count,
1156                                      maxIntCount, buffer);
1157                }
1158            }
1159            break;
1160
1161        case PATTERN_MONTH:            // 'M' (context seinsive)
1162            if (useDateFormatSymbols) {
1163                String[] months;
1164                if (count >= 4) {
1165                    months = formatData.getMonths();
1166                    current = months[value];
1167                } else if (count == 3) {
1168                    months = formatData.getShortMonths();
1169                    current = months[value];
1170                }
1171            } else {
1172                if (count < 3) {
1173                    current = null;
1174                } else if (forceStandaloneForm) {
1175                    current = calendar.getDisplayName(field, style | 0x8000, locale);
1176                    if (current == null) {
1177                        current = calendar.getDisplayName(field, style, locale);
1178                    }
1179                }
1180            }
1181            if (current == null) {
1182                zeroPaddingNumber(value+1, count, maxIntCount, buffer);
1183            }
1184            break;
1185
1186        case PATTERN_MONTH_STANDALONE: // 'L'
1187            assert current == null;
1188            if (locale == null) {
1189                String[] months;
1190                if (count >= 4) {
1191                    months = formatData.getMonths();
1192                    current = months[value];
1193                } else if (count == 3) {
1194                    months = formatData.getShortMonths();
1195                    current = months[value];
1196                }
1197            } else {
1198                if (count >= 3) {
1199                    current = calendar.getDisplayName(field, style | 0x8000, locale);
1200                }
1201            }
1202            if (current == null) {
1203                zeroPaddingNumber(value+1, count, maxIntCount, buffer);
1204            }
1205            break;
1206
1207        case PATTERN_HOUR_OF_DAY1: // 'k' 1-based.  eg, 23:59 + 1 hour =>> 24:59
1208            if (current == null) {
1209                if (value == 0) {
1210                    zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1,
1211                                      count, maxIntCount, buffer);
1212                } else {
1213                    zeroPaddingNumber(value, count, maxIntCount, buffer);
1214                }
1215            }
1216            break;
1217
1218        case PATTERN_DAY_OF_WEEK: // 'E'
1219            if (useDateFormatSymbols) {
1220                String[] weekdays;
1221                if (count >= 4) {
1222                    weekdays = formatData.getWeekdays();
1223                    current = weekdays[value];
1224                } else { // count < 4, use abbreviated form if exists
1225                    weekdays = formatData.getShortWeekdays();
1226                    current = weekdays[value];
1227                }
1228            }
1229            break;
1230
1231        case PATTERN_AM_PM:    // 'a'
1232            if (useDateFormatSymbols) {
1233                String[] ampm = formatData.getAmPmStrings();
1234                current = ampm[value];
1235            }
1236            break;
1237
1238        case PATTERN_HOUR1:    // 'h' 1-based.  eg, 11PM + 1 hour =>> 12 AM
1239            if (current == null) {
1240                if (value == 0) {
1241                    zeroPaddingNumber(calendar.getLeastMaximum(Calendar.HOUR) + 1,
1242                                      count, maxIntCount, buffer);
1243                } else {
1244                    zeroPaddingNumber(value, count, maxIntCount, buffer);
1245                }
1246            }
1247            break;
1248
1249        case PATTERN_ZONE_NAME: // 'z'
1250            if (current == null) {
1251                if (formatData.locale == null || formatData.isZoneStringsSet) {
1252                    int zoneIndex =
1253                        formatData.getZoneIndex(calendar.getTimeZone().getID());
1254                    if (zoneIndex == -1) {
1255                        value = calendar.get(Calendar.ZONE_OFFSET) +
1256                            calendar.get(Calendar.DST_OFFSET);
1257                        buffer.append(ZoneInfoFile.toCustomID(value));
1258                    } else {
1259                        int index = (calendar.get(Calendar.DST_OFFSET) == 0) ? 1: 3;
1260                        if (count < 4) {
1261                            // Use the short name
1262                            index++;
1263                        }
1264                        String[][] zoneStrings = formatData.getZoneStringsWrapper();
1265                        buffer.append(zoneStrings[zoneIndex][index]);
1266                    }
1267                } else {
1268                    TimeZone tz = calendar.getTimeZone();
1269                    boolean daylight = (calendar.get(Calendar.DST_OFFSET) != 0);
1270                    int tzstyle = (count < 4 ? TimeZone.SHORT : TimeZone.LONG);
1271                    buffer.append(tz.getDisplayName(daylight, tzstyle, formatData.locale));
1272                }
1273            }
1274            break;
1275
1276        case PATTERN_ZONE_VALUE: // 'Z' ("-/+hhmm" form)
1277            value = (calendar.get(Calendar.ZONE_OFFSET) +
1278                     calendar.get(Calendar.DST_OFFSET)) / 60000;
1279
1280            int width = 4;
1281            if (value >= 0) {
1282                buffer.append('+');
1283            } else {
1284                width++;
1285            }
1286
1287            int num = (value / 60) * 100 + (value % 60);
1288            CalendarUtils.sprintf0d(buffer, num, width);
1289            break;
1290
1291        case PATTERN_ISO_ZONE:   // 'X'
1292            value = calendar.get(Calendar.ZONE_OFFSET)
1293                    + calendar.get(Calendar.DST_OFFSET);
1294
1295            if (value == 0) {
1296                buffer.append('Z');
1297                break;
1298            }
1299
1300            value /=  60000;
1301            if (value >= 0) {
1302                buffer.append('+');
1303            } else {
1304                buffer.append('-');
1305                value = -value;
1306            }
1307
1308            CalendarUtils.sprintf0d(buffer, value / 60, 2);
1309            if (count == 1) {
1310                break;
1311            }
1312
1313            if (count == 3) {
1314                buffer.append(':');
1315            }
1316            CalendarUtils.sprintf0d(buffer, value % 60, 2);
1317            break;
1318
1319        default:
1320     // case PATTERN_DAY_OF_MONTH:         // 'd'
1321     // case PATTERN_HOUR_OF_DAY0:         // 'H' 0-based.  eg, 23:59 + 1 hour =>> 00:59
1322     // case PATTERN_MINUTE:               // 'm'
1323     // case PATTERN_SECOND:               // 's'
1324     // case PATTERN_MILLISECOND:          // 'S'
1325     // case PATTERN_DAY_OF_YEAR:          // 'D'
1326     // case PATTERN_DAY_OF_WEEK_IN_MONTH: // 'F'
1327     // case PATTERN_WEEK_OF_YEAR:         // 'w'
1328     // case PATTERN_WEEK_OF_MONTH:        // 'W'
1329     // case PATTERN_HOUR0:                // 'K' eg, 11PM + 1 hour =>> 0 AM
1330     // case PATTERN_ISO_DAY_OF_WEEK:      // 'u' pseudo field, Monday = 1, ..., Sunday = 7
1331            if (current == null) {
1332                zeroPaddingNumber(value, count, maxIntCount, buffer);
1333            }
1334            break;
1335        } // switch (patternCharIndex)
1336
1337        if (current != null) {
1338            buffer.append(current);
1339        }
1340
1341        int fieldID = PATTERN_INDEX_TO_DATE_FORMAT_FIELD[patternCharIndex];
1342        Field f = PATTERN_INDEX_TO_DATE_FORMAT_FIELD_ID[patternCharIndex];
1343
1344        delegate.formatted(fieldID, f, f, beginOffset, buffer.length(), buffer);
1345    }
1346
1347    /**
1348     * Formats a number with the specified minimum and maximum number of digits.
1349     */
1350    private void zeroPaddingNumber(int value, int minDigits, int maxDigits, StringBuffer buffer)
1351    {
1352        // Optimization for 1, 2 and 4 digit numbers. This should
1353        // cover most cases of formatting date/time related items.
1354        // Note: This optimization code assumes that maxDigits is
1355        // either 2 or Integer.MAX_VALUE (maxIntCount in format()).
1356        try {
1357            if (zeroDigit == 0) {
1358                zeroDigit = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getZeroDigit();
1359            }
1360            if (value >= 0) {
1361                if (value < 100 && minDigits >= 1 && minDigits <= 2) {
1362                    if (value < 10) {
1363                        if (minDigits == 2) {
1364                            buffer.append(zeroDigit);
1365                        }
1366                        buffer.append((char)(zeroDigit + value));
1367                    } else {
1368                        buffer.append((char)(zeroDigit + value / 10));
1369                        buffer.append((char)(zeroDigit + value % 10));
1370                    }
1371                    return;
1372                } else if (value >= 1000 && value < 10000) {
1373                    if (minDigits == 4) {
1374                        buffer.append((char)(zeroDigit + value / 1000));
1375                        value %= 1000;
1376                        buffer.append((char)(zeroDigit + value / 100));
1377                        value %= 100;
1378                        buffer.append((char)(zeroDigit + value / 10));
1379                        buffer.append((char)(zeroDigit + value % 10));
1380                        return;
1381                    }
1382                    if (minDigits == 2 && maxDigits == 2) {
1383                        zeroPaddingNumber(value % 100, 2, 2, buffer);
1384                        return;
1385                    }
1386                }
1387            }
1388        } catch (Exception e) {
1389        }
1390
1391        numberFormat.setMinimumIntegerDigits(minDigits);
1392        numberFormat.setMaximumIntegerDigits(maxDigits);
1393        numberFormat.format((long)value, buffer, DontCareFieldPosition.INSTANCE);
1394    }
1395
1396
1397    /**
1398     * Parses text from a string to produce a <code>Date</code>.
1399     * <p>
1400     * The method attempts to parse text starting at the index given by
1401     * <code>pos</code>.
1402     * If parsing succeeds, then the index of <code>pos</code> is updated
1403     * to the index after the last character used (parsing does not necessarily
1404     * use all characters up to the end of the string), and the parsed
1405     * date is returned. The updated <code>pos</code> can be used to
1406     * indicate the starting point for the next call to this method.
1407     * If an error occurs, then the index of <code>pos</code> is not
1408     * changed, the error index of <code>pos</code> is set to the index of
1409     * the character where the error occurred, and null is returned.
1410     *
1411     * <p>This parsing operation uses the {@link DateFormat#calendar
1412     * calendar} to produce a {@code Date}. All of the {@code
1413     * calendar}'s date-time fields are {@linkplain Calendar#clear()
1414     * cleared} before parsing, and the {@code calendar}'s default
1415     * values of the date-time fields are used for any missing
1416     * date-time information. For example, the year value of the
1417     * parsed {@code Date} is 1970 with {@link GregorianCalendar} if
1418     * no year value is given from the parsing operation.  The {@code
1419     * TimeZone} value may be overwritten, depending on the given
1420     * pattern and the time zone value in {@code text}. Any {@code
1421     * TimeZone} value that has previously been set by a call to
1422     * {@link #setTimeZone(java.util.TimeZone) setTimeZone} may need
1423     * to be restored for further operations.
1424     *
1425     * @param text  A <code>String</code>, part of which should be parsed.
1426     * @param pos   A <code>ParsePosition</code> object with index and error
1427     *              index information as described above.
1428     * @return A <code>Date</code> parsed from the string. In case of
1429     *         error, returns null.
1430     * @exception NullPointerException if <code>text</code> or <code>pos</code> is null.
1431     */
1432    @Override
1433    public Date parse(String text, ParsePosition pos)
1434    {
1435        checkNegativeNumberExpression();
1436
1437        int start = pos.index;
1438        int oldStart = start;
1439        int textLength = text.length();
1440
1441        boolean[] ambiguousYear = {false};
1442
1443        CalendarBuilder calb = new CalendarBuilder();
1444
1445        for (int i = 0; i < compiledPattern.length; ) {
1446            int tag = compiledPattern[i] >>> 8;
1447            int count = compiledPattern[i++] & 0xff;
1448            if (count == 255) {
1449                count = compiledPattern[i++] << 16;
1450                count |= compiledPattern[i++];
1451            }
1452
1453            switch (tag) {
1454            case TAG_QUOTE_ASCII_CHAR:
1455                if (start >= textLength || text.charAt(start) != (char)count) {
1456                    pos.index = oldStart;
1457                    pos.errorIndex = start;
1458                    return null;
1459                }
1460                start++;
1461                break;
1462
1463            case TAG_QUOTE_CHARS:
1464                while (count-- > 0) {
1465                    if (start >= textLength || text.charAt(start) != compiledPattern[i++]) {
1466                        pos.index = oldStart;
1467                        pos.errorIndex = start;
1468                        return null;
1469                    }
1470                    start++;
1471                }
1472                break;
1473
1474            default:
1475                // Peek the next pattern to determine if we need to
1476                // obey the number of pattern letters for
1477                // parsing. It's required when parsing contiguous
1478                // digit text (e.g., "20010704") with a pattern which
1479                // has no delimiters between fields, like "yyyyMMdd".
1480                boolean obeyCount = false;
1481
1482                // In Arabic, a minus sign for a negative number is put after
1483                // the number. Even in another locale, a minus sign can be
1484                // put after a number using DateFormat.setNumberFormat().
1485                // If both the minus sign and the field-delimiter are '-',
1486                // subParse() needs to determine whether a '-' after a number
1487                // in the given text is a delimiter or is a minus sign for the
1488                // preceding number. We give subParse() a clue based on the
1489                // information in compiledPattern.
1490                boolean useFollowingMinusSignAsDelimiter = false;
1491
1492                if (i < compiledPattern.length) {
1493                    int nextTag = compiledPattern[i] >>> 8;
1494                    if (!(nextTag == TAG_QUOTE_ASCII_CHAR ||
1495                          nextTag == TAG_QUOTE_CHARS)) {
1496                        obeyCount = true;
1497                    }
1498
1499                    if (hasFollowingMinusSign &&
1500                        (nextTag == TAG_QUOTE_ASCII_CHAR ||
1501                         nextTag == TAG_QUOTE_CHARS)) {
1502                        int c;
1503                        if (nextTag == TAG_QUOTE_ASCII_CHAR) {
1504                            c = compiledPattern[i] & 0xff;
1505                        } else {
1506                            c = compiledPattern[i+1];
1507                        }
1508
1509                        if (c == minusSign) {
1510                            useFollowingMinusSignAsDelimiter = true;
1511                        }
1512                    }
1513                }
1514                start = subParse(text, start, tag, count, obeyCount,
1515                                 ambiguousYear, pos,
1516                                 useFollowingMinusSignAsDelimiter, calb);
1517                if (start < 0) {
1518                    pos.index = oldStart;
1519                    return null;
1520                }
1521            }
1522        }
1523
1524        // At this point the fields of Calendar have been set.  Calendar
1525        // will fill in default values for missing fields when the time
1526        // is computed.
1527
1528        pos.index = start;
1529
1530        Date parsedDate;
1531        try {
1532            parsedDate = calb.establish(calendar).getTime();
1533            // If the year value is ambiguous,
1534            // then the two-digit year == the default start year
1535            if (ambiguousYear[0]) {
1536                if (parsedDate.before(defaultCenturyStart)) {
1537                    parsedDate = calb.addYear(100).establish(calendar).getTime();
1538                }
1539            }
1540        }
1541        // An IllegalArgumentException will be thrown by Calendar.getTime()
1542        // if any fields are out of range, e.g., MONTH == 17.
1543        catch (IllegalArgumentException e) {
1544            pos.errorIndex = start;
1545            pos.index = oldStart;
1546            return null;
1547        }
1548
1549        return parsedDate;
1550    }
1551
1552    /**
1553     * Private code-size reduction function used by subParse.
1554     * @param text the time text being parsed.
1555     * @param start where to start parsing.
1556     * @param field the date field being parsed.
1557     * @param data the string array to parsed.
1558     * @return the new start position if matching succeeded; a negative number
1559     * indicating matching failure, otherwise.
1560     */
1561    private int matchString(String text, int start, int field, String[] data, CalendarBuilder calb)
1562    {
1563        int i = 0;
1564        int count = data.length;
1565
1566        if (field == Calendar.DAY_OF_WEEK) {
1567            i = 1;
1568        }
1569
1570        // There may be multiple strings in the data[] array which begin with
1571        // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
1572        // We keep track of the longest match, and return that.  Note that this
1573        // unfortunately requires us to test all array elements.
1574        int bestMatchLength = 0, bestMatch = -1;
1575        for (; i<count; ++i)
1576        {
1577            int length = data[i].length();
1578            // Always compare if we have no match yet; otherwise only compare
1579            // against potentially better matches (longer strings).
1580            if (length > bestMatchLength &&
1581                text.regionMatches(true, start, data[i], 0, length))
1582            {
1583                bestMatch = i;
1584                bestMatchLength = length;
1585            }
1586        }
1587        if (bestMatch >= 0)
1588        {
1589            calb.set(field, bestMatch);
1590            return start + bestMatchLength;
1591        }
1592        return -start;
1593    }
1594
1595    /**
1596     * Performs the same thing as matchString(String, int, int,
1597     * String[]). This method takes a Map<String, Integer> instead of
1598     * String[].
1599     */
1600    private int matchString(String text, int start, int field,
1601                            Map<String,Integer> data, CalendarBuilder calb) {
1602        if (data != null) {
1603            // TODO: make this default when it's in the spec.
1604            if (data instanceof SortedMap) {
1605                for (String name : data.keySet()) {
1606                    if (text.regionMatches(true, start, name, 0, name.length())) {
1607                        calb.set(field, data.get(name));
1608                        return start + name.length();
1609                    }
1610                }
1611                return -start;
1612            }
1613
1614            String bestMatch = null;
1615
1616            for (String name : data.keySet()) {
1617                int length = name.length();
1618                if (bestMatch == null || length > bestMatch.length()) {
1619                    if (text.regionMatches(true, start, name, 0, length)) {
1620                        bestMatch = name;
1621                    }
1622                }
1623            }
1624
1625            if (bestMatch != null) {
1626                calb.set(field, data.get(bestMatch));
1627                return start + bestMatch.length();
1628            }
1629        }
1630        return -start;
1631    }
1632
1633    private int matchZoneString(String text, int start, String[] zoneNames) {
1634        for (int i = 1; i <= 4; ++i) {
1635            // Checking long and short zones [1 & 2],
1636            // and long and short daylight [3 & 4].
1637            String zoneName = zoneNames[i];
1638            if (text.regionMatches(true, start,
1639                                   zoneName, 0, zoneName.length())) {
1640                return i;
1641            }
1642        }
1643        return -1;
1644    }
1645
1646    private boolean matchDSTString(String text, int start, int zoneIndex, int standardIndex,
1647                                   String[][] zoneStrings) {
1648        int index = standardIndex + 2;
1649        String zoneName  = zoneStrings[zoneIndex][index];
1650        if (text.regionMatches(true, start,
1651                               zoneName, 0, zoneName.length())) {
1652            return true;
1653        }
1654        return false;
1655    }
1656
1657    /**
1658     * find time zone 'text' matched zoneStrings and set to internal
1659     * calendar.
1660     */
1661    private int subParseZoneString(String text, int start, CalendarBuilder calb) {
1662        boolean useSameName = false; // true if standard and daylight time use the same abbreviation.
1663        TimeZone currentTimeZone = getTimeZone();
1664
1665        // At this point, check for named time zones by looking through
1666        // the locale data from the TimeZoneNames strings.
1667        // Want to be able to parse both short and long forms.
1668        int zoneIndex = formatData.getZoneIndex(currentTimeZone.getID());
1669        TimeZone tz = null;
1670        String[][] zoneStrings = formatData.getZoneStringsWrapper();
1671        String[] zoneNames = null;
1672        int nameIndex = 0;
1673        if (zoneIndex != -1) {
1674            zoneNames = zoneStrings[zoneIndex];
1675            if ((nameIndex = matchZoneString(text, start, zoneNames)) > 0) {
1676                if (nameIndex <= 2) {
1677                    // Check if the standard name (abbr) and the daylight name are the same.
1678                    useSameName = zoneNames[nameIndex].equalsIgnoreCase(zoneNames[nameIndex + 2]);
1679                }
1680                tz = TimeZone.getTimeZone(zoneNames[0]);
1681            }
1682        }
1683        if (tz == null) {
1684            zoneIndex = formatData.getZoneIndex(TimeZone.getDefault().getID());
1685            if (zoneIndex != -1) {
1686                zoneNames = zoneStrings[zoneIndex];
1687                if ((nameIndex = matchZoneString(text, start, zoneNames)) > 0) {
1688                    if (nameIndex <= 2) {
1689                        useSameName = zoneNames[nameIndex].equalsIgnoreCase(zoneNames[nameIndex + 2]);
1690                    }
1691                    tz = TimeZone.getTimeZone(zoneNames[0]);
1692                }
1693            }
1694        }
1695
1696        if (tz == null) {
1697            int len = zoneStrings.length;
1698            for (int i = 0; i < len; i++) {
1699                zoneNames = zoneStrings[i];
1700                if ((nameIndex = matchZoneString(text, start, zoneNames)) > 0) {
1701                    if (nameIndex <= 2) {
1702                        useSameName = zoneNames[nameIndex].equalsIgnoreCase(zoneNames[nameIndex + 2]);
1703                    }
1704                    tz = TimeZone.getTimeZone(zoneNames[0]);
1705                    break;
1706                }
1707            }
1708        }
1709        if (tz != null) { // Matched any ?
1710            if (!tz.equals(currentTimeZone)) {
1711                setTimeZone(tz);
1712            }
1713            // If the time zone matched uses the same name
1714            // (abbreviation) for both standard and daylight time,
1715            // let the time zone in the Calendar decide which one.
1716            //
1717            // Also if tz.getDSTSaving() returns 0 for DST, use tz to
1718            // determine the local time. (6645292)
1719            int dstAmount = (nameIndex >= 3) ? tz.getDSTSavings() : 0;
1720            if (!(useSameName || (nameIndex >= 3 && dstAmount == 0))) {
1721                calb.clear(Calendar.ZONE_OFFSET).set(Calendar.DST_OFFSET, dstAmount);
1722            }
1723            return (start + zoneNames[nameIndex].length());
1724        }
1725        return 0;
1726    }
1727
1728    /**
1729     * Parses numeric forms of time zone offset, such as "hh:mm", and
1730     * sets calb to the parsed value.
1731     *
1732     * @param text  the text to be parsed
1733     * @param start the character position to start parsing
1734     * @param sign  1: positive; -1: negative
1735     * @param count 0: 'Z' or "GMT+hh:mm" parsing; 1 - 3: the number of 'X's
1736     * @param colon true - colon required between hh and mm; false - no colon required
1737     * @param calb  a CalendarBuilder in which the parsed value is stored
1738     * @return updated parsed position, or its negative value to indicate a parsing error
1739     */
1740    private int subParseNumericZone(String text, int start, int sign, int count,
1741                                    boolean colon, CalendarBuilder calb) {
1742        int index = start;
1743
1744      parse:
1745        try {
1746            char c = text.charAt(index++);
1747            // Parse hh
1748            int hours;
1749            if (!isDigit(c)) {
1750                break parse;
1751            }
1752            hours = c - '0';
1753            c = text.charAt(index++);
1754            if (isDigit(c)) {
1755                hours = hours * 10 + (c - '0');
1756            } else {
1757                // If no colon in RFC 822 or 'X' (ISO), two digits are
1758                // required.
1759                if (count > 0 || !colon) {
1760                    break parse;
1761                }
1762                --index;
1763            }
1764            if (hours > 23) {
1765                break parse;
1766            }
1767            int minutes = 0;
1768            if (count != 1) {
1769                // Proceed with parsing mm
1770                c = text.charAt(index++);
1771                if (colon) {
1772                    if (c != ':') {
1773                        break parse;
1774                    }
1775                    c = text.charAt(index++);
1776                }
1777                if (!isDigit(c)) {
1778                    break parse;
1779                }
1780                minutes = c - '0';
1781                c = text.charAt(index++);
1782                if (!isDigit(c)) {
1783                    break parse;
1784                }
1785                minutes = minutes * 10 + (c - '0');
1786                if (minutes > 59) {
1787                    break parse;
1788                }
1789            }
1790            minutes += hours * 60;
1791            calb.set(Calendar.ZONE_OFFSET, minutes * MILLIS_PER_MINUTE * sign)
1792                .set(Calendar.DST_OFFSET, 0);
1793            return index;
1794        } catch (IndexOutOfBoundsException e) {
1795        }
1796        return  1 - index; // -(index - 1)
1797    }
1798
1799    private boolean isDigit(char c) {
1800        return c >= '0' && c <= '9';
1801    }
1802
1803    /**
1804     * Private member function that converts the parsed date strings into
1805     * timeFields. Returns -start (for ParsePosition) if failed.
1806     * @param text the time text to be parsed.
1807     * @param start where to start parsing.
1808     * @param patternCharIndex the index of the pattern character.
1809     * @param count the count of a pattern character.
1810     * @param obeyCount if true, then the next field directly abuts this one,
1811     * and we should use the count to know when to stop parsing.
1812     * @param ambiguousYear return parameter; upon return, if ambiguousYear[0]
1813     * is true, then a two-digit year was parsed and may need to be readjusted.
1814     * @param origPos origPos.errorIndex is used to return an error index
1815     * at which a parse error occurred, if matching failure occurs.
1816     * @return the new start position if matching succeeded; -1 indicating
1817     * matching failure, otherwise. In case matching failure occurred,
1818     * an error index is set to origPos.errorIndex.
1819     */
1820    private int subParse(String text, int start, int patternCharIndex, int count,
1821                         boolean obeyCount, boolean[] ambiguousYear,
1822                         ParsePosition origPos,
1823                         boolean useFollowingMinusSignAsDelimiter, CalendarBuilder calb) {
1824        Number number;
1825        int value = 0;
1826        ParsePosition pos = new ParsePosition(0);
1827        pos.index = start;
1828        if (patternCharIndex == PATTERN_WEEK_YEAR && !calendar.isWeekDateSupported()) {
1829            // use calendar year 'y' instead
1830            patternCharIndex = PATTERN_YEAR;
1831        }
1832        int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
1833
1834        // If there are any spaces here, skip over them.  If we hit the end
1835        // of the string, then fail.
1836        for (;;) {
1837            if (pos.index >= text.length()) {
1838                origPos.errorIndex = start;
1839                return -1;
1840            }
1841            char c = text.charAt(pos.index);
1842            if (c != ' ' && c != '\t') {
1843                break;
1844            }
1845            ++pos.index;
1846        }
1847        // Remember the actual start index
1848        int actualStart = pos.index;
1849
1850      parsing:
1851        {
1852            // We handle a few special cases here where we need to parse
1853            // a number value.  We handle further, more generic cases below.  We need
1854            // to handle some of them here because some fields require extra processing on
1855            // the parsed value.
1856            if (patternCharIndex == PATTERN_HOUR_OF_DAY1 ||
1857                patternCharIndex == PATTERN_HOUR1 ||
1858                (patternCharIndex == PATTERN_MONTH && count <= 2) ||
1859                patternCharIndex == PATTERN_YEAR ||
1860                patternCharIndex == PATTERN_WEEK_YEAR) {
1861                // It would be good to unify this with the obeyCount logic below,
1862                // but that's going to be difficult.
1863                if (obeyCount) {
1864                    if ((start+count) > text.length()) {
1865                        break parsing;
1866                    }
1867                    number = numberFormat.parse(text.substring(0, start+count), pos);
1868                } else {
1869                    number = numberFormat.parse(text, pos);
1870                }
1871                if (number == null) {
1872                    if (patternCharIndex != PATTERN_YEAR || calendar instanceof GregorianCalendar) {
1873                        break parsing;
1874                    }
1875                } else {
1876                    value = number.intValue();
1877
1878                    if (useFollowingMinusSignAsDelimiter && (value < 0) &&
1879                        (((pos.index < text.length()) &&
1880                         (text.charAt(pos.index) != minusSign)) ||
1881                         ((pos.index == text.length()) &&
1882                          (text.charAt(pos.index-1) == minusSign)))) {
1883                        value = -value;
1884                        pos.index--;
1885                    }
1886                }
1887            }
1888
1889            boolean useDateFormatSymbols = useDateFormatSymbols();
1890
1891            int index;
1892            switch (patternCharIndex) {
1893            case PATTERN_ERA: // 'G'
1894                if (useDateFormatSymbols) {
1895                    if ((index = matchString(text, start, Calendar.ERA, formatData.getEras(), calb)) > 0) {
1896                        return index;
1897                    }
1898                } else {
1899                    Map<String, Integer> map = getDisplayNamesMap(field, locale);
1900                    if ((index = matchString(text, start, field, map, calb)) > 0) {
1901                        return index;
1902                    }
1903                }
1904                break parsing;
1905
1906            case PATTERN_WEEK_YEAR: // 'Y'
1907            case PATTERN_YEAR:      // 'y'
1908                if (!(calendar instanceof GregorianCalendar)) {
1909                    // calendar might have text representations for year values,
1910                    // such as "\u5143" in JapaneseImperialCalendar.
1911                    int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT;
1912                    Map<String, Integer> map = calendar.getDisplayNames(field, style, locale);
1913                    if (map != null) {
1914                        if ((index = matchString(text, start, field, map, calb)) > 0) {
1915                            return index;
1916                        }
1917                    }
1918                    calb.set(field, value);
1919                    return pos.index;
1920                }
1921
1922                // If there are 3 or more YEAR pattern characters, this indicates
1923                // that the year value is to be treated literally, without any
1924                // two-digit year adjustments (e.g., from "01" to 2001).  Otherwise
1925                // we made adjustments to place the 2-digit year in the proper
1926                // century, for parsed strings from "00" to "99".  Any other string
1927                // is treated literally:  "2250", "-1", "1", "002".
1928                if (count <= 2 && (pos.index - actualStart) == 2
1929                    && Character.isDigit(text.charAt(actualStart))
1930                    && Character.isDigit(text.charAt(actualStart + 1))) {
1931                    // Assume for example that the defaultCenturyStart is 6/18/1903.
1932                    // This means that two-digit years will be forced into the range
1933                    // 6/18/1903 to 6/17/2003.  As a result, years 00, 01, and 02
1934                    // correspond to 2000, 2001, and 2002.  Years 04, 05, etc. correspond
1935                    // to 1904, 1905, etc.  If the year is 03, then it is 2003 if the
1936                    // other fields specify a date before 6/18, or 1903 if they specify a
1937                    // date afterwards.  As a result, 03 is an ambiguous year.  All other
1938                    // two-digit years are unambiguous.
1939                    int ambiguousTwoDigitYear = defaultCenturyStartYear % 100;
1940                    ambiguousYear[0] = value == ambiguousTwoDigitYear;
1941                    value += (defaultCenturyStartYear/100)*100 +
1942                        (value < ambiguousTwoDigitYear ? 100 : 0);
1943                }
1944                calb.set(field, value);
1945                return pos.index;
1946
1947            case PATTERN_MONTH: // 'M'
1948                if (count <= 2) // i.e., M or MM.
1949                {
1950                    // Don't want to parse the month if it is a string
1951                    // while pattern uses numeric style: M or MM.
1952                    // [We computed 'value' above.]
1953                    calb.set(Calendar.MONTH, value - 1);
1954                    return pos.index;
1955                }
1956
1957                if (useDateFormatSymbols) {
1958                    // count >= 3 // i.e., MMM or MMMM
1959                    // Want to be able to parse both short and long forms.
1960                    // Try count == 4 first:
1961                    int newStart;
1962                    if ((newStart = matchString(text, start, Calendar.MONTH,
1963                                                formatData.getMonths(), calb)) > 0) {
1964                        return newStart;
1965                    }
1966                    // count == 4 failed, now try count == 3
1967                    if ((index = matchString(text, start, Calendar.MONTH,
1968                                             formatData.getShortMonths(), calb)) > 0) {
1969                        return index;
1970                    }
1971                } else {
1972                    Map<String, Integer> map = getDisplayNamesMap(field, locale);
1973                    if ((index = matchString(text, start, field, map, calb)) > 0) {
1974                        return index;
1975                    }
1976                }
1977                break parsing;
1978
1979            case PATTERN_HOUR_OF_DAY1: // 'k' 1-based.  eg, 23:59 + 1 hour =>> 24:59
1980                if (!isLenient()) {
1981                    // Validate the hour value in non-lenient
1982                    if (value < 1 || value > 24) {
1983                        break parsing;
1984                    }
1985                }
1986                // [We computed 'value' above.]
1987                if (value == calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1) {
1988                    value = 0;
1989                }
1990                calb.set(Calendar.HOUR_OF_DAY, value);
1991                return pos.index;
1992
1993            case PATTERN_DAY_OF_WEEK:  // 'E'
1994                {
1995                    if (useDateFormatSymbols) {
1996                        // Want to be able to parse both short and long forms.
1997                        // Try count == 4 (DDDD) first:
1998                        int newStart;
1999                        if ((newStart=matchString(text, start, Calendar.DAY_OF_WEEK,
2000                                                  formatData.getWeekdays(), calb)) > 0) {
2001                            return newStart;
2002                        }
2003                        // DDDD failed, now try DDD
2004                        if ((index = matchString(text, start, Calendar.DAY_OF_WEEK,
2005                                                 formatData.getShortWeekdays(), calb)) > 0) {
2006                            return index;
2007                        }
2008                    } else {
2009                        int[] styles = { Calendar.LONG, Calendar.SHORT };
2010                        for (int style : styles) {
2011                            Map<String,Integer> map = calendar.getDisplayNames(field, style, locale);
2012                            if ((index = matchString(text, start, field, map, calb)) > 0) {
2013                                return index;
2014                            }
2015                        }
2016                    }
2017                }
2018                break parsing;
2019
2020            case PATTERN_AM_PM:    // 'a'
2021                if (useDateFormatSymbols) {
2022                    if ((index = matchString(text, start, Calendar.AM_PM,
2023                                             formatData.getAmPmStrings(), calb)) > 0) {
2024                        return index;
2025                    }
2026                } else {
2027                    Map<String,Integer> map = getDisplayNamesMap(field, locale);
2028                    if ((index = matchString(text, start, field, map, calb)) > 0) {
2029                        return index;
2030                    }
2031                }
2032                break parsing;
2033
2034            case PATTERN_HOUR1: // 'h' 1-based.  eg, 11PM + 1 hour =>> 12 AM
2035                if (!isLenient()) {
2036                    // Validate the hour value in non-lenient
2037                    if (value < 1 || value > 12) {
2038                        break parsing;
2039                    }
2040                }
2041                // [We computed 'value' above.]
2042                if (value == calendar.getLeastMaximum(Calendar.HOUR) + 1) {
2043                    value = 0;
2044                }
2045                calb.set(Calendar.HOUR, value);
2046                return pos.index;
2047
2048            case PATTERN_ZONE_NAME:  // 'z'
2049            case PATTERN_ZONE_VALUE: // 'Z'
2050                {
2051                    int sign = 0;
2052                    try {
2053                        char c = text.charAt(pos.index);
2054                        if (c == '+') {
2055                            sign = 1;
2056                        } else if (c == '-') {
2057                            sign = -1;
2058                        }
2059                        if (sign == 0) {
2060                            // Try parsing a custom time zone "GMT+hh:mm" or "GMT".
2061                            if ((c == 'G' || c == 'g')
2062                                && (text.length() - start) >= GMT.length()
2063                                && text.regionMatches(true, start, GMT, 0, GMT.length())) {
2064                                pos.index = start + GMT.length();
2065
2066                                if ((text.length() - pos.index) > 0) {
2067                                    c = text.charAt(pos.index);
2068                                    if (c == '+') {
2069                                        sign = 1;
2070                                    } else if (c == '-') {
2071                                        sign = -1;
2072                                    }
2073                                }
2074
2075                                if (sign == 0) {    /* "GMT" without offset */
2076                                    calb.set(Calendar.ZONE_OFFSET, 0)
2077                                        .set(Calendar.DST_OFFSET, 0);
2078                                    return pos.index;
2079                                }
2080
2081                                // Parse the rest as "hh:mm"
2082                                int i = subParseNumericZone(text, ++pos.index,
2083                                                            sign, 0, true, calb);
2084                                if (i > 0) {
2085                                    return i;
2086                                }
2087                                pos.index = -i;
2088                            } else {
2089                                // Try parsing the text as a time zone
2090                                // name or abbreviation.
2091                                int i = subParseZoneString(text, pos.index, calb);
2092                                if (i > 0) {
2093                                    return i;
2094                                }
2095                                pos.index = -i;
2096                            }
2097                        } else {
2098                            // Parse the rest as "hhmm" (RFC 822)
2099                            int i = subParseNumericZone(text, ++pos.index,
2100                                                        sign, 0, false, calb);
2101                            if (i > 0) {
2102                                return i;
2103                            }
2104                            pos.index = -i;
2105                        }
2106                    } catch (IndexOutOfBoundsException e) {
2107                    }
2108                }
2109                break parsing;
2110
2111            case PATTERN_ISO_ZONE:   // 'X'
2112                {
2113                    if ((text.length() - pos.index) <= 0) {
2114                        break parsing;
2115                    }
2116
2117                    int sign;
2118                    char c = text.charAt(pos.index);
2119                    if (c == 'Z') {
2120                        calb.set(Calendar.ZONE_OFFSET, 0).set(Calendar.DST_OFFSET, 0);
2121                        return ++pos.index;
2122                    }
2123
2124                    // parse text as "+/-hh[[:]mm]" based on count
2125                    if (c == '+') {
2126                        sign = 1;
2127                    } else if (c == '-') {
2128                        sign = -1;
2129                    } else {
2130                        ++pos.index;
2131                        break parsing;
2132                    }
2133                    int i = subParseNumericZone(text, ++pos.index, sign, count,
2134                                                count == 3, calb);
2135                    if (i > 0) {
2136                        return i;
2137                    }
2138                    pos.index = -i;
2139                }
2140                break parsing;
2141
2142            default:
2143         // case PATTERN_DAY_OF_MONTH:         // 'd'
2144         // case PATTERN_HOUR_OF_DAY0:         // 'H' 0-based.  eg, 23:59 + 1 hour =>> 00:59
2145         // case PATTERN_MINUTE:               // 'm'
2146         // case PATTERN_SECOND:               // 's'
2147         // case PATTERN_MILLISECOND:          // 'S'
2148         // case PATTERN_DAY_OF_YEAR:          // 'D'
2149         // case PATTERN_DAY_OF_WEEK_IN_MONTH: // 'F'
2150         // case PATTERN_WEEK_OF_YEAR:         // 'w'
2151         // case PATTERN_WEEK_OF_MONTH:        // 'W'
2152         // case PATTERN_HOUR0:                // 'K' 0-based.  eg, 11PM + 1 hour =>> 0 AM
2153         // case PATTERN_ISO_DAY_OF_WEEK:      // 'u' (pseudo field);
2154
2155                // Handle "generic" fields
2156                if (obeyCount) {
2157                    if ((start+count) > text.length()) {
2158                        break parsing;
2159                    }
2160                    number = numberFormat.parse(text.substring(0, start+count), pos);
2161                } else {
2162                    number = numberFormat.parse(text, pos);
2163                }
2164                if (number != null) {
2165                    value = number.intValue();
2166
2167                    if (useFollowingMinusSignAsDelimiter && (value < 0) &&
2168                        (((pos.index < text.length()) &&
2169                         (text.charAt(pos.index) != minusSign)) ||
2170                         ((pos.index == text.length()) &&
2171                          (text.charAt(pos.index-1) == minusSign)))) {
2172                        value = -value;
2173                        pos.index--;
2174                    }
2175
2176                    calb.set(field, value);
2177                    return pos.index;
2178                }
2179                break parsing;
2180            }
2181        }
2182
2183        // Parsing failed.
2184        origPos.errorIndex = pos.index;
2185        return -1;
2186    }
2187
2188    /**
2189     * Returns true if the DateFormatSymbols has been set explicitly or locale
2190     * is null.
2191     */
2192    private boolean useDateFormatSymbols() {
2193        return useDateFormatSymbols || locale == null;
2194    }
2195
2196    /**
2197     * Translates a pattern, mapping each character in the from string to the
2198     * corresponding character in the to string.
2199     *
2200     * @exception IllegalArgumentException if the given pattern is invalid
2201     */
2202    private String translatePattern(String pattern, String from, String to) {
2203        StringBuilder result = new StringBuilder();
2204        boolean inQuote = false;
2205        for (int i = 0; i < pattern.length(); ++i) {
2206            char c = pattern.charAt(i);
2207            if (inQuote) {
2208                if (c == '\'') {
2209                    inQuote = false;
2210                }
2211            }
2212            else {
2213                if (c == '\'') {
2214                    inQuote = true;
2215                } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
2216                    int ci = from.indexOf(c);
2217                    if (ci >= 0) {
2218                        // patternChars is longer than localPatternChars due
2219                        // to serialization compatibility. The pattern letters
2220                        // unsupported by localPatternChars pass through.
2221                        if (ci < to.length()) {
2222                            c = to.charAt(ci);
2223                        }
2224                    } else {
2225                        throw new IllegalArgumentException("Illegal pattern " +
2226                                                           " character '" +
2227                                                           c + "'");
2228                    }
2229                }
2230            }
2231            result.append(c);
2232        }
2233        if (inQuote) {
2234            throw new IllegalArgumentException("Unfinished quote in pattern");
2235        }
2236        return result.toString();
2237    }
2238
2239    /**
2240     * Returns a pattern string describing this date format.
2241     *
2242     * @return a pattern string describing this date format.
2243     */
2244    public String toPattern() {
2245        return pattern;
2246    }
2247
2248    /**
2249     * Returns a localized pattern string describing this date format.
2250     *
2251     * @return a localized pattern string describing this date format.
2252     */
2253    public String toLocalizedPattern() {
2254        return translatePattern(pattern,
2255                                DateFormatSymbols.patternChars,
2256                                formatData.getLocalPatternChars());
2257    }
2258
2259    /**
2260     * Applies the given pattern string to this date format.
2261     *
2262     * @param pattern the new date and time pattern for this date format
2263     * @exception NullPointerException if the given pattern is null
2264     * @exception IllegalArgumentException if the given pattern is invalid
2265     */
2266    public void applyPattern(String pattern)
2267    {
2268        applyPatternImpl(pattern);
2269    }
2270
2271    private void applyPatternImpl(String pattern) {
2272        compiledPattern = compile(pattern);
2273        this.pattern = pattern;
2274    }
2275
2276    /**
2277     * Applies the given localized pattern string to this date format.
2278     *
2279     * @param pattern a String to be mapped to the new date and time format
2280     *        pattern for this format
2281     * @exception NullPointerException if the given pattern is null
2282     * @exception IllegalArgumentException if the given pattern is invalid
2283     */
2284    public void applyLocalizedPattern(String pattern) {
2285         String p = translatePattern(pattern,
2286                                     formatData.getLocalPatternChars(),
2287                                     DateFormatSymbols.patternChars);
2288         compiledPattern = compile(p);
2289         this.pattern = p;
2290    }
2291
2292    /**
2293     * Gets a copy of the date and time format symbols of this date format.
2294     *
2295     * @return the date and time format symbols of this date format
2296     * @see #setDateFormatSymbols
2297     */
2298    public DateFormatSymbols getDateFormatSymbols()
2299    {
2300        return (DateFormatSymbols)formatData.clone();
2301    }
2302
2303    /**
2304     * Sets the date and time format symbols of this date format.
2305     *
2306     * @param newFormatSymbols the new date and time format symbols
2307     * @exception NullPointerException if the given newFormatSymbols is null
2308     * @see #getDateFormatSymbols
2309     */
2310    public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols)
2311    {
2312        this.formatData = (DateFormatSymbols)newFormatSymbols.clone();
2313        useDateFormatSymbols = true;
2314    }
2315
2316    /**
2317     * Creates a copy of this <code>SimpleDateFormat</code>. This also
2318     * clones the format's date format symbols.
2319     *
2320     * @return a clone of this <code>SimpleDateFormat</code>
2321     */
2322    @Override
2323    public Object clone() {
2324        SimpleDateFormat other = (SimpleDateFormat) super.clone();
2325        other.formatData = (DateFormatSymbols) formatData.clone();
2326        return other;
2327    }
2328
2329    /**
2330     * Returns the hash code value for this <code>SimpleDateFormat</code> object.
2331     *
2332     * @return the hash code value for this <code>SimpleDateFormat</code> object.
2333     */
2334    @Override
2335    public int hashCode()
2336    {
2337        return pattern.hashCode();
2338        // just enough fields for a reasonable distribution
2339    }
2340
2341    /**
2342     * Compares the given object with this <code>SimpleDateFormat</code> for
2343     * equality.
2344     *
2345     * @return true if the given object is equal to this
2346     * <code>SimpleDateFormat</code>
2347     */
2348    @Override
2349    public boolean equals(Object obj)
2350    {
2351        if (!super.equals(obj)) {
2352            return false; // super does class check
2353        }
2354        SimpleDateFormat that = (SimpleDateFormat) obj;
2355        return (pattern.equals(that.pattern)
2356                && formatData.equals(that.formatData));
2357    }
2358
2359    private static final int[] REST_OF_STYLES = {
2360        Calendar.SHORT_STANDALONE, Calendar.LONG_FORMAT, Calendar.LONG_STANDALONE,
2361    };
2362    private Map<String, Integer> getDisplayNamesMap(int field, Locale locale) {
2363        Map<String, Integer> map = calendar.getDisplayNames(field, Calendar.SHORT_FORMAT, locale);
2364        // Get all SHORT and LONG styles (avoid NARROW styles).
2365        for (int style : REST_OF_STYLES) {
2366            Map<String, Integer> m = calendar.getDisplayNames(field, style, locale);
2367            if (m != null) {
2368                map.putAll(m);
2369            }
2370        }
2371        return map;
2372    }
2373
2374    /**
2375     * After reading an object from the input stream, the format
2376     * pattern in the object is verified.
2377     *
2378     * @exception InvalidObjectException if the pattern is invalid
2379     */
2380    private void readObject(ObjectInputStream stream)
2381                         throws IOException, ClassNotFoundException {
2382        stream.defaultReadObject();
2383
2384        try {
2385            compiledPattern = compile(pattern);
2386        } catch (Exception e) {
2387            throw new InvalidObjectException("invalid pattern");
2388        }
2389
2390        if (serialVersionOnStream < 1) {
2391            // didn't have defaultCenturyStart field
2392            initializeDefaultCentury();
2393        }
2394        else {
2395            // fill in dependent transient field
2396            parseAmbiguousDatesAsAfter(defaultCenturyStart);
2397        }
2398        serialVersionOnStream = currentSerialVersion;
2399
2400        // If the deserialized object has a SimpleTimeZone, try
2401        // to replace it with a ZoneInfo equivalent in order to
2402        // be compatible with the SimpleTimeZone-based
2403        // implementation as much as possible.
2404        TimeZone tz = getTimeZone();
2405        if (tz instanceof SimpleTimeZone) {
2406            String id = tz.getID();
2407            TimeZone zi = TimeZone.getTimeZone(id);
2408            if (zi != null && zi.hasSameRules(tz) && zi.getID().equals(id)) {
2409                setTimeZone(zi);
2410            }
2411        }
2412    }
2413
2414    /**
2415     * Analyze the negative subpattern of DecimalFormat and set/update values
2416     * as necessary.
2417     */
2418    private void checkNegativeNumberExpression() {
2419        if ((numberFormat instanceof DecimalFormat) &&
2420            !numberFormat.equals(originalNumberFormat)) {
2421            String numberPattern = ((DecimalFormat)numberFormat).toPattern();
2422            if (!numberPattern.equals(originalNumberPattern)) {
2423                hasFollowingMinusSign = false;
2424
2425                int separatorIndex = numberPattern.indexOf(';');
2426                // If the negative subpattern is not absent, we have to analayze
2427                // it in order to check if it has a following minus sign.
2428                if (separatorIndex > -1) {
2429                    int minusIndex = numberPattern.indexOf('-', separatorIndex);
2430                    if ((minusIndex > numberPattern.lastIndexOf('0')) &&
2431                        (minusIndex > numberPattern.lastIndexOf('#'))) {
2432                        hasFollowingMinusSign = true;
2433                        minusSign = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getMinusSign();
2434                    }
2435                }
2436                originalNumberPattern = numberPattern;
2437            }
2438            originalNumberFormat = numberFormat;
2439        }
2440    }
2441
2442}
2443