]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-52_1/main/classes/core/src/com/ibm/icu/text/SimpleDateFormat.java
Added flags.
[Dictionary.git] / jars / icu4j-52_1 / main / classes / core / src / com / ibm / icu / text / SimpleDateFormat.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 1996-2013, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7
8 package com.ibm.icu.text;
9
10 import java.io.IOException;
11 import java.io.ObjectInputStream;
12 import java.io.ObjectOutputStream;
13 import java.text.AttributedCharacterIterator;
14 import java.text.AttributedString;
15 import java.text.FieldPosition;
16 import java.text.Format;
17 import java.text.ParsePosition;
18 import java.util.ArrayList;
19 import java.util.Date;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Locale;
23 import java.util.MissingResourceException;
24
25 import com.ibm.icu.impl.CalendarData;
26 import com.ibm.icu.impl.DateNumberFormat;
27 import com.ibm.icu.impl.ICUCache;
28 import com.ibm.icu.impl.PatternProps;
29 import com.ibm.icu.impl.SimpleCache;
30 import com.ibm.icu.lang.UCharacter;
31 import com.ibm.icu.text.TimeZoneFormat.Style;
32 import com.ibm.icu.text.TimeZoneFormat.TimeType;
33 import com.ibm.icu.util.BasicTimeZone;
34 import com.ibm.icu.util.Calendar;
35 import com.ibm.icu.util.HebrewCalendar;
36 import com.ibm.icu.util.Output;
37 import com.ibm.icu.util.TimeZone;
38 import com.ibm.icu.util.TimeZoneTransition;
39 import com.ibm.icu.util.ULocale;
40 import com.ibm.icu.util.ULocale.Category;
41
42
43 /**
44  * {@icuenhanced java.text.SimpleDateFormat}.{@icu _usage_}
45  *
46  * <p><code>SimpleDateFormat</code> is a concrete class for formatting and
47  * parsing dates in a locale-sensitive manner. It allows for formatting
48  * (date -> text), parsing (text -> date), and normalization.
49  *
50  * <p>
51  * <code>SimpleDateFormat</code> allows you to start by choosing
52  * any user-defined patterns for date-time formatting. However, you
53  * are encouraged to create a date-time formatter with either
54  * <code>getTimeInstance</code>, <code>getDateInstance</code>, or
55  * <code>getDateTimeInstance</code> in <code>DateFormat</code>. Each
56  * of these class methods can return a date/time formatter initialized
57  * with a default format pattern. You may modify the format pattern
58  * using the <code>applyPattern</code> methods as desired.
59  * For more information on using these methods, see
60  * {@link DateFormat}.
61  *
62  * <p><strong>Date and Time Patterns:</strong></p>
63  *
64  * <p>Date and time formats are specified by <em>date and time pattern</em> strings.
65  * Within date and time pattern strings, all unquoted ASCII letters [A-Za-z] are reserved
66  * as pattern letters representing calendar fields. <code>SimpleDateFormat</code> supports
67  * the date and time formatting algorithm and pattern letters defined by <a href="http://www.unicode.org/reports/tr35/">UTS#35
68  * Unicode Locale Data Markup Language (LDML)</a>. The following pattern letters are
69  * currently available:</p>
70  * <blockquote>
71  * <table border="1">
72  *     <tr>
73  *         <th>Field</th>
74  *         <th style="text-align: center">Sym.</th>
75  *         <th style="text-align: center">No.</th>
76  *         <th>Example</th>
77  *         <th>Description</th>
78  *     </tr>
79  *     <tr>
80  *         <th rowspan="3">era</th>
81  *         <td style="text-align: center" rowspan="3">G</td>
82  *         <td style="text-align: center">1..3</td>
83  *         <td>AD</td>
84  *         <td rowspan="3">Era - Replaced with the Era string for the current date. One to three letters for the 
85  *         abbreviated form, four letters for the long form, five for the narrow form.</td>
86  *     </tr>
87  *     <tr>
88  *         <td style="text-align: center">4</td>
89  *         <td>Anno Domini</td>
90  *     </tr>
91  *     <tr>
92  *         <td style="text-align: center">5</td>
93  *         <td>A</td>
94  *     </tr>
95  *     <tr>
96  *         <th rowspan="6">year</th>
97  *         <td style="text-align: center">y</td>
98  *         <td style="text-align: center">1..n</td>
99  *         <td>1996</td>
100  *         <td>Year. Normally the length specifies the padding, but for two letters it also specifies the maximum
101  *         length. Example:<div align="center">
102  *             <center>
103  *             <table border="1" cellpadding="2" cellspacing="0">
104  *                 <tr>
105  *                     <th>Year</th>
106  *                     <th style="text-align: right">y</th>
107  *                     <th style="text-align: right">yy</th>
108  *                     <th style="text-align: right">yyy</th>
109  *                     <th style="text-align: right">yyyy</th>
110  *                     <th style="text-align: right">yyyyy</th>
111  *                 </tr>
112  *                 <tr>
113  *                     <td>AD 1</td>
114  *                     <td style="text-align: right">1</td>
115  *                     <td style="text-align: right">01</td>
116  *                     <td style="text-align: right">001</td>
117  *                     <td style="text-align: right">0001</td>
118  *                     <td style="text-align: right">00001</td>
119  *                 </tr>
120  *                 <tr>
121  *                     <td>AD 12</td>
122  *                     <td style="text-align: right">12</td>
123  *                     <td style="text-align: right">12</td>
124  *                     <td style="text-align: right">012</td>
125  *                     <td style="text-align: right">0012</td>
126  *                     <td style="text-align: right">00012</td>
127  *                 </tr>
128  *                 <tr>
129  *                     <td>AD 123</td>
130  *                     <td style="text-align: right">123</td>
131  *                     <td style="text-align: right">23</td>
132  *                     <td style="text-align: right">123</td>
133  *                     <td style="text-align: right">0123</td>
134  *                     <td style="text-align: right">00123</td>
135  *                 </tr>
136  *                 <tr>
137  *                     <td>AD 1234</td>
138  *                     <td style="text-align: right">1234</td>
139  *                     <td style="text-align: right">34</td>
140  *                     <td style="text-align: right">1234</td>
141  *                     <td style="text-align: right">1234</td>
142  *                     <td style="text-align: right">01234</td>
143  *                 </tr>
144  *                 <tr>
145  *                     <td>AD 12345</td>
146  *                     <td style="text-align: right">12345</td>
147  *                     <td style="text-align: right">45</td>
148  *                     <td style="text-align: right">12345</td>
149  *                     <td style="text-align: right">12345</td>
150  *                     <td style="text-align: right">12345</td>
151  *                 </tr>
152  *             </table>
153  *             </center></div>
154  *         </td>
155  *     </tr>
156  *     <tr>
157  *         <td style="text-align: center">Y</td>
158  *         <td style="text-align: center">1..n</td>
159  *         <td>1997</td>
160  *         <td>Year (in "Week of Year" based calendars). Normally the length specifies the padding,
161  *         but for two letters it also specifies the maximum length. This year designation is used in ISO
162  *         year-week calendar as defined by ISO 8601, but can be used in non-Gregorian based calendar systems
163  *         where week date processing is desired. May not always be the same value as calendar year.</td>
164  *     </tr>
165  *     <tr>
166  *         <td style="text-align: center">u</td>
167  *         <td style="text-align: center">1..n</td>
168  *         <td>4601</td>
169  *         <td>Extended year. This is a single number designating the year of this calendar system, encompassing
170  *         all supra-year fields. For example, for the Julian calendar system, year numbers are positive, with an
171  *         era of BCE or CE. An extended year value for the Julian calendar system assigns positive values to CE
172  *         years and negative values to BCE years, with 1 BCE being year 0.</td>
173  *     </tr>
174  *     <tr>
175  *         <td style="text-align: center" rowspan="3">U</td>
176  *         <td style="text-align: center">1..3</td>
177  *         <td>甲子</td>
178  *         <td rowspan="3">Cyclic year name. Calendars such as the Chinese lunar calendar (and related calendars)
179  *         and the Hindu calendars use 60-year cycles of year names. Use one through three letters for the abbreviated
180  *         name, four for the full name, or five for the narrow name (currently the data only provides abbreviated names,
181  *         which will be used for all requested name widths). If the calendar does not provide cyclic year name data,
182  *         or if the year value to be formatted is out of the range of years for which cyclic name data is provided,
183  *         then numeric formatting is used (behaves like 'y').</td>
184  *     </tr>
185  *     <tr>
186  *         <td style="text-align: center">4</td>
187  *         <td>(currently also 甲子)</td>
188  *     </tr>
189  *     <tr>
190  *         <td style="text-align: center">5</td>
191  *         <td>(currently also 甲子)</td>
192  *     </tr>
193  *     <tr>
194  *         <th rowspan="6">quarter</th>
195  *         <td rowspan="3" style="text-align: center">Q</td>
196  *         <td style="text-align: center">1..2</td>
197  *         <td>02</td>
198  *         <td rowspan="3">Quarter - Use one or two for the numerical quarter, three for the abbreviation, or four 
199  *         for the full name.</td>
200  *     </tr>
201  *     <tr>
202  *         <td style="text-align: center">3</td>
203  *         <td>Q2</td>
204  *     </tr>
205  *     <tr>
206  *         <td style="text-align: center">4</td>
207  *         <td>2nd quarter</td>
208  *     </tr>
209  *     <tr>
210  *         <td rowspan="3" style="text-align: center">q</td>
211  *         <td style="text-align: center">1..2</td>
212  *         <td>02</td>
213  *         <td rowspan="3"><b>Stand-Alone</b> Quarter - Use one or two for the numerical quarter, three for the abbreviation, 
214  *         or four for the full name.</td>
215  *     </tr>
216  *     <tr>
217  *         <td style="text-align: center">3</td>
218  *         <td>Q2</td>
219  *     </tr>
220  *     <tr>
221  *         <td style="text-align: center">4</td>
222  *         <td>2nd quarter</td>
223  *     </tr>
224  *     <tr>
225  *         <th rowspan="8">month</th>
226  *         <td rowspan="4" style="text-align: center">M</td>
227  *         <td style="text-align: center">1..2</td>
228  *         <td>09</td>
229  *         <td rowspan="4">Month - Use one or two for the numerical month, three for the abbreviation, four for
230  *         the full name, or five for the narrow name.</td>
231  *     </tr>
232  *     <tr>
233  *         <td style="text-align: center">3</td>
234  *         <td>Sept</td>
235  *     </tr>
236  *     <tr>
237  *         <td style="text-align: center">4</td>
238  *         <td>September</td>
239  *     </tr>
240  *     <tr>
241  *         <td style="text-align: center">5</td>
242  *         <td>S</td>
243  *     </tr>
244  *     <tr>
245  *         <td rowspan="4" style="text-align: center">L</td>
246  *         <td style="text-align: center">1..2</td>
247  *         <td>09</td>
248  *         <td rowspan="4"><b>Stand-Alone</b> Month - Use one or two for the numerical month, three for the abbreviation, 
249  *         or four for the full name, or 5 for the narrow name.</td>
250  *     </tr>
251  *     <tr>
252  *         <td style="text-align: center">3</td>
253  *         <td>Sept</td>
254  *     </tr>
255  *     <tr>
256  *         <td style="text-align: center">4</td>
257  *         <td>September</td>
258  *     </tr>
259  *     <tr>
260  *         <td style="text-align: center">5</td>
261  *         <td>S</td>
262  *     </tr>
263  *     <tr>
264  *         <th rowspan="2">week</th>
265  *         <td style="text-align: center">w</td>
266  *         <td style="text-align: center">1..2</td>
267  *         <td>27</td>
268  *         <td>Week of Year.</td>
269  *     </tr>
270  *     <tr>
271  *         <td style="text-align: center">W</td>
272  *         <td style="text-align: center">1</td>
273  *         <td>3</td>
274  *         <td>Week of Month</td>
275  *     </tr>
276  *     <tr>
277  *         <th rowspan="4">day</th>
278  *         <td style="text-align: center">d</td>
279  *         <td style="text-align: center">1..2</td>
280  *         <td>1</td>
281  *         <td>Date - Day of the month</td>
282  *     </tr>
283  *     <tr>
284  *         <td style="text-align: center">D</td>
285  *         <td style="text-align: center">1..3</td>
286  *         <td>345</td>
287  *         <td>Day of year</td>
288  *     </tr>
289  *     <tr>
290  *         <td style="text-align: center">F</td>
291  *         <td style="text-align: center">1</td>
292  *         <td>2</td>
293  *         <td>Day of Week in Month. The example is for the 2nd Wed in July</td>
294  *     </tr>
295  *     <tr>
296  *         <td style="text-align: center">g</td>
297  *         <td style="text-align: center">1..n</td>
298  *         <td>2451334</td>
299  *         <td>Modified Julian day. This is different from the conventional Julian day number in two regards.
300  *         First, it demarcates days at local zone midnight, rather than noon GMT. Second, it is a local number;
301  *         that is, it depends on the local time zone. It can be thought of as a single number that encompasses 
302  *         all the date-related fields.</td>
303  *     </tr>
304  *     <tr>
305  *         <th rowspan="14">week<br>
306  *         day</th>
307  *         <td rowspan="4" style="text-align: center">E</td>
308  *         <td style="text-align: center">1..3</td>
309  *         <td>Tues</td>
310  *         <td rowspan="4">Day of week - Use one through three letters for the short day, or four for the full name, 
311  *         five for the narrow name, or six for the short name.</td>
312  *     </tr>
313  *     <tr>
314  *         <td style="text-align: center">4</td>
315  *         <td>Tuesday</td>
316  *     </tr>
317  *     <tr>
318  *         <td style="text-align: center">5</td>
319  *         <td>T</td>
320  *     </tr>
321  *     <tr>
322  *         <td style="text-align: center">6</td>
323  *         <td>Tu</td>
324  *     </tr>
325  *     <tr>
326  *         <td rowspan="5" style="text-align: center">e</td>
327  *         <td style="text-align: center">1..2</td>
328  *         <td>2</td>
329  *         <td rowspan="5">Local day of week. Same as E except adds a numeric value that will depend on the local
330  *         starting day of the week, using one or two letters. For this example, Monday is the first day of the week.</td>
331  *     </tr>
332  *     <tr>
333  *         <td style="text-align: center">3</td>
334  *         <td>Tues</td>
335  *     </tr>
336  *     <tr>
337  *         <td style="text-align: center">4</td>
338  *         <td>Tuesday</td>
339  *     </tr>
340  *     <tr>
341  *         <td style="text-align: center">5</td>
342  *         <td>T</td>
343  *     </tr>
344  *     <tr>
345  *         <td style="text-align: center">6</td>
346  *         <td>Tu</td>
347  *     </tr>
348  *     <tr>
349  *         <td rowspan="5" style="text-align: center">c</td>
350  *         <td style="text-align: center">1</td>
351  *         <td>2</td>
352  *         <td rowspan="5"><b>Stand-Alone</b> local day of week - Use one letter for the local numeric value (same
353  *         as 'e'), three for the short day, four for the full name, five for the narrow name, or six for
354  *         the short name.</td>
355  *     </tr>
356  *     <tr>
357  *         <td style="text-align: center">3</td>
358  *         <td>Tues</td>
359  *     </tr>
360  *     <tr>
361  *         <td style="text-align: center">4</td>
362  *         <td>Tuesday</td>
363  *     </tr>
364  *     <tr>
365  *         <td style="text-align: center">5</td>
366  *         <td>T</td>
367  *     </tr>
368  *     <tr>
369  *         <td style="text-align: center">6</td>
370  *         <td>Tu</td>
371  *     </tr>
372  *     <tr>
373  *         <th>period</th>
374  *         <td style="text-align: center">a</td>
375  *         <td style="text-align: center">1</td>
376  *         <td>AM</td>
377  *         <td>AM or PM</td>
378  *     </tr>
379  *     <tr>
380  *         <th rowspan="4">hour</th>
381  *         <td style="text-align: center">h</td>
382  *         <td style="text-align: center">1..2</td>
383  *         <td>11</td>
384  *         <td>Hour [1-12]. When used in skeleton data or in a skeleton passed in an API for flexible data pattern
385  *         generation, it should match the 12-hour-cycle format preferred by the locale (h or K); it should not match
386  *         a 24-hour-cycle format (H or k). Use hh for zero padding.</td>
387  *     </tr>
388  *     <tr>
389  *         <td style="text-align: center">H</td>
390  *         <td style="text-align: center">1..2</td>
391  *         <td>13</td>
392  *         <td>Hour [0-23]. When used in skeleton data or in a skeleton passed in an API for flexible data pattern
393  *         generation, it should match the 24-hour-cycle format preferred by the locale (H or k); it should not match a
394  *         12-hour-cycle format (h or K). Use HH for zero padding.</td>
395  *     </tr>
396  *     <tr>
397  *         <td style="text-align: center">K</td>
398  *         <td style="text-align: center">1..2</td>
399  *         <td>0</td>
400  *         <td>Hour [0-11]. When used in a skeleton, only matches K or h, see above. Use KK for zero padding.</td>
401  *     </tr>
402  *     <tr>
403  *         <td style="text-align: center">k</td>
404  *         <td style="text-align: center">1..2</td>
405  *         <td>24</td>
406  *         <td>Hour [1-24]. When used in a skeleton, only matches k or H, see above. Use kk for zero padding.</td>
407  *     </tr>
408  *     <tr>
409  *         <th>minute</th>
410  *         <td style="text-align: center">m</td>
411  *         <td style="text-align: center">1..2</td>
412  *         <td>59</td>
413  *         <td>Minute. Use one or two for zero padding.</td>
414  *     </tr>
415  *     <tr>
416  *         <th rowspan="3">second</th>
417  *         <td style="text-align: center">s</td>
418  *         <td style="text-align: center">1..2</td>
419  *         <td>12</td>
420  *         <td>Second. Use one or two for zero padding.</td>
421  *     </tr>
422  *     <tr>
423  *         <td style="text-align: center">S</td>
424  *         <td style="text-align: center">1..n</td>
425  *         <td>3456</td>
426  *         <td>Fractional Second - truncates (like other time fields) to the count of letters.
427  *         (example shows display using pattern SSSS for seconds value 12.34567)</td>
428  *     </tr>
429  *     <tr>
430  *         <td style="text-align: center">A</td>
431  *         <td style="text-align: center">1..n</td>
432  *         <td>69540000</td>
433  *         <td>Milliseconds in day. This field behaves <i>exactly</i> like a composite of all time-related fields,
434  *         not including the zone fields. As such, it also reflects discontinuities of those fields on DST transition
435  *         days. On a day of DST onset, it will jump forward. On a day of DST cessation, it will jump backward. This
436  *         reflects the fact that is must be combined with the offset field to obtain a unique local time value.</td>
437  *     </tr>
438  *     <tr>
439  *         <th rowspan="23">zone</th>
440  *         <td rowspan="2" style="text-align: center">z</td>
441  *         <td style="text-align: center">1..3</td>
442  *         <td>PDT</td>
443  *         <td>The <i>short specific non-location format</i>.
444  *         Where that is unavailable, falls back to the <i>short localized GMT format</i> ("O").</td>
445  *     </tr>
446  *     <tr>
447  *         <td style="text-align: center">4</td>
448  *         <td>Pacific Daylight Time</td>
449  *         <td>The <i>long specific non-location format</i>.
450  *         Where that is unavailable, falls back to the <i>long localized GMT format</i> ("OOOO").</td>
451  *     </tr>
452  *     <tr>
453  *         <td rowspan="3" style="text-align: center">Z</td>
454  *         <td style="text-align: center">1..3</td>
455  *         <td>-0800</td>
456  *         <td>The <i>ISO8601 basic format</i> with hours, minutes and optional seconds fields.
457  *         The format is equivalent to RFC 822 zone format (when optional seconds field is absent).
458  *         This is equivalent to the "xxxx" specifier.</td>
459  *     </tr>
460  *     <tr>
461  *         <td style="text-align: center">4</td>
462  *         <td>GMT-8:00</td>
463  *         <td>The <i>long localized GMT format</i>.
464  *         This is equivalent to the "OOOO" specifier.</td>
465  *     </tr>
466  *     <tr>
467  *         <td style="text-align: center">5</td>
468  *         <td>-08:00<br>
469  *         -07:52:58</td>
470  *         <td>The <i>ISO8601 extended format</i> with hours, minutes and optional seconds fields.
471  *         The ISO8601 UTC indicator "Z" is used when local time offset is 0.
472  *         This is equivalent to the "XXXXX" specifier.</td>
473  *     </tr>
474  *     <tr>
475  *         <td rowspan="2" style="text-align: center">O</td>
476  *         <td style="text-align: center">1</td>
477  *         <td>GMT-8</td>
478  *         <td>The <i>short localized GMT format</i>.</td>
479  *     </tr>
480  *     <tr>
481  *         <td style="text-align: center">4</td>
482  *         <td>GMT-08:00</td>
483  *         <td>The <i>long localized GMT format</i>.</td>
484  *     </tr>
485  *     <tr>
486  *         <td rowspan="2" style="text-align: center">v</td>
487  *         <td style="text-align: center">1</td>
488  *         <td>PT</td>
489  *         <td>The <i>short generic non-location format</i>.
490  *         Where that is unavailable, falls back to the <i>generic location format</i> ("VVVV"),
491  *         then the <i>short localized GMT format</i> as the final fallback.</td>
492  *     </tr>
493  *     <tr>
494  *         <td style="text-align: center">4</td>
495  *         <td>Pacific Time</td>
496  *         <td>The <i>long generic non-location format</i>.
497  *         Where that is unavailable, falls back to <i>generic location format</i> ("VVVV").
498  *     </tr>
499  *     <tr>
500  *         <td rowspan="4" style="text-align: center">V</td>
501  *         <td style="text-align: center">1</td>
502  *         <td>uslax</td>
503  *         <td>The short time zone ID.
504  *         Where that is unavailable, the special short time zone ID <i>unk</i> (Unknown Zone) is used.<br>
505  *         <i><b>Note</b>: This specifier was originally used for a variant of the short specific non-location format,
506  *         but it was deprecated in the later version of the LDML specification. In CLDR 23/ICU 51, the definition of
507  *         the specifier was changed to designate a short time zone ID.</i></td>
508  *     </tr>
509  *     <tr>
510  *         <td style="text-align: center">2</td>
511  *         <td>America/Los_Angeles</td>
512  *         <td>The long time zone ID.</td>
513  *     </tr>
514  *     <tr>
515  *         <td style="text-align: center">3</td>
516  *         <td>Los Angeles</td>
517  *         <td>The exemplar city (location) for the time zone.
518  *         Where that is unavailable, the localized exemplar city name for the special zone <i>Etc/Unknown</i> is used
519  *         as the fallback (for example, "Unknown City"). </td>
520  *     </tr>
521  *     <tr>
522  *         <td style="text-align: center">4</td>
523  *         <td>Los Angeles Time</td>
524  *         <td>The <i>generic location format</i>.
525  *         Where that is unavailable, falls back to the <i>long localized GMT format</i> ("OOOO";
526  *         Note: Fallback is only necessary with a GMT-style Time Zone ID, like Etc/GMT-830.)<br>
527  *         This is especially useful when presenting possible timezone choices for user selection, 
528  *         since the naming is more uniform than the "v" format.</td>
529  *     </tr>
530  *     <tr>
531  *         <td rowspan="5" style="text-align: center">X</td>
532  *         <td style="text-align: center">1</td>
533  *         <td>-08<br>
534  *         +0530<br>
535  *         Z</td>
536  *         <td>The <i>ISO8601 basic format</i> with hours field and optional minutes field.
537  *         The ISO8601 UTC indicator "Z" is used when local time offset is 0.</td>
538  *     </tr>
539  *     <tr>
540  *         <td style="text-align: center">2</td>
541  *         <td>-0800<br>
542  *         Z</td>
543  *         <td>The <i>ISO8601 basic format</i> with hours and minutes fields.
544  *         The ISO8601 UTC indicator "Z" is used when local time offset is 0.</td>
545  *     </tr>
546  *     <tr>
547  *         <td style="text-align: center">3</td>
548  *         <td>-08:00<br>
549  *         Z</td>
550  *         <td>The <i>ISO8601 extended format</i> with hours and minutes fields.
551  *         The ISO8601 UTC indicator "Z" is used when local time offset is 0.</td>
552  *     </tr>
553  *     <tr>
554  *         <td style="text-align: center">4</td>
555  *         <td>-0800<br>
556  *         -075258<br>
557  *         Z</td>
558  *         <td>The <i>ISO8601 basic format</i> with hours, minutes and optional seconds fields.
559  *         (Note: The seconds field is not supported by the ISO8601 specification.)
560  *         The ISO8601 UTC indicator "Z" is used when local time offset is 0.</td>
561  *     </tr>
562  *     <tr>
563  *         <td style="text-align: center">5</td>
564  *         <td>-08:00<br>
565  *         -07:52:58<br>
566  *         Z</td>
567  *         <td>The <i>ISO8601 extended format</i> with hours, minutes and optional seconds fields.
568  *         (Note: The seconds field is not supported by the ISO8601 specification.)
569  *         The ISO8601 UTC indicator "Z" is used when local time offset is 0.</td>
570  *     </tr>
571  *     <tr>
572  *         <td rowspan="5" style="text-align: center">x</td>
573  *         <td style="text-align: center">1</td>
574  *         <td>-08<br>
575  *         +0530</td>
576  *         <td>The <i>ISO8601 basic format</i> with hours field and optional minutes field.</td>
577  *     </tr>
578  *     <tr>
579  *         <td style="text-align: center">2</td>
580  *         <td>-0800</td>
581  *         <td>The <i>ISO8601 basic format</i> with hours and minutes fields.</td>
582  *     </tr>
583  *     <tr>
584  *         <td style="text-align: center">3</td>
585  *         <td>-08:00</td>
586  *         <td>The <i>ISO8601 extended format</i> with hours and minutes fields.</td>
587  *     </tr>
588  *     <tr>
589  *         <td style="text-align: center">4</td>
590  *         <td>-0800<br>
591  *         -075258</td>
592  *         <td>The <i>ISO8601 basic format</i> with hours, minutes and optional seconds fields.
593  *         (Note: The seconds field is not supported by the ISO8601 specification.)</td>
594  *     </tr>
595  *     <tr>
596  *         <td style="text-align: center">5</td>
597  *         <td>-08:00<br>
598  *         -07:52:58</td>
599  *         <td>The <i>ISO8601 extended format</i> with hours, minutes and optional seconds fields.
600  *         (Note: The seconds field is not supported by the ISO8601 specification.)</td>
601  *     </tr>
602  * </table>
603  * 
604  * </blockquote>
605  * <p>
606  * Any characters in the pattern that are not in the ranges of ['a'..'z']
607  * and ['A'..'Z'] will be treated as quoted text. For instance, characters
608  * like ':', '.', ' ', '#' and '@' will appear in the resulting time text
609  * even they are not embraced within single quotes.
610  * <p>
611  * A pattern containing any invalid pattern letter will result in a thrown
612  * exception during formatting or parsing.
613  *
614  * <p>
615  * <strong>Examples Using the US Locale:</strong>
616  * <blockquote>
617  * <pre>
618  * Format Pattern                         Result
619  * --------------                         -------
620  * "yyyy.MM.dd G 'at' HH:mm:ss vvvv" ->>  1996.07.10 AD at 15:08:56 Pacific Time
621  * "EEE, MMM d, ''yy"                ->>  Wed, July 10, '96
622  * "h:mm a"                          ->>  12:08 PM
623  * "hh 'o''clock' a, zzzz"           ->>  12 o'clock PM, Pacific Daylight Time
624  * "K:mm a, vvv"                     ->>  0:00 PM, PT
625  * "yyyyy.MMMMM.dd GGG hh:mm aaa"    ->>  01996.July.10 AD 12:08 PM
626  * </pre>
627  * </blockquote>
628  * <strong>Code Sample:</strong>
629  * <blockquote>
630  * <pre>
631  * SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, "PST");
632  * pdt.setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2*60*60*1000);
633  * pdt.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2*60*60*1000);
634  * <br>
635  * // Format the current time.
636  * SimpleDateFormat formatter
637  *     = new SimpleDateFormat ("yyyy.MM.dd G 'at' hh:mm:ss a zzz");
638  * Date currentTime_1 = new Date();
639  * String dateString = formatter.format(currentTime_1);
640  * <br>
641  * // Parse the previous string back into a Date.
642  * ParsePosition pos = new ParsePosition(0);
643  * Date currentTime_2 = formatter.parse(dateString, pos);
644  * </pre>
645  * </blockquote>
646  * In the example, the time value <code>currentTime_2</code> obtained from
647  * parsing will be equal to <code>currentTime_1</code>. However, they may not be
648  * equal if the am/pm marker 'a' is left out from the format pattern while
649  * the "hour in am/pm" pattern symbol is used. This information loss can
650  * happen when formatting the time in PM.
651  *
652  * <p>When parsing a date string using the abbreviated year pattern ("yy"),
653  * SimpleDateFormat must interpret the abbreviated year
654  * relative to some century.  It does this by adjusting dates to be
655  * within 80 years before and 20 years after the time the SimpleDateFormat
656  * instance is created. For example, using a pattern of "MM/dd/yy" and a
657  * SimpleDateFormat instance created on Jan 1, 1997,  the string
658  * "01/11/12" would be interpreted as Jan 11, 2012 while the string "05/04/64"
659  * would be interpreted as May 4, 1964.
660  * During parsing, only strings consisting of exactly two digits, as defined by
661  * {@link com.ibm.icu.lang.UCharacter#isDigit(int)}, will be parsed into the default
662  * century.
663  * Any other numeric string, such as a one digit string, a three or more digit
664  * string, or a two digit string that isn't all digits (for example, "-1"), is
665  * interpreted literally.  So "01/02/3" or "01/02/003" are parsed, using the
666  * same pattern, as Jan 2, 3 AD.  Likewise, "01/02/-3" is parsed as Jan 2, 4 BC.
667  *
668  * <p>If the year pattern does not have exactly two 'y' characters, the year is
669  * interpreted literally, regardless of the number of digits.  So using the
670  * pattern "MM/dd/yyyy", "01/11/12" parses to Jan 11, 12 A.D.
671  *
672  * <p>When numeric fields abut one another directly, with no intervening delimiter
673  * characters, they constitute a run of abutting numeric fields.  Such runs are
674  * parsed specially.  For example, the format "HHmmss" parses the input text
675  * "123456" to 12:34:56, parses the input text "12345" to 1:23:45, and fails to
676  * parse "1234".  In other words, the leftmost field of the run is flexible,
677  * while the others keep a fixed width.  If the parse fails anywhere in the run,
678  * then the leftmost field is shortened by one character, and the entire run is
679  * parsed again. This is repeated until either the parse succeeds or the
680  * leftmost field is one character in length.  If the parse still fails at that
681  * point, the parse of the run fails.
682  *
683  * <p>For time zones that have no names, use strings GMT+hours:minutes or
684  * GMT-hours:minutes.
685  *
686  * <p>The calendar defines what is the first day of the week, the first week
687  * of the year, whether hours are zero based or not (0 vs 12 or 24), and the
688  * time zone. There is one common decimal format to handle all the numbers;
689  * the digit count is handled programmatically according to the pattern.
690  *
691  * <h4>Synchronization</h4>
692  *
693  * Date formats are not synchronized. It is recommended to create separate
694  * format instances for each thread. If multiple threads access a format
695  * concurrently, it must be synchronized externally.
696  *
697  * @see          com.ibm.icu.util.Calendar
698  * @see          com.ibm.icu.util.GregorianCalendar
699  * @see          com.ibm.icu.util.TimeZone
700  * @see          DateFormat
701  * @see          DateFormatSymbols
702  * @see          DecimalFormat
703  * @see          TimeZoneFormat
704  * @author       Mark Davis, Chen-Lieh Huang, Alan Liu
705  * @stable ICU 2.0
706  */
707 public class SimpleDateFormat extends DateFormat {
708
709     // the official serial version ID which says cryptically
710     // which version we're compatible with
711     private static final long serialVersionUID = 4774881970558875024L;
712
713     // the internal serial version which says which version was written
714     // - 0 (default) for version up to JDK 1.1.3
715     // - 1 for version from JDK 1.1.4, which includes a new field
716     // - 2 we write additional int for capitalizationContext
717     static final int currentSerialVersion = 2;
718
719     static boolean DelayedHebrewMonthCheck = false;
720
721     /*
722      * From calendar field to its level.
723      * Used to order calendar field.
724      * For example, calendar fields can be defined in the following order:
725      * year >  month > date > am-pm > hour >  minute
726      * YEAR --> 10, MONTH -->20, DATE --> 30;
727      * AM_PM -->40, HOUR --> 50, MINUTE -->60
728      */
729     private static final int[] CALENDAR_FIELD_TO_LEVEL =
730     {
731         /*GyM*/ 0, 10, 20,
732         /*wW*/ 20, 30,
733         /*dDEF*/ 30, 20, 30, 30,
734         /*ahHm*/ 40, 50, 50, 60,
735         /*sS..*/ 70, 80,
736         /*z?Y*/ 0, 0, 10,
737         /*eug*/ 30, 10, 0,
738         /*A*/ 40
739     };
740
741
742
743     /*
744      * From calendar field letter to its level.
745      * Used to order calendar field.
746      * For example, calendar fields can be defined in the following order:
747      * year >  month > date > am-pm > hour >  minute
748      * 'y' --> 10, 'M' -->20, 'd' --> 30; 'a' -->40, 'h' --> 50, 'm' -->60
749      */
750     private static final int[] PATTERN_CHAR_TO_LEVEL =
751     {
752     //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
753         -1, 40, -1, -1, 20, 30, 30,  0, 50, -1, -1, 50, 20, 20, -1,  0,
754     //   P   Q   R   S   T   U   V   W   X   Y   Z
755         -1, 20, -1, 80, -1, 10,  0, 30,  0, 10,  0, -1, -1, -1, -1, -1,
756     //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
757         -1, 40, -1, 30, 30, 30, -1,  0, 50, -1, -1, 50, -1, 60, -1, -1,
758     //   p   q   r   s   t   u   v   w   x   y   z
759         -1, 20, -1, 70, -1, 10,  0, 20,  0, 10,  0, -1, -1, -1, -1, -1
760     };
761
762     // When calendar uses hebr numbering (i.e. he@calendar=hebrew),
763     // offset the years within the current millenium down to 1-999
764     private static final int HEBREW_CAL_CUR_MILLENIUM_START_YEAR = 5000;
765     private static final int HEBREW_CAL_CUR_MILLENIUM_END_YEAR = 6000;
766
767     /**
768      * The version of the serialized data on the stream.  Possible values:
769      * <ul>
770      * <li><b>0</b> or not present on stream: JDK 1.1.3.  This version
771      * has no <code>defaultCenturyStart</code> on stream.
772      * <li><b>1</b> JDK 1.1.4 or later.  This version adds
773      * <code>defaultCenturyStart</code>.
774      * <li><b>2</b> This version writes an additional int for
775      * <code>capitalizationContext</code>.
776      * </ul>
777      * When streaming out this class, the most recent format
778      * and the highest allowable <code>serialVersionOnStream</code>
779      * is written.
780      * @serial
781      */
782     private int serialVersionOnStream = currentSerialVersion;
783
784     /**
785      * The pattern string of this formatter.  This is always a non-localized
786      * pattern.  May not be null.  See class documentation for details.
787      * @serial
788      */
789     private String pattern;
790
791     /**
792      * The override string of this formatter.  Used to override the
793      * numbering system for one or more fields.
794      * @serial
795      */
796     private String override;
797
798     /**
799      * The hash map used for number format overrides.
800      * @serial
801      */
802     private HashMap<String, NumberFormat> numberFormatters;
803
804     /**
805      * The hash map used for number format overrides.
806      * @serial
807      */
808     private HashMap<Character, String> overrideMap;
809
810     /**
811      * The symbols used by this formatter for week names, month names,
812      * etc.  May not be null.
813      * @serial
814      * @see DateFormatSymbols
815      */
816     private DateFormatSymbols formatData;
817
818     private transient ULocale locale;
819
820     /**
821      * We map dates with two-digit years into the century starting at
822      * <code>defaultCenturyStart</code>, which may be any date.  May
823      * not be null.
824      * @serial
825      * @since JDK1.1.4
826      */
827     private Date defaultCenturyStart;
828
829     private transient int defaultCenturyStartYear;
830
831     // defaultCenturyBase is set when an instance is created
832     // and may be used for calculating defaultCenturyStart when needed.
833     private transient long defaultCenturyBase;
834
835     // We need to preserve time zone type when parsing specific
836     // time zone text (xxx Standard Time vs xxx Daylight Time)
837     private transient TimeType tztype = TimeType.UNKNOWN;
838
839     private static final int millisPerHour = 60 * 60 * 1000;
840
841     // When possessing ISO format, the ERA may be ommitted is the
842     // year specifier is a negative number.
843     private static final int ISOSpecialEra = -32000;
844     
845     // This prefix is designed to NEVER MATCH real text, in order to
846     // suppress the parsing of negative numbers.  Adjust as needed (if
847     // this becomes valid Unicode).
848     private static final String SUPPRESS_NEGATIVE_PREFIX = "\uAB00";
849
850     /**
851      * If true, this object supports fast formatting using the
852      * subFormat variant that takes a StringBuffer.
853      */
854     private transient boolean useFastFormat;
855
856     /*
857      *  The time zone sub-formatter, introduced in ICU 4.8
858      */
859     private volatile TimeZoneFormat tzFormat;
860
861     /*
862      *  Capitalization setting, introduced in ICU 50
863      *  Special serialization, see writeObject & readObject below
864      */
865     private transient DisplayContext capitalizationSetting;
866
867     /*
868      *  Old defaultCapitalizationContext field
869      *  from ICU 49.1:
870      */
871     //private ContextValue defaultCapitalizationContext;
872     /**
873      *  Old ContextValue enum, preserved only to avoid
874      *  deserialization errs from ICU 49.1.
875      */
876     @SuppressWarnings("unused")
877     private enum ContextValue {
878         UNKNOWN,
879         CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE,
880         CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE,
881         CAPITALIZATION_FOR_UI_LIST_OR_MENU,
882         CAPITALIZATION_FOR_STANDALONE
883     }
884
885     /**
886      * Constructs a SimpleDateFormat using the default pattern for the default <code>FORMAT</code>
887      * locale.  <b>Note:</b> Not all locales support SimpleDateFormat; for full
888      * generality, use the factory methods in the DateFormat class.
889      *
890      * @see DateFormat
891      * @see Category#FORMAT
892      * @stable ICU 2.0
893      */
894     public SimpleDateFormat() {
895         this(getDefaultPattern(), null, null, null, null, true, null);
896     }
897
898     /**
899      * Constructs a SimpleDateFormat using the given pattern in the default <code>FORMAT</code>
900      * locale.  <b>Note:</b> Not all locales support SimpleDateFormat; for full
901      * generality, use the factory methods in the DateFormat class.
902      * @see Category#FORMAT
903      * @stable ICU 2.0
904      */
905     public SimpleDateFormat(String pattern)
906     {
907         this(pattern, null, null, null, null, true, null);
908     }
909
910     /**
911      * Constructs a SimpleDateFormat using the given pattern and locale.
912      * <b>Note:</b> Not all locales support SimpleDateFormat; for full
913      * generality, use the factory methods in the DateFormat class.
914      * @stable ICU 2.0
915      */
916     public SimpleDateFormat(String pattern, Locale loc)
917     {
918         this(pattern, null, null, null, ULocale.forLocale(loc), true, null);
919     }
920
921     /**
922      * Constructs a SimpleDateFormat using the given pattern and locale.
923      * <b>Note:</b> Not all locales support SimpleDateFormat; for full
924      * generality, use the factory methods in the DateFormat class.
925      * @stable ICU 3.2
926      */
927     public SimpleDateFormat(String pattern, ULocale loc)
928     {
929         this(pattern, null, null, null, loc, true, null);
930     }
931
932     /**
933      * Constructs a SimpleDateFormat using the given pattern , override and locale.
934      * @param pattern The pattern to be used
935      * @param override The override string.  A numbering system override string can take one of the following forms:
936      *     1). If just a numbering system name is specified, it applies to all numeric fields in the date format pattern.
937      *     2). To specify an alternate numbering system on a field by field basis, use the field letters from the pattern
938      *         followed by an = sign, followed by the numbering system name.  For example, to specify that just the year
939      *         be formatted using Hebrew digits, use the override "y=hebr".  Multiple overrides can be specified in a single
940      *         string by separating them with a semi-colon. For example, the override string "m=thai;y=deva" would format using
941      *         Thai digits for the month and Devanagari digits for the year.
942      * @param loc The locale to be used
943      * @stable ICU 4.2
944      */
945     public SimpleDateFormat(String pattern, String override, ULocale loc)
946     {
947         this(pattern, null, null, null, loc, false,override);
948     }
949
950     /**
951      * Constructs a SimpleDateFormat using the given pattern and
952      * locale-specific symbol data.
953      * Warning: uses default <code>FORMAT</code> locale for digits!
954      * @stable ICU 2.0
955      */
956     public SimpleDateFormat(String pattern, DateFormatSymbols formatData)
957     {
958         this(pattern, (DateFormatSymbols)formatData.clone(), null, null, null, true, null);
959     }
960
961     /**
962      * @internal
963      * @deprecated This API is ICU internal only.
964      */
965     public SimpleDateFormat(String pattern, DateFormatSymbols formatData, ULocale loc)
966     {
967         this(pattern, (DateFormatSymbols)formatData.clone(), null, null, loc, true,null);
968     }
969
970     /**
971      * Package-private constructor that allows a subclass to specify
972      * whether it supports fast formatting.
973      *
974      * TODO make this API public.
975      */
976     SimpleDateFormat(String pattern, DateFormatSymbols formatData, Calendar calendar, ULocale locale,
977                      boolean useFastFormat, String override) {
978         this(pattern, (DateFormatSymbols)formatData.clone(), (Calendar)calendar.clone(), null, locale, useFastFormat,override);
979     }
980
981     /*
982      * The constructor called from all other SimpleDateFormat constructors
983      */
984     private SimpleDateFormat(String pattern, DateFormatSymbols formatData, Calendar calendar,
985             NumberFormat numberFormat, ULocale locale, boolean useFastFormat,String override) {
986         this.pattern = pattern;
987         this.formatData = formatData;
988         this.calendar = calendar;
989         this.numberFormat = numberFormat;
990         this.locale = locale; // time zone formatting
991         this.useFastFormat = useFastFormat;
992         this.override = override;
993         initialize();
994     }
995
996     /**
997      * Creates an instance of SimpleDateFormat for the given format configuration
998      * @param formatConfig the format configuration
999      * @return A SimpleDateFormat instance
1000      * @internal
1001      * @deprecated This API is ICU internal only.
1002      */
1003     public static SimpleDateFormat getInstance(Calendar.FormatConfiguration formatConfig) {
1004
1005         String ostr = formatConfig.getOverrideString();
1006         boolean useFast = ( ostr != null && ostr.length() > 0 );
1007
1008         return new SimpleDateFormat(formatConfig.getPatternString(),
1009                     formatConfig.getDateFormatSymbols(),
1010                     formatConfig.getCalendar(),
1011                     null,
1012                     formatConfig.getLocale(),
1013                     useFast,
1014                     formatConfig.getOverrideString());
1015     }
1016
1017     /*
1018      * Initialized fields
1019      */
1020     private void initialize() {
1021         if (locale == null) {
1022             locale = ULocale.getDefault(Category.FORMAT);
1023         }
1024         if (formatData == null) {
1025             formatData = new DateFormatSymbols(locale);
1026         }
1027         if (calendar == null) {
1028             calendar = Calendar.getInstance(locale);
1029         }
1030         if (numberFormat == null) {
1031             NumberingSystem ns = NumberingSystem.getInstance(locale);
1032             if (ns.isAlgorithmic()) {
1033                 numberFormat = NumberFormat.getInstance(locale);
1034             } else {
1035                 String digitString = ns.getDescription();
1036                 String nsName = ns.getName();
1037                 // Use a NumberFormat optimized for date formatting
1038                 numberFormat = new DateNumberFormat(locale, digitString, nsName);
1039             }
1040         }
1041         // Note: deferring calendar calculation until when we really need it.
1042         // Instead, we just record time of construction for backward compatibility.
1043         defaultCenturyBase = System.currentTimeMillis();
1044
1045         setLocale(calendar.getLocale(ULocale.VALID_LOCALE ), calendar.getLocale(ULocale.ACTUAL_LOCALE));
1046         initLocalZeroPaddingNumberFormat();
1047
1048         if (override != null) {
1049            initNumberFormatters(locale);
1050         }
1051         
1052         capitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
1053
1054     }
1055
1056     /**
1057      * Private method lazily instantiate the TimeZoneFormat field
1058      * @param bForceUpdate when true, check if tzFormat is synchronized with
1059      * the current numberFormat and update its digits if necessary. When false,
1060      * this check is skipped.
1061      */
1062     private synchronized void initializeTimeZoneFormat(boolean bForceUpdate) {
1063         if (bForceUpdate || tzFormat == null) {
1064             tzFormat = TimeZoneFormat.getInstance(locale);
1065
1066             String digits = null;
1067             if (numberFormat instanceof DecimalFormat) {
1068                 DecimalFormatSymbols decsym = ((DecimalFormat) numberFormat).getDecimalFormatSymbols();
1069                 digits = new String(decsym.getDigits());
1070             } else if (numberFormat instanceof DateNumberFormat) {
1071                 digits = new String(((DateNumberFormat)numberFormat).getDigits());
1072             }
1073
1074             if (digits != null) {
1075                 if (!tzFormat.getGMTOffsetDigits().equals(digits)) {
1076                     if (tzFormat.isFrozen()) {
1077                         tzFormat = tzFormat.cloneAsThawed();
1078                     }
1079                     tzFormat.setGMTOffsetDigits(digits);
1080                 }
1081             }
1082         }
1083     }
1084
1085     /**
1086      * Private method, returns non-null TimeZoneFormat.
1087      * @return the TimeZoneFormat used by this formatter.
1088      */
1089     private TimeZoneFormat tzFormat() {
1090         if (tzFormat == null) {
1091             initializeTimeZoneFormat(false);
1092         }
1093         return tzFormat;
1094     }
1095
1096     // privates for the default pattern
1097     private static ULocale cachedDefaultLocale = null;
1098     private static String cachedDefaultPattern = null;
1099     private static final String FALLBACKPATTERN = "yy/MM/dd HH:mm";
1100
1101     /*
1102      * Returns the default date and time pattern (SHORT) for the default locale.
1103      * This method is only used by the default SimpleDateFormat constructor.
1104      */
1105     private static synchronized String getDefaultPattern() {
1106         ULocale defaultLocale = ULocale.getDefault(Category.FORMAT);
1107         if (!defaultLocale.equals(cachedDefaultLocale)) {
1108             cachedDefaultLocale = defaultLocale;
1109             Calendar cal = Calendar.getInstance(cachedDefaultLocale);
1110             try {
1111                 CalendarData calData = new CalendarData(cachedDefaultLocale, cal.getType());
1112                 String[] dateTimePatterns = calData.getDateTimePatterns();
1113                 int glueIndex = 8;
1114                 if (dateTimePatterns.length >= 13)
1115                 {
1116                     glueIndex += (SHORT + 1);
1117                 }
1118                 cachedDefaultPattern = MessageFormat.format(dateTimePatterns[glueIndex],
1119                         new Object[] {dateTimePatterns[SHORT], dateTimePatterns[SHORT + 4]});
1120             } catch (MissingResourceException e) {
1121                 cachedDefaultPattern = FALLBACKPATTERN;
1122             }
1123         }
1124         return cachedDefaultPattern;
1125     }
1126
1127     /* Define one-century window into which to disambiguate dates using
1128      * two-digit years.
1129      */
1130     private void parseAmbiguousDatesAsAfter(Date startDate) {
1131         defaultCenturyStart = startDate;
1132         calendar.setTime(startDate);
1133         defaultCenturyStartYear = calendar.get(Calendar.YEAR);
1134     }
1135
1136     /* Initialize defaultCenturyStart and defaultCenturyStartYear by base time.
1137      * The default start time is 80 years before the creation time of this object.
1138      */
1139     private void initializeDefaultCenturyStart(long baseTime) {
1140         defaultCenturyBase = baseTime;
1141         // clone to avoid messing up date stored in calendar object
1142         // when this method is called while parsing
1143         Calendar tmpCal = (Calendar)calendar.clone();
1144         tmpCal.setTimeInMillis(baseTime);
1145         tmpCal.add(Calendar.YEAR, -80);
1146         defaultCenturyStart = tmpCal.getTime();
1147         defaultCenturyStartYear = tmpCal.get(Calendar.YEAR);
1148     }
1149
1150     /* Gets the default century start date for this object */
1151     private Date getDefaultCenturyStart() {
1152         if (defaultCenturyStart == null) {
1153             // not yet initialized
1154             initializeDefaultCenturyStart(defaultCenturyBase);
1155         }
1156         return defaultCenturyStart;
1157     }
1158
1159     /* Gets the default century start year for this object */
1160     private int getDefaultCenturyStartYear() {
1161         if (defaultCenturyStart == null) {
1162             // not yet initialized
1163             initializeDefaultCenturyStart(defaultCenturyBase);
1164         }
1165         return defaultCenturyStartYear;
1166     }
1167
1168     /**
1169      * Sets the 100-year period 2-digit years will be interpreted as being in
1170      * to begin on the date the user specifies.
1171      * @param startDate During parsing, two digit years will be placed in the range
1172      * <code>startDate</code> to <code>startDate + 100 years</code>.
1173      * @stable ICU 2.0
1174      */
1175     public void set2DigitYearStart(Date startDate) {
1176         parseAmbiguousDatesAsAfter(startDate);
1177     }
1178
1179     /**
1180      * Returns the beginning date of the 100-year period 2-digit years are interpreted
1181      * as being within.
1182      * @return the start of the 100-year period into which two digit years are
1183      * parsed
1184      * @stable ICU 2.0
1185      */
1186     public Date get2DigitYearStart() {
1187         return getDefaultCenturyStart();
1188     }
1189
1190     /**
1191      * Formats a date or time, which is the standard millis
1192      * since January 1, 1970, 00:00:00 GMT.
1193      * <p>Example: using the US locale:
1194      * "yyyy.MM.dd G 'at' HH:mm:ss zzz" ->> 1996.07.10 AD at 15:08:56 PDT
1195      * @param cal the calendar whose date-time value is to be formatted into a date-time string
1196      * @param toAppendTo where the new date-time text is to be appended
1197      * @param pos the formatting position. On input: an alignment field,
1198      * if desired. On output: the offsets of the alignment field.
1199      * @return the formatted date-time string.
1200      * @see DateFormat
1201      * @stable ICU 2.0
1202      */
1203     public StringBuffer format(Calendar cal, StringBuffer toAppendTo,
1204                                FieldPosition pos) {
1205         TimeZone backupTZ = null;
1206         if (cal != calendar && !cal.getType().equals(calendar.getType())) {
1207             // Different calendar type
1208             // We use the time and time zone from the input calendar, but
1209             // do not use the input calendar for field calculation.
1210             calendar.setTimeInMillis(cal.getTimeInMillis());
1211             backupTZ = calendar.getTimeZone();
1212             calendar.setTimeZone(cal.getTimeZone());
1213             cal = calendar;
1214         }
1215         StringBuffer result = format(cal, capitalizationSetting, toAppendTo, pos, null);
1216         if (backupTZ != null) {
1217             // Restore the original time zone
1218             calendar.setTimeZone(backupTZ);
1219         }
1220         return result;
1221     }
1222
1223     // The actual method to format date. If List attributes is not null,
1224     // then attribute information will be recorded.
1225     private StringBuffer format(Calendar cal, DisplayContext capitalizationContext,
1226             StringBuffer toAppendTo, FieldPosition pos, List<FieldPosition> attributes) {
1227         // Initialize
1228         pos.setBeginIndex(0);
1229         pos.setEndIndex(0);
1230
1231         // Careful: For best performance, minimize the number of calls
1232         // to StringBuffer.append() by consolidating appends when
1233         // possible.
1234
1235         Object[] items = getPatternItems();
1236         for (int i = 0; i < items.length; i++) {
1237             if (items[i] instanceof String) {
1238                 toAppendTo.append((String)items[i]);
1239             } else {
1240                 PatternItem item = (PatternItem)items[i];
1241                 int start = 0;
1242                 if (attributes != null) {
1243                     // Save the current length
1244                     start = toAppendTo.length();
1245                 }
1246                 if (useFastFormat) {
1247                     subFormat(toAppendTo, item.type, item.length, toAppendTo.length(),
1248                               i, capitalizationContext, pos, cal);
1249                 } else {
1250                     toAppendTo.append(subFormat(item.type, item.length, toAppendTo.length(),
1251                                                 i, capitalizationContext, pos, cal));
1252                 }
1253                 if (attributes != null) {
1254                     // Check the sub format length
1255                     int end = toAppendTo.length();
1256                     if (end - start > 0) {
1257                         // Append the attribute to the list
1258                         DateFormat.Field attr = patternCharToDateFormatField(item.type);
1259                         FieldPosition fp = new FieldPosition(attr);
1260                         fp.setBeginIndex(start);
1261                         fp.setEndIndex(end);
1262                         attributes.add(fp);
1263                     }
1264                 }
1265             }
1266         }
1267         return toAppendTo;
1268
1269     }
1270
1271     // Map pattern character to index
1272     private static final int PATTERN_CHAR_BASE = 0x40;
1273     private static final int[] PATTERN_CHAR_TO_INDEX =
1274     {
1275     //       A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
1276         -1, 22, -1, -1, 10,  9, 11,  0,  5, -1, -1, 16, 26,  2, -1, 31,
1277     //   P   Q   R   S   T   U   V   W   X   Y   Z
1278         -1, 27, -1,  8, -1, 30, 29, 13, 32, 18, 23, -1, -1, -1, -1, -1,
1279     //       a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
1280         -1, 14, -1, 25,  3, 19, -1, 21, 15, -1, -1,  4, -1,  6, -1, -1,
1281     //   p   q   r   s   t   u   v   w   x   y   z
1282         -1, 28, -1,  7, -1, 20, 24, 12, 33,  1, 17, -1, -1, -1, -1, -1
1283     };
1284
1285     // Map pattern character index to Calendar field number
1286     private static final int[] PATTERN_INDEX_TO_CALENDAR_FIELD =
1287     {
1288         /*GyM*/ Calendar.ERA, Calendar.YEAR, Calendar.MONTH,
1289         /*dkH*/ Calendar.DATE, Calendar.HOUR_OF_DAY, Calendar.HOUR_OF_DAY,
1290         /*msS*/ Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND,
1291         /*EDF*/ Calendar.DAY_OF_WEEK, Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK_IN_MONTH,
1292         /*wWa*/ Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH, Calendar.AM_PM,
1293         /*hKz*/ Calendar.HOUR, Calendar.HOUR, Calendar.ZONE_OFFSET,
1294         /*Yeu*/ Calendar.YEAR_WOY, Calendar.DOW_LOCAL, Calendar.EXTENDED_YEAR,
1295         /*gAZ*/ Calendar.JULIAN_DAY, Calendar.MILLISECONDS_IN_DAY, Calendar.ZONE_OFFSET,
1296         /*v*/   Calendar.ZONE_OFFSET,
1297         /*c*/   Calendar.DOW_LOCAL,
1298         /*L*/   Calendar.MONTH,
1299         /*Qq*/  Calendar.MONTH, Calendar.MONTH,
1300         /*V*/   Calendar.ZONE_OFFSET,
1301         /*U*/   Calendar.YEAR,
1302         /*O*/   Calendar.ZONE_OFFSET,
1303         /*Xx*/  Calendar.ZONE_OFFSET, Calendar.ZONE_OFFSET,
1304     };
1305
1306     // Map pattern character index to DateFormat field number
1307     private static final int[] PATTERN_INDEX_TO_DATE_FORMAT_FIELD = {
1308         /*GyM*/ DateFormat.ERA_FIELD, DateFormat.YEAR_FIELD, DateFormat.MONTH_FIELD,
1309         /*dkH*/ DateFormat.DATE_FIELD, DateFormat.HOUR_OF_DAY1_FIELD, DateFormat.HOUR_OF_DAY0_FIELD,
1310         /*msS*/ DateFormat.MINUTE_FIELD, DateFormat.SECOND_FIELD, DateFormat.FRACTIONAL_SECOND_FIELD,
1311         /*EDF*/ DateFormat.DAY_OF_WEEK_FIELD, DateFormat.DAY_OF_YEAR_FIELD, DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD,
1312         /*wWa*/ DateFormat.WEEK_OF_YEAR_FIELD, DateFormat.WEEK_OF_MONTH_FIELD, DateFormat.AM_PM_FIELD,
1313         /*hKz*/ DateFormat.HOUR1_FIELD, DateFormat.HOUR0_FIELD, DateFormat.TIMEZONE_FIELD,
1314         /*Yeu*/ DateFormat.YEAR_WOY_FIELD, DateFormat.DOW_LOCAL_FIELD, DateFormat.EXTENDED_YEAR_FIELD,
1315         /*gAZ*/ DateFormat.JULIAN_DAY_FIELD, DateFormat.MILLISECONDS_IN_DAY_FIELD, DateFormat.TIMEZONE_RFC_FIELD,
1316         /*v*/   DateFormat.TIMEZONE_GENERIC_FIELD,
1317         /*c*/   DateFormat.STANDALONE_DAY_FIELD,
1318         /*L*/   DateFormat.STANDALONE_MONTH_FIELD,
1319         /*Qq*/  DateFormat.QUARTER_FIELD, DateFormat.STANDALONE_QUARTER_FIELD,
1320         /*V*/   DateFormat.TIMEZONE_SPECIAL_FIELD,
1321         /*U*/   DateFormat.YEAR_NAME_FIELD,
1322         /*O*/   DateFormat.TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD,
1323         /*Xx*/  DateFormat.TIMEZONE_ISO_FIELD, DateFormat.TIMEZONE_ISO_LOCAL_FIELD,
1324     };
1325
1326     // Map pattern character index to DateFormat.Field
1327     private static final DateFormat.Field[] PATTERN_INDEX_TO_DATE_FORMAT_ATTRIBUTE = {
1328         /*GyM*/ DateFormat.Field.ERA, DateFormat.Field.YEAR, DateFormat.Field.MONTH,
1329         /*dkH*/ DateFormat.Field.DAY_OF_MONTH, DateFormat.Field.HOUR_OF_DAY1, DateFormat.Field.HOUR_OF_DAY0,
1330         /*msS*/ DateFormat.Field.MINUTE, DateFormat.Field.SECOND, DateFormat.Field.MILLISECOND,
1331         /*EDF*/ DateFormat.Field.DAY_OF_WEEK, DateFormat.Field.DAY_OF_YEAR, DateFormat.Field.DAY_OF_WEEK_IN_MONTH,
1332         /*wWa*/ DateFormat.Field.WEEK_OF_YEAR, DateFormat.Field.WEEK_OF_MONTH, DateFormat.Field.AM_PM,
1333         /*hKz*/ DateFormat.Field.HOUR1, DateFormat.Field.HOUR0, DateFormat.Field.TIME_ZONE,
1334         /*Yeu*/ DateFormat.Field.YEAR_WOY, DateFormat.Field.DOW_LOCAL, DateFormat.Field.EXTENDED_YEAR,
1335         /*gAZ*/ DateFormat.Field.JULIAN_DAY, DateFormat.Field.MILLISECONDS_IN_DAY, DateFormat.Field.TIME_ZONE,
1336         /*v*/   DateFormat.Field.TIME_ZONE,
1337         /*c*/   DateFormat.Field.DAY_OF_WEEK,
1338         /*L*/   DateFormat.Field.MONTH,
1339         /*Qq*/  DateFormat.Field.QUARTER, DateFormat.Field.QUARTER,
1340         /*V*/   DateFormat.Field.TIME_ZONE,
1341         /*U*/   DateFormat.Field.YEAR,
1342         /*O*/   DateFormat.Field.TIME_ZONE,
1343         /*Xx*/  DateFormat.Field.TIME_ZONE, DateFormat.Field.TIME_ZONE,
1344     };
1345
1346     /**
1347      * Returns a DateFormat.Field constant associated with the specified format pattern
1348      * character.
1349      *
1350      * @param ch The pattern character
1351      * @return DateFormat.Field associated with the pattern character
1352      *
1353      * @stable ICU 3.8
1354      */
1355     protected DateFormat.Field patternCharToDateFormatField(char ch) {
1356         int patternCharIndex = -1;
1357         if ('A' <= ch && ch <= 'z') {
1358             patternCharIndex = PATTERN_CHAR_TO_INDEX[(int)ch - PATTERN_CHAR_BASE];
1359         }
1360         if (patternCharIndex != -1) {
1361             return PATTERN_INDEX_TO_DATE_FORMAT_ATTRIBUTE[patternCharIndex];
1362         }
1363         return null;
1364     }
1365
1366     /**
1367      * Formats a single field, given its pattern character.  Subclasses may
1368      * override this method in order to modify or add formatting
1369      * capabilities.
1370      * @param ch the pattern character
1371      * @param count the number of times ch is repeated in the pattern
1372      * @param beginOffset the offset of the output string at the start of
1373      * this field; used to set pos when appropriate
1374      * @param pos receives the position of a field, when appropriate
1375      * @param fmtData the symbols for this formatter
1376      * @stable ICU 2.0
1377      */
1378     protected String subFormat(char ch, int count, int beginOffset,
1379                                FieldPosition pos, DateFormatSymbols fmtData,
1380                                Calendar cal)
1381         throws IllegalArgumentException
1382     {
1383         // Note: formatData is ignored
1384         return subFormat(ch, count, beginOffset, 0, DisplayContext.CAPITALIZATION_NONE, pos, cal);
1385     }
1386
1387      /**
1388      * Formats a single field. This is the version called internally; it
1389      * adds fieldNum and capitalizationContext parameters.
1390      *
1391      * @internal
1392      * @deprecated This API is ICU internal only.
1393      */
1394     protected String subFormat(char ch, int count, int beginOffset,
1395                                int fieldNum, DisplayContext capitalizationContext,
1396                                FieldPosition pos,
1397                                Calendar cal)
1398     {
1399         StringBuffer buf = new StringBuffer();
1400         subFormat(buf, ch, count, beginOffset, fieldNum, capitalizationContext, pos, cal);
1401         return buf.toString();
1402     }
1403
1404    /**
1405      * Formats a single field; useFastFormat variant.  Reuses a
1406      * StringBuffer for results instead of creating a String on the
1407      * heap for each call.
1408      *
1409      * NOTE We don't really need the beginOffset parameter, EXCEPT for
1410      * the need to support the slow subFormat variant (above) which
1411      * has to pass it in to us.
1412      *
1413      * @internal
1414      * @deprecated This API is ICU internal only.
1415      */
1416     @SuppressWarnings("fallthrough")
1417     protected void subFormat(StringBuffer buf,
1418                              char ch, int count, int beginOffset,
1419                              int fieldNum, DisplayContext capitalizationContext,
1420                              FieldPosition pos,
1421                              Calendar cal) {
1422
1423         final int maxIntCount = Integer.MAX_VALUE;
1424         final int bufstart = buf.length();
1425         TimeZone tz = cal.getTimeZone();
1426         long date = cal.getTimeInMillis();
1427         String result = null;
1428         
1429         // final int patternCharIndex = DateFormatSymbols.patternChars.indexOf(ch);
1430         int patternCharIndex = -1;
1431         if ('A' <= ch && ch <= 'z') {
1432             patternCharIndex = PATTERN_CHAR_TO_INDEX[(int)ch - PATTERN_CHAR_BASE];
1433         }
1434
1435         if (patternCharIndex == -1) {
1436             if (ch == 'l') { // (SMALL LETTER L) deprecated placeholder for leap month marker, ignore
1437                 return;
1438             } else {
1439                 throw new IllegalArgumentException("Illegal pattern character " +
1440                                                    "'" + ch + "' in \"" +
1441                                                    pattern + '"');
1442             }
1443         }
1444
1445         final int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
1446         int value = cal.get(field);
1447
1448         NumberFormat currentNumberFormat = getNumberFormat(ch);
1449         DateFormatSymbols.CapitalizationContextUsage capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.OTHER;
1450
1451         switch (patternCharIndex) {
1452         case 0: // 'G' - ERA
1453             if ( cal.getType().equals("chinese") || cal.getType().equals("dangi") ) {
1454                 // moved from ChineseDateFormat
1455                 zeroPaddingNumber(currentNumberFormat, buf, value, 1, 9);
1456             } else {
1457                 if (count == 5) {
1458                     safeAppend(formatData.narrowEras, value, buf);
1459                     capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.ERA_NARROW;
1460                 } else if (count == 4) {
1461                     safeAppend(formatData.eraNames, value, buf);
1462                     capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.ERA_WIDE;
1463                 } else {
1464                     safeAppend(formatData.eras, value, buf);
1465                     capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.ERA_ABBREV;
1466                 }
1467             }
1468             break;
1469         case 30: // 'U' - YEAR_NAME_FIELD
1470             if (formatData.shortYearNames != null && value <= formatData.shortYearNames.length) {
1471                 safeAppend(formatData.shortYearNames, value-1, buf);
1472                 break;
1473             }
1474             // else fall through to numeric year handling, do not break here 
1475         case 1: // 'y' - YEAR
1476         case 18: // 'Y' - YEAR_WOY
1477             if ( override != null && (override.compareTo("hebr") == 0 || override.indexOf("y=hebr") >= 0) &&
1478                     value > HEBREW_CAL_CUR_MILLENIUM_START_YEAR && value < HEBREW_CAL_CUR_MILLENIUM_END_YEAR ) {
1479                 value -= HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
1480             }
1481             /* According to the specification, if the number of pattern letters ('y') is 2,
1482              * the year is truncated to 2 digits; otherwise it is interpreted as a number.
1483              * But the original code process 'y', 'yy', 'yyy' in the same way. and process
1484              * patterns with 4 or more than 4 'y' characters in the same way.
1485              * So I change the codes to meet the specification. [Richard/GCl]
1486              */
1487             if (count == 2) {
1488                 zeroPaddingNumber(currentNumberFormat,buf, value, 2, 2); // clip 1996 to 96
1489             } else { //count = 1 or count > 2
1490                 zeroPaddingNumber(currentNumberFormat,buf, value, count, maxIntCount);
1491             }
1492             break;
1493         case 2: // 'M' - MONTH
1494         case 26: // 'L' - STANDALONE MONTH
1495             if ( cal.getType().equals("hebrew")) {
1496                 boolean isLeap = HebrewCalendar.isLeapYear(cal.get(Calendar.YEAR));
1497                 if (isLeap && value == 6 && count >= 3 ) {
1498                     value = 13; // Show alternate form for Adar II in leap years in Hebrew calendar.
1499                 }
1500                 if (!isLeap && value >= 6 && count < 3 ) {
1501                     value--; // Adjust the month number down 1 in Hebrew non-leap years, i.e. Adar is 6, not 7.
1502                 }
1503             }
1504             int isLeapMonth = (formatData.leapMonthPatterns != null && formatData.leapMonthPatterns.length >= DateFormatSymbols.DT_MONTH_PATTERN_COUNT)?
1505                      cal.get(Calendar.IS_LEAP_MONTH): 0;
1506             // should consolidate the next section by using arrays of pointers & counts for the right symbols...
1507             if (count == 5) {
1508                 if (patternCharIndex == 2) {
1509                     safeAppendWithMonthPattern(formatData.narrowMonths, value, buf, (isLeapMonth!=0)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_FORMAT_NARROW]: null);
1510                 } else {
1511                     safeAppendWithMonthPattern(formatData.standaloneNarrowMonths, value, buf, (isLeapMonth!=0)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_STANDALONE_NARROW]: null);
1512                 }
1513                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.MONTH_NARROW;
1514             } else if (count == 4) {
1515                 if (patternCharIndex == 2) {
1516                     safeAppendWithMonthPattern(formatData.months, value, buf, (isLeapMonth!=0)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_FORMAT_WIDE]: null);
1517                     capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.MONTH_FORMAT;
1518                 } else {
1519                     safeAppendWithMonthPattern(formatData.standaloneMonths, value, buf, (isLeapMonth!=0)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_STANDALONE_WIDE]: null);
1520                     capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.MONTH_STANDALONE;
1521                 }
1522             } else if (count == 3) {
1523                 if (patternCharIndex == 2) {
1524                     safeAppendWithMonthPattern(formatData.shortMonths, value, buf, (isLeapMonth!=0)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV]: null);
1525                     capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.MONTH_FORMAT;
1526                 } else {
1527                     safeAppendWithMonthPattern(formatData.standaloneShortMonths, value, buf, (isLeapMonth!=0)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_STANDALONE_ABBREV]: null);
1528                     capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.MONTH_STANDALONE;
1529                 }
1530             } else {
1531                 StringBuffer monthNumber = new StringBuffer();
1532                 zeroPaddingNumber(currentNumberFormat, monthNumber, value+1, count, maxIntCount);
1533                 String[] monthNumberStrings = new String[1];
1534                 monthNumberStrings[0] = monthNumber.toString();
1535                 safeAppendWithMonthPattern(monthNumberStrings, 0, buf, (isLeapMonth!=0)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_NUMERIC]: null);
1536             }
1537             break;
1538         case 4: // 'k' - HOUR_OF_DAY (1..24)
1539             if (value == 0) {
1540                 zeroPaddingNumber(currentNumberFormat,buf,
1541                                   cal.getMaximum(Calendar.HOUR_OF_DAY)+1,
1542                                   count, maxIntCount);
1543             } else {
1544                 zeroPaddingNumber(currentNumberFormat,buf, value, count, maxIntCount);
1545             }
1546             break;
1547         case 8: // 'S' - FRACTIONAL_SECOND
1548             // Fractional seconds left-justify
1549             {
1550                 numberFormat.setMinimumIntegerDigits(Math.min(3, count));
1551                 numberFormat.setMaximumIntegerDigits(maxIntCount);
1552                 if (count == 1) {
1553                     value /= 100;
1554                 } else if (count == 2) {
1555                     value /= 10;
1556                 }
1557                 FieldPosition p = new FieldPosition(-1);
1558                 numberFormat.format((long) value, buf, p);
1559                 if (count > 3) {
1560                     numberFormat.setMinimumIntegerDigits(count - 3);
1561                     numberFormat.format(0L, buf, p);
1562                 }
1563             }
1564             break;
1565         case 19: // 'e' - DOW_LOCAL (use DOW_LOCAL for numeric, DAY_OF_WEEK for format names)
1566             if (count < 3) {
1567                 zeroPaddingNumber(currentNumberFormat,buf, value, count, maxIntCount);
1568                 break;
1569             }
1570             // For alpha day-of-week, we don't want DOW_LOCAL,
1571             // we need the standard DAY_OF_WEEK.
1572             value = cal.get(Calendar.DAY_OF_WEEK);
1573             // fall through, do not break here
1574         case 9: // 'E' - DAY_OF_WEEK
1575             if (count == 5) {
1576                 safeAppend(formatData.narrowWeekdays, value, buf);
1577                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_NARROW;
1578             } else if (count == 4) {
1579                 safeAppend(formatData.weekdays, value, buf);
1580                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_FORMAT;
1581             } else if (count == 6 && formatData.shorterWeekdays != null) {
1582                 safeAppend(formatData.shorterWeekdays, value, buf);
1583                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_FORMAT;
1584             } else {// count <= 3, use abbreviated form if exists
1585                 safeAppend(formatData.shortWeekdays, value, buf);
1586                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_FORMAT;
1587             }
1588             break;
1589         case 14: // 'a' - AM_PM
1590             safeAppend(formatData.ampms, value, buf);
1591             break;
1592         case 15: // 'h' - HOUR (1..12)
1593             if (value == 0) {
1594                 zeroPaddingNumber(currentNumberFormat,buf,
1595                                   cal.getLeastMaximum(Calendar.HOUR)+1,
1596                                   count, maxIntCount);
1597             } else {
1598                 zeroPaddingNumber(currentNumberFormat,buf, value, count, maxIntCount);
1599             }
1600             break;
1601
1602         case 17: // 'z' - TIMEZONE_FIELD
1603             if (count < 4) {
1604                 // "z", "zz", "zzz"
1605                 result = tzFormat().format(Style.SPECIFIC_SHORT, tz, date);
1606                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.METAZONE_SHORT;
1607             } else {
1608                 result = tzFormat().format(Style.SPECIFIC_LONG, tz, date);
1609                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.METAZONE_LONG;
1610             }
1611             buf.append(result);
1612             break;
1613         case 23: // 'Z' - TIMEZONE_RFC_FIELD
1614             if (count < 4) {
1615                 // RFC822 format - equivalent to ISO 8601 local offset fixed width format
1616                 result = tzFormat().format(Style.ISO_BASIC_LOCAL_FULL, tz, date);
1617             } else if (count == 5) {
1618                 // ISO 8601 extended format
1619                 result = tzFormat().format(Style.ISO_EXTENDED_FULL, tz, date);
1620             } else {
1621                 // long form, localized GMT pattern
1622                 result = tzFormat().format(Style.LOCALIZED_GMT, tz, date);
1623             }
1624                 buf.append(result);
1625             break;
1626         case 24: // 'v' - TIMEZONE_GENERIC_FIELD
1627             if (count == 1) {
1628                 // "v"
1629                 result = tzFormat().format(Style.GENERIC_SHORT, tz, date);
1630                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.METAZONE_SHORT;
1631             } else if (count == 4) {
1632                 // "vvvv"
1633                 result = tzFormat().format(Style.GENERIC_LONG, tz, date);
1634                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.METAZONE_LONG;
1635             }
1636             buf.append(result);
1637             break;
1638         case 29: // 'V' - TIMEZONE_SPECIAL_FIELD
1639             if (count == 1) {
1640                 // "V"
1641                 result = tzFormat().format(Style.ZONE_ID_SHORT, tz, date);
1642             } else if (count == 2) {
1643                 // "VV"
1644                 result = tzFormat().format(Style.ZONE_ID, tz, date);
1645             } else if (count == 3) {
1646                 // "VVV"
1647                 result = tzFormat().format(Style.EXEMPLAR_LOCATION, tz, date);
1648             } else if (count == 4) {
1649                 // "VVVV"
1650                 result = tzFormat().format(Style.GENERIC_LOCATION, tz, date);
1651                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.ZONE_LONG;
1652             }
1653             buf.append(result);
1654             break;
1655         case 31: // 'O' - TIMEZONE_LOCALIZED_GMT_OFFSET_FIELD
1656             if (count == 1) {
1657                 // "O" - Short Localized GMT format
1658                 result = tzFormat().format(Style.LOCALIZED_GMT_SHORT, tz, date);
1659             } else if (count == 4) {
1660                 // "OOOO" - Localized GMT format
1661                 result = tzFormat().format(Style.LOCALIZED_GMT, tz, date);
1662             }
1663             buf.append(result);
1664             break;
1665         case 32: // 'X' - TIMEZONE_ISO_FIELD
1666             if (count == 1) {
1667                 // "X" - ISO Basic/Short
1668                 result = tzFormat().format(Style.ISO_BASIC_SHORT, tz, date);
1669             } else if (count == 2) {
1670                 // "XX" - ISO Basic/Fixed
1671                 result = tzFormat().format(Style.ISO_BASIC_FIXED, tz, date);
1672             } else if (count == 3) {
1673                 // "XXX" - ISO Extended/Fixed
1674                 result = tzFormat().format(Style.ISO_EXTENDED_FIXED, tz, date);
1675             } else if (count == 4) {
1676                 // "XXXX" - ISO Basic/Optional second field
1677                 result = tzFormat().format(Style.ISO_BASIC_FULL, tz, date);
1678             } else if (count == 5) {
1679                 // "XXXXX" - ISO Extended/Optional second field
1680                 result = tzFormat().format(Style.ISO_EXTENDED_FULL, tz, date);
1681             }
1682             buf.append(result);
1683             break;
1684         case 33: // 'x' - TIMEZONE_ISO_LOCAL_FIELD
1685             if (count == 1) {
1686                 // "x" - ISO Local Basic/Short
1687                 result = tzFormat().format(Style.ISO_BASIC_LOCAL_SHORT, tz, date);
1688             } else if (count == 2) {
1689                 // "x" - ISO Local Basic/Fixed
1690                 result = tzFormat().format(Style.ISO_BASIC_LOCAL_FIXED, tz, date);
1691             } else if (count == 3) {
1692                 // "xxx" - ISO Local Extended/Fixed
1693                 result = tzFormat().format(Style.ISO_EXTENDED_LOCAL_FIXED, tz, date);
1694             } else if (count == 4) {
1695                 // "xxxx" - ISO Local Basic/Optional second field
1696                 result = tzFormat().format(Style.ISO_BASIC_LOCAL_FULL, tz, date);
1697             } else if (count == 5) {
1698                 // "xxxxx" - ISO Local Extended/Optional second field
1699                 result = tzFormat().format(Style.ISO_EXTENDED_LOCAL_FULL, tz, date);
1700             }
1701             buf.append(result);
1702             break;
1703
1704         case 25: // 'c' - STANDALONE DAY (use DOW_LOCAL for numeric, DAY_OF_WEEK for standalone)
1705             if (count < 3) {
1706                 zeroPaddingNumber(currentNumberFormat,buf, value, 1, maxIntCount);
1707                 break;
1708             }
1709             // For alpha day-of-week, we don't want DOW_LOCAL,
1710             // we need the standard DAY_OF_WEEK.
1711             value = cal.get(Calendar.DAY_OF_WEEK);
1712             if (count == 5) {
1713                 safeAppend(formatData.standaloneNarrowWeekdays, value, buf);
1714                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_NARROW;
1715             } else if (count == 4) {
1716                 safeAppend(formatData.standaloneWeekdays, value, buf);
1717                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_STANDALONE;
1718             } else if (count == 6 && formatData.standaloneShorterWeekdays != null) {
1719                 safeAppend(formatData.standaloneShorterWeekdays, value, buf);
1720                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_STANDALONE;
1721             } else { // count == 3
1722                 safeAppend(formatData.standaloneShortWeekdays, value, buf);
1723                 capContextUsageType = DateFormatSymbols.CapitalizationContextUsage.DAY_STANDALONE;
1724             }
1725             break;
1726         case 27: // 'Q' - QUARTER
1727             if (count >= 4) {
1728                 safeAppend(formatData.quarters, value/3, buf);
1729             } else if (count == 3) {
1730                 safeAppend(formatData.shortQuarters, value/3, buf);
1731             } else {
1732                 zeroPaddingNumber(currentNumberFormat,buf, (value/3)+1, count, maxIntCount);
1733             }
1734             break;
1735         case 28: // 'q' - STANDALONE QUARTER
1736             if (count >= 4) {
1737                 safeAppend(formatData.standaloneQuarters, value/3, buf);
1738             } else if (count == 3) {
1739                 safeAppend(formatData.standaloneShortQuarters, value/3, buf);
1740             } else {
1741                 zeroPaddingNumber(currentNumberFormat,buf, (value/3)+1, count, maxIntCount);
1742             }
1743             break;
1744         default:
1745             // case 3: // 'd' - DATE
1746             // case 5: // 'H' - HOUR_OF_DAY (0..23)
1747             // case 6: // 'm' - MINUTE
1748             // case 7: // 's' - SECOND
1749             // case 10: // 'D' - DAY_OF_YEAR
1750             // case 11: // 'F' - DAY_OF_WEEK_IN_MONTH
1751             // case 12: // 'w' - WEEK_OF_YEAR
1752             // case 13: // 'W' - WEEK_OF_MONTH
1753             // case 16: // 'K' - HOUR (0..11)
1754             // case 20: // 'u' - EXTENDED_YEAR
1755             // case 21: // 'g' - JULIAN_DAY
1756             // case 22: // 'A' - MILLISECONDS_IN_DAY
1757
1758             zeroPaddingNumber(currentNumberFormat,buf, value, count, maxIntCount);
1759             break;
1760         } // switch (patternCharIndex)
1761
1762         if (fieldNum == 0) {
1763             boolean titlecase = false;
1764             if (capitalizationContext != null) {
1765                 switch (capitalizationContext) {
1766                     case CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE:
1767                         titlecase = true;
1768                         break;
1769                     case CAPITALIZATION_FOR_UI_LIST_OR_MENU:
1770                     case CAPITALIZATION_FOR_STANDALONE:
1771                         if (formatData.capitalization != null) {
1772                              boolean[] transforms = formatData.capitalization.get(capContextUsageType);
1773                             titlecase = (capitalizationContext==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU)?
1774                                         transforms[0]: transforms[1];
1775                         }
1776                         break;
1777                     default:
1778                        break;
1779                 }
1780             }
1781             if (titlecase) {
1782                 String firstField = buf.substring(bufstart); // bufstart or beginOffset, should be the same
1783                 String firstFieldTitleCase = UCharacter.toTitleCase(locale, firstField, null,
1784                                                      UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
1785                 buf.replace(bufstart, buf.length(), firstFieldTitleCase);
1786             }
1787         }
1788
1789         // Set the FieldPosition (for the first occurrence only)
1790         if (pos.getBeginIndex() == pos.getEndIndex()) {
1791             if (pos.getField() == PATTERN_INDEX_TO_DATE_FORMAT_FIELD[patternCharIndex]) {
1792                 pos.setBeginIndex(beginOffset);
1793                 pos.setEndIndex(beginOffset + buf.length() - bufstart);
1794             } else if (pos.getFieldAttribute() ==
1795                        PATTERN_INDEX_TO_DATE_FORMAT_ATTRIBUTE[patternCharIndex]) {
1796                 pos.setBeginIndex(beginOffset);
1797                 pos.setEndIndex(beginOffset + buf.length() - bufstart);
1798             }
1799         }
1800     }
1801
1802     private static void safeAppend(String[] array, int value, StringBuffer appendTo) {
1803         if (array != null && value >= 0 && value < array.length) {
1804             appendTo.append(array[value]);
1805         }
1806     }
1807
1808     private static void safeAppendWithMonthPattern(String[] array, int value, StringBuffer appendTo, String monthPattern) {
1809         if (array != null && value >= 0 && value < array.length) {
1810             if (monthPattern == null) {
1811                 appendTo.append(array[value]);
1812             } else {
1813                 appendTo.append(MessageFormat.format(monthPattern, array[value]));
1814             }
1815         }
1816     }
1817
1818     /*
1819      * PatternItem store parsed date/time field pattern information.
1820      */
1821     private static class PatternItem {
1822         final char type;
1823         final int length;
1824         final boolean isNumeric;
1825
1826         PatternItem(char type, int length) {
1827             this.type = type;
1828             this.length = length;
1829             isNumeric = isNumeric(type, length);
1830         }
1831     }
1832
1833     private static ICUCache<String, Object[]> PARSED_PATTERN_CACHE =
1834         new SimpleCache<String, Object[]>();
1835     private transient Object[] patternItems;
1836
1837     /*
1838      * Returns parsed pattern items.  Each item is either String or
1839      * PatternItem.
1840      */
1841     private Object[] getPatternItems() {
1842         if (patternItems != null) {
1843             return patternItems;
1844         }
1845
1846         patternItems = PARSED_PATTERN_CACHE.get(pattern);
1847         if (patternItems != null) {
1848             return patternItems;
1849         }
1850
1851         boolean isPrevQuote = false;
1852         boolean inQuote = false;
1853         StringBuilder text = new StringBuilder();
1854         char itemType = 0;  // 0 for string literal, otherwise date/time pattern character
1855         int itemLength = 1;
1856
1857         List<Object> items = new ArrayList<Object>();
1858
1859         for (int i = 0; i < pattern.length(); i++) {
1860             char ch = pattern.charAt(i);
1861             if (ch == '\'') {
1862                 if (isPrevQuote) {
1863                     text.append('\'');
1864                     isPrevQuote = false;
1865                 } else {
1866                     isPrevQuote = true;
1867                     if (itemType != 0) {
1868                         items.add(new PatternItem(itemType, itemLength));
1869                         itemType = 0;
1870                     }
1871                 }
1872                 inQuote = !inQuote;
1873             } else {
1874                 isPrevQuote = false;
1875                 if (inQuote) {
1876                     text.append(ch);
1877                 } else {
1878                     if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
1879                         // a date/time pattern character
1880                         if (ch == itemType) {
1881                             itemLength++;
1882                         } else {
1883                             if (itemType == 0) {
1884                                 if (text.length() > 0) {
1885                                     items.add(text.toString());
1886                                     text.setLength(0);
1887                                 }
1888                             } else {
1889                                 items.add(new PatternItem(itemType, itemLength));
1890                             }
1891                             itemType = ch;
1892                             itemLength = 1;
1893                         }
1894                     } else {
1895                         // a string literal
1896                         if (itemType != 0) {
1897                             items.add(new PatternItem(itemType, itemLength));
1898                             itemType = 0;
1899                         }
1900                         text.append(ch);
1901                     }
1902                 }
1903             }
1904         }
1905         // handle last item
1906         if (itemType == 0) {
1907             if (text.length() > 0) {
1908                 items.add(text.toString());
1909                 text.setLength(0);
1910             }
1911         } else {
1912             items.add(new PatternItem(itemType, itemLength));
1913         }
1914
1915         patternItems = items.toArray(new Object[items.size()]);
1916
1917         PARSED_PATTERN_CACHE.put(pattern, patternItems);
1918
1919         return patternItems;
1920     }
1921
1922     /**
1923      * Internal high-speed method.  Reuses a StringBuffer for results
1924      * instead of creating a String on the heap for each call.
1925      * @internal
1926      * @deprecated This API is ICU internal only.
1927      */
1928     protected void zeroPaddingNumber(NumberFormat nf,StringBuffer buf, int value,
1929                                      int minDigits, int maxDigits) {
1930         // Note: Indian calendar uses negative value for a calendar
1931         // field. fastZeroPaddingNumber cannot handle negative numbers.
1932         // BTW, it looks like a design bug in the Indian calendar...
1933         if (useLocalZeroPaddingNumberFormat && value >= 0) {
1934             fastZeroPaddingNumber(buf, value, minDigits, maxDigits);
1935         } else {
1936             nf.setMinimumIntegerDigits(minDigits);
1937             nf.setMaximumIntegerDigits(maxDigits);
1938             nf.format(value, buf, new FieldPosition(-1));
1939         }
1940     }
1941
1942     /**
1943      * Overrides superclass method
1944      * @stable ICU 2.0
1945      */
1946     public void setNumberFormat(NumberFormat newNumberFormat) {
1947         // Override this method to update local zero padding number formatter
1948         super.setNumberFormat(newNumberFormat);
1949         initLocalZeroPaddingNumberFormat();
1950         initializeTimeZoneFormat(true);
1951     }
1952
1953     private void initLocalZeroPaddingNumberFormat() {
1954         if (numberFormat instanceof DecimalFormat) {
1955             decDigits = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getDigits();
1956             useLocalZeroPaddingNumberFormat = true;
1957         } else if (numberFormat instanceof DateNumberFormat) {
1958             decDigits = ((DateNumberFormat)numberFormat).getDigits();
1959             useLocalZeroPaddingNumberFormat = true;
1960         } else {
1961             useLocalZeroPaddingNumberFormat = false;
1962         }
1963
1964         if (useLocalZeroPaddingNumberFormat) {
1965             decimalBuf = new char[10];  // sufficient for int numbers
1966         }
1967     }
1968
1969     // If true, use local version of zero padding number format
1970     private transient boolean useLocalZeroPaddingNumberFormat;
1971     private transient char[] decDigits;
1972     private transient char[] decimalBuf;
1973
1974     /*
1975      * Lightweight zero padding integer number format function.
1976      *
1977      * Note: This implementation is almost equivalent to format method in DateNumberFormat.
1978      * In the method zeroPaddingNumber above should be able to use the one in DateNumberFormat,
1979      * but, it does not help IBM J9's JIT to optimize the performance much.  In simple repeative
1980      * date format test case, having local implementation is ~10% faster than using one in
1981      * DateNumberFormat on IBM J9 VM.  On Sun Hotspot VM, I do not see such difference.
1982      *
1983      * -Yoshito
1984      */
1985     private void fastZeroPaddingNumber(StringBuffer buf, int value, int minDigits, int maxDigits) {
1986         int limit = decimalBuf.length < maxDigits ? decimalBuf.length : maxDigits;
1987         int index = limit - 1;
1988         while (true) {
1989             decimalBuf[index] = decDigits[(value % 10)];
1990             value /= 10;
1991             if (index == 0 || value == 0) {
1992                 break;
1993             }
1994             index--;
1995         }
1996         int padding = minDigits - (limit - index);
1997         while (padding > 0 && index > 0) {
1998             decimalBuf[--index] = decDigits[0];
1999             padding--;
2000         }
2001         while (padding > 0) {
2002             // when pattern width is longer than decimalBuf, need extra
2003             // leading zeros - ticke#7595
2004             buf.append(decDigits[0]);
2005             padding--;
2006         }
2007         buf.append(decimalBuf, index, limit - index);
2008     }
2009
2010     /**
2011      * Formats a number with the specified minimum and maximum number of digits.
2012      * @stable ICU 2.0
2013      */
2014     protected String zeroPaddingNumber(long value, int minDigits, int maxDigits)
2015     {
2016         numberFormat.setMinimumIntegerDigits(minDigits);
2017         numberFormat.setMaximumIntegerDigits(maxDigits);
2018         return numberFormat.format(value);
2019     }
2020
2021     /**
2022      * Format characters that indicate numeric fields.  The character
2023      * at index 0 is treated specially.
2024      */
2025     private static final String NUMERIC_FORMAT_CHARS = "MYyudehHmsSDFwWkK";
2026
2027     /**
2028      * Return true if the given format character, occuring count
2029      * times, represents a numeric field.
2030      */
2031     private static final boolean isNumeric(char formatChar, int count) {
2032         int i = NUMERIC_FORMAT_CHARS.indexOf(formatChar);
2033         return (i > 0 || (i == 0 && count < 3));
2034     }
2035
2036     /**
2037      * Overrides DateFormat
2038      * @see DateFormat
2039      * @stable ICU 2.0
2040      */
2041     public void parse(String text, Calendar cal, ParsePosition parsePos)
2042     {
2043         TimeZone backupTZ = null;
2044         Calendar resultCal = null;
2045         if (cal != calendar && !cal.getType().equals(calendar.getType())) {
2046             // Different calendar type
2047             // We use the time/zone from the input calendar, but
2048             // do not use the input calendar for field calculation.
2049             calendar.setTimeInMillis(cal.getTimeInMillis());
2050             backupTZ = calendar.getTimeZone();
2051             calendar.setTimeZone(cal.getTimeZone());
2052             resultCal = cal;
2053             cal = calendar;
2054         }
2055
2056         int pos = parsePos.getIndex();
2057         int start = pos;
2058
2059         // Reset tztype
2060         tztype = TimeType.UNKNOWN;
2061         boolean[] ambiguousYear = { false };
2062
2063         // item index for the first numeric field within a contiguous numeric run
2064         int numericFieldStart = -1;
2065         // item length for the first numeric field within a contiguous numeric run
2066         int numericFieldLength = 0;
2067         // start index of numeric text run in the input text
2068         int numericStartPos = 0;
2069         
2070         MessageFormat numericLeapMonthFormatter = null;
2071         if (formatData.leapMonthPatterns != null && formatData.leapMonthPatterns.length >= DateFormatSymbols.DT_MONTH_PATTERN_COUNT) {
2072             numericLeapMonthFormatter = new MessageFormat(formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_NUMERIC], locale);
2073         }
2074
2075         Object[] items = getPatternItems();
2076         int i = 0;
2077         while (i < items.length) {
2078             if (items[i] instanceof PatternItem) {
2079                 // Handle pattern field
2080                 PatternItem field = (PatternItem)items[i];
2081                 if (field.isNumeric) {
2082                     // Handle fields within a run of abutting numeric fields.  Take
2083                     // the pattern "HHmmss" as an example. We will try to parse
2084                     // 2/2/2 characters of the input text, then if that fails,
2085                     // 1/2/2.  We only adjust the width of the leftmost field; the
2086                     // others remain fixed.  This allows "123456" => 12:34:56, but
2087                     // "12345" => 1:23:45.  Likewise, for the pattern "yyyyMMdd" we
2088                     // try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
2089                     if (numericFieldStart == -1) {
2090                         // check if this field is followed by abutting another numeric field
2091                         if ((i + 1) < items.length
2092                                 && (items[i + 1] instanceof PatternItem)
2093                                 && ((PatternItem)items[i + 1]).isNumeric) {
2094                             // record the first numeric field within a numeric text run
2095                             numericFieldStart = i;
2096                             numericFieldLength = field.length;
2097                             numericStartPos = pos;
2098                         }
2099                     }
2100                 }
2101                 if (numericFieldStart != -1) {
2102                     // Handle a numeric field within abutting numeric fields
2103                     int len = field.length;
2104                     if (numericFieldStart == i) {
2105                         len = numericFieldLength;
2106                     }
2107
2108                     // Parse a numeric field
2109                     pos = subParse(text, pos, field.type, len,
2110                             true, false, ambiguousYear, cal, numericLeapMonthFormatter);
2111
2112                     if (pos < 0) {
2113                         // If the parse fails anywhere in the numeric run, back up to the
2114                         // start of the run and use shorter pattern length for the first
2115                         // numeric field.
2116                         --numericFieldLength;
2117                         if (numericFieldLength == 0) {
2118                             // can not make shorter any more
2119                             parsePos.setIndex(start);
2120                             parsePos.setErrorIndex(pos);
2121                             if (backupTZ != null) {
2122                                 calendar.setTimeZone(backupTZ);
2123                             }
2124                             return;
2125                         }
2126                         i = numericFieldStart;
2127                         pos = numericStartPos;
2128                         continue;
2129                     }
2130
2131                 } else if (field.type != 'l') { // (SMALL LETTER L) obsolete pattern char just gets ignored
2132                     // Handle a non-numeric field or a non-abutting numeric field
2133                     numericFieldStart = -1;
2134
2135                     int s = pos;
2136                     pos = subParse(text, pos, field.type, field.length,
2137                             false, true, ambiguousYear, cal, numericLeapMonthFormatter);
2138                     
2139                     if (pos < 0) {
2140                         if (pos == ISOSpecialEra) {
2141                             // era not present, in special cases allow this to continue
2142                             pos = s;
2143
2144                             if (i+1 < items.length) { 
2145                                 
2146                                 String patl = null;
2147                                 // if it will cause a class cast exception to String, we can't use it                                
2148                                 try {
2149                                     patl = (String)items[i+1];
2150                                 } catch(ClassCastException cce) {
2151                                     parsePos.setIndex(start);
2152                                     parsePos.setErrorIndex(s);
2153                                     if (backupTZ != null) {
2154                                         calendar.setTimeZone(backupTZ);
2155                                     }
2156                                     return;
2157                                 }                                
2158                                 
2159                                 // get next item in pattern
2160                                 if(patl == null)
2161                                     patl = (String)items[i+1];
2162                                 int plen = patl.length();
2163                                 int idx=0;
2164                                 
2165                                 // White space characters found in patten.
2166                                 // Skip contiguous white spaces.
2167                                 while (idx < plen) {
2168
2169                                     char pch = patl.charAt(idx);
2170                                     if (PatternProps.isWhiteSpace(pch))
2171                                         idx++;
2172                                     else
2173                                         break;
2174                                 }
2175                                 
2176                                 // if next item in pattern is all whitespace, skip it
2177                                 if (idx == plen) {
2178                                     i++;
2179                                 }
2180
2181                             }
2182                         } else {
2183                             parsePos.setIndex(start);
2184                             parsePos.setErrorIndex(s);
2185                             if (backupTZ != null) {
2186                                 calendar.setTimeZone(backupTZ);
2187                             }
2188                             return;
2189                         }                              
2190                     }
2191                     
2192                 }
2193             } else {
2194                 // Handle literal pattern text literal
2195                 numericFieldStart = -1;
2196                 boolean[] complete = new boolean[1];
2197                 pos = matchLiteral(text, pos, items, i, complete);
2198                 if (!complete[0]) {
2199                     // Set the position of mismatch
2200                     parsePos.setIndex(start);
2201                     parsePos.setErrorIndex(pos);
2202                     if (backupTZ != null) {
2203                         calendar.setTimeZone(backupTZ);
2204                     }
2205                     return;
2206                 }
2207             }
2208             ++i;
2209         }
2210         
2211         // Special hack for trailing "." after non-numeric field.
2212         if (pos < text.length()) {
2213             char extra = text.charAt(pos);
2214             if (extra == '.' && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_WHITESPACE) && items.length != 0) {
2215                 // only do if the last field is not numeric
2216                 Object lastItem = items[items.length - 1];
2217                 if (lastItem instanceof PatternItem && !((PatternItem)lastItem).isNumeric) {
2218                     pos++; // skip the extra "."
2219                 }
2220             }
2221         }
2222
2223         // At this point the fields of Calendar have been set.  Calendar
2224         // will fill in default values for missing fields when the time
2225         // is computed.
2226
2227         parsePos.setIndex(pos);
2228
2229         // This part is a problem:  When we call parsedDate.after, we compute the time.
2230         // Take the date April 3 2004 at 2:30 am.  When this is first set up, the year
2231         // will be wrong if we're parsing a 2-digit year pattern.  It will be 1904.
2232         // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day.  2:30 am
2233         // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am
2234         // on that day.  It is therefore parsed out to fields as 3:30 am.  Then we
2235         // add 100 years, and get April 3 2004 at 3:30 am.  Note that April 3 2004 is
2236         // a Saturday, so it can have a 2:30 am -- and it should. [LIU]
2237         /*
2238           Date parsedDate = cal.getTime();
2239           if( ambiguousYear[0] && !parsedDate.after(getDefaultCenturyStart()) ) {
2240           cal.add(Calendar.YEAR, 100);
2241           parsedDate = cal.getTime();
2242           }
2243         */
2244         // Because of the above condition, save off the fields in case we need to readjust.
2245         // The procedure we use here is not particularly efficient, but there is no other
2246         // way to do this given the API restrictions present in Calendar.  We minimize
2247         // inefficiency by only performing this computation when it might apply, that is,
2248         // when the two-digit year is equal to the start year, and thus might fall at the
2249         // front or the back of the default century.  This only works because we adjust
2250         // the year correctly to start with in other cases -- see subParse().
2251         try {
2252             if (ambiguousYear[0] || tztype != TimeType.UNKNOWN) {
2253                 // We need a copy of the fields, and we need to avoid triggering a call to
2254                 // complete(), which will recalculate the fields.  Since we can't access
2255                 // the fields[] array in Calendar, we clone the entire object.  This will
2256                 // stop working if Calendar.clone() is ever rewritten to call complete().
2257                 Calendar copy;
2258                 if (ambiguousYear[0]) { // the two-digit year == the default start year
2259                     copy = (Calendar)cal.clone();
2260                     Date parsedDate = copy.getTime();
2261                     if (parsedDate.before(getDefaultCenturyStart())) {
2262                         // We can't use add here because that does a complete() first.
2263                         cal.set(Calendar.YEAR, getDefaultCenturyStartYear() + 100);
2264                     }
2265                 }
2266                 if (tztype != TimeType.UNKNOWN) {
2267                     copy = (Calendar)cal.clone();
2268                     TimeZone tz = copy.getTimeZone();
2269                     BasicTimeZone btz = null;
2270                     if (tz instanceof BasicTimeZone) {
2271                         btz = (BasicTimeZone)tz;
2272                     }
2273
2274                     // Get local millis
2275                     copy.set(Calendar.ZONE_OFFSET, 0);
2276                     copy.set(Calendar.DST_OFFSET, 0);
2277                     long localMillis = copy.getTimeInMillis();
2278
2279                     // Make sure parsed time zone type (Standard or Daylight)
2280                     // matches the rule used by the parsed time zone.
2281                     int[] offsets = new int[2];
2282                     if (btz != null) {
2283                         if (tztype == TimeType.STANDARD) {
2284                             btz.getOffsetFromLocal(localMillis,
2285                                     BasicTimeZone.LOCAL_STD, BasicTimeZone.LOCAL_STD, offsets);
2286                         } else {
2287                             btz.getOffsetFromLocal(localMillis,
2288                                     BasicTimeZone.LOCAL_DST, BasicTimeZone.LOCAL_DST, offsets);
2289                         }
2290                     } else {
2291                         // No good way to resolve ambiguous time at transition,
2292                         // but following code work in most case.
2293                         tz.getOffset(localMillis, true, offsets);
2294
2295                         if (tztype == TimeType.STANDARD && offsets[1] != 0
2296                             || tztype == TimeType.DAYLIGHT && offsets[1] == 0) {
2297                             // Roll back one day and try it again.
2298                             // Note: This code assumes 1. timezone transition only happens
2299                             // once within 24 hours at max
2300                             // 2. the difference of local offsets at the transition is
2301                             // less than 24 hours.
2302                             tz.getOffset(localMillis - (24*60*60*1000), true, offsets);
2303                         }
2304                     }
2305
2306                     // Now, compare the results with parsed type, either standard or
2307                     // daylight saving time
2308                     int resolvedSavings = offsets[1];
2309                     if (tztype == TimeType.STANDARD) {
2310                         if (offsets[1] != 0) {
2311                             // Override DST_OFFSET = 0 in the result calendar
2312                             resolvedSavings = 0;
2313                         }
2314                     } else { // tztype == TZTYPE_DST
2315                         if (offsets[1] == 0) {
2316                             if (btz != null) {
2317                                 long time = localMillis + offsets[0];
2318                                 // We use the nearest daylight saving time rule.
2319                                 TimeZoneTransition beforeTrs, afterTrs;
2320                                 long beforeT = time, afterT = time;
2321                                 int beforeSav = 0, afterSav = 0;
2322
2323                                 // Search for DST rule before or on the time
2324                                 while (true) {
2325                                     beforeTrs = btz.getPreviousTransition(beforeT, true);
2326                                     if (beforeTrs == null) {
2327                                         break;
2328                                     }
2329                                     beforeT = beforeTrs.getTime() - 1;
2330                                     beforeSav = beforeTrs.getFrom().getDSTSavings();
2331                                     if (beforeSav != 0) {
2332                                         break;
2333                                     }
2334                                 }
2335
2336                                 // Search for DST rule after the time
2337                                 while (true) {
2338                                     afterTrs = btz.getNextTransition(afterT, false);
2339                                     if (afterTrs == null) {
2340                                         break;
2341                                     }
2342                                     afterT = afterTrs.getTime();
2343                                     afterSav = afterTrs.getTo().getDSTSavings();
2344                                     if (afterSav != 0) {
2345                                         break;
2346                                     }
2347                                 }
2348
2349                                 if (beforeTrs != null && afterTrs != null) {
2350                                     if (time - beforeT > afterT - time) {
2351                                         resolvedSavings = afterSav;
2352                                     } else {
2353                                         resolvedSavings = beforeSav;
2354                                     }
2355                                 } else if (beforeTrs != null && beforeSav != 0) {
2356                                     resolvedSavings = beforeSav;
2357                                 } else if (afterTrs != null && afterSav != 0) {
2358                                     resolvedSavings = afterSav;
2359                                 } else {
2360                                     resolvedSavings = btz.getDSTSavings();
2361                                 }
2362                             } else {
2363                                 resolvedSavings = tz.getDSTSavings();
2364                             }
2365                             if (resolvedSavings == 0) {
2366                                 // Final fallback
2367                                 resolvedSavings = millisPerHour;
2368                             }
2369                         }
2370                     }
2371                     cal.set(Calendar.ZONE_OFFSET, offsets[0]);
2372                     cal.set(Calendar.DST_OFFSET, resolvedSavings);
2373                 }
2374             }
2375         }
2376         // An IllegalArgumentException will be thrown by Calendar.getTime()
2377         // if any fields are out of range, e.g., MONTH == 17.
2378         catch (IllegalArgumentException e) {
2379             parsePos.setErrorIndex(pos);
2380             parsePos.setIndex(start);
2381             if (backupTZ != null) {
2382                 calendar.setTimeZone(backupTZ);
2383             }
2384             return;
2385         }
2386         // Set the parsed result if local calendar is used
2387         // instead of the input calendar
2388         if (resultCal != null) {
2389             resultCal.setTimeZone(cal.getTimeZone());
2390             resultCal.setTimeInMillis(cal.getTimeInMillis());
2391         }
2392         // Restore the original time zone if required
2393         if (backupTZ != null) {
2394             calendar.setTimeZone(backupTZ);
2395         }
2396     }
2397
2398     /**
2399      * Matches text (starting at pos) with patl. Returns the new pos, and sets complete[0]
2400      * if it matched the entire text. Whitespace sequences are treated as singletons.
2401      * <p>If isLenient and if we fail to match the first time, some special hacks are put into place.
2402      * <ul><li>we are between date and time fields, then one or more whitespace characters
2403      * in the text are accepted instead.</li>
2404      * <ul><li>we are after a non-numeric field, and the text starts with a ".", we skip it.</li>
2405      * </ul>
2406      */
2407     private int matchLiteral(String text, int pos, Object[] items, int itemIndex, boolean[] complete) {
2408         int originalPos = pos;
2409         String patternLiteral = (String)items[itemIndex];
2410         int plen = patternLiteral.length();
2411         int tlen = text.length();
2412         int idx = 0;
2413         while (idx < plen && pos < tlen) {
2414             char pch = patternLiteral.charAt(idx);
2415             char ich = text.charAt(pos);
2416             if (PatternProps.isWhiteSpace(pch)
2417                 && PatternProps.isWhiteSpace(ich)) {
2418                 // White space characters found in both patten and input.
2419                 // Skip contiguous white spaces.
2420                 while ((idx + 1) < plen &&
2421                         PatternProps.isWhiteSpace(patternLiteral.charAt(idx + 1))) {
2422                      ++idx;
2423                 }
2424                 while ((pos + 1) < tlen &&
2425                         PatternProps.isWhiteSpace(text.charAt(pos + 1))) {
2426                      ++pos;
2427                 }
2428             } else if (pch != ich) {
2429                 if (ich == '.' && pos == originalPos && 0 < itemIndex && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_WHITESPACE)) {
2430                     Object before = items[itemIndex-1];
2431                     if (before instanceof PatternItem) {
2432                         boolean isNumeric = ((PatternItem) before).isNumeric;
2433                         if (!isNumeric) {
2434                             ++pos; // just update pos
2435                             continue;
2436                         }
2437                     }
2438                 } else if ((pch == ' ' || pch == '.') && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_WHITESPACE)) {
2439                     ++idx;
2440                     continue;
2441                 }
2442                 break;
2443             }
2444             ++idx;
2445             ++pos;
2446         }
2447         complete[0] = idx == plen;
2448         if (complete[0] == false && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_WHITESPACE) && 0 < itemIndex && itemIndex < items.length - 1) {
2449             // If fully lenient, accept " "* for any text between a date and a time field
2450             // We don't go more lenient, because we don't want to accept "12/31" for "12:31".
2451             // People may be trying to parse for a date, then for a time.
2452             if (originalPos < tlen) {
2453                 Object before = items[itemIndex-1];
2454                 Object after = items[itemIndex+1];
2455                 if (before instanceof PatternItem && after instanceof PatternItem) {
2456                     char beforeType = ((PatternItem) before).type;
2457                     char afterType = ((PatternItem) after).type;
2458                     if (DATE_PATTERN_TYPE.contains(beforeType) != DATE_PATTERN_TYPE.contains(afterType)) {
2459                         int newPos = originalPos;
2460                         while (true) {
2461                             char ich = text.charAt(newPos);
2462                             if (!PatternProps.isWhiteSpace(ich)) {
2463                                 break;
2464                             }
2465                             ++newPos;
2466                         }
2467                         complete[0] = newPos > originalPos;
2468                         pos = newPos;
2469                     }
2470                 }
2471             }
2472         }
2473         return pos;
2474     }
2475     
2476     static final UnicodeSet DATE_PATTERN_TYPE = new UnicodeSet("[GyYuUQqMLlwWd]").freeze();
2477
2478     /**
2479      * Attempt to match the text at a given position against an array of
2480      * strings.  Since multiple strings in the array may match (for
2481      * example, if the array contains "a", "ab", and "abc", all will match
2482      * the input string "abcd") the longest match is returned.  As a side
2483      * effect, the given field of <code>cal</code> is set to the index
2484      * of the best match, if there is one.
2485      * @param text the time text being parsed.
2486      * @param start where to start parsing.
2487      * @param field the date field being parsed.
2488      * @param data the string array to parsed.
2489      * @param cal
2490      * @return the new start position if matching succeeded; a negative
2491      * number indicating matching failure, otherwise.  As a side effect,
2492      * sets the <code>cal</code> field <code>field</code> to the index
2493      * of the best match, if matching succeeded.
2494      * @stable ICU 2.0
2495      */
2496     protected int matchString(String text, int start, int field, String[] data, Calendar cal)
2497     {
2498         return matchString(text, start, field, data, null, cal);
2499     }
2500
2501     /**
2502      * Attempt to match the text at a given position against an array of
2503      * strings.  Since multiple strings in the array may match (for
2504      * example, if the array contains "a", "ab", and "abc", all will match
2505      * the input string "abcd") the longest match is returned.  As a side
2506      * effect, the given field of <code>cal</code> is set to the index
2507      * of the best match, if there is one.
2508      * @param text the time text being parsed.
2509      * @param start where to start parsing.
2510      * @param field the date field being parsed.
2511      * @param data the string array to parsed.
2512      * @param monthPattern leap month pattern, or null if none.
2513      * @param cal
2514      * @return the new start position if matching succeeded; a negative
2515      * number indicating matching failure, otherwise.  As a side effect,
2516      * sets the <code>cal</code> field <code>field</code> to the index
2517      * of the best match, if matching succeeded.
2518      * @internal
2519      * @deprecated This API is ICU internal only.
2520      */
2521     private int matchString(String text, int start, int field, String[] data, String monthPattern, Calendar cal)
2522     {
2523         int i = 0;
2524         int count = data.length;
2525
2526         if (field == Calendar.DAY_OF_WEEK) i = 1;
2527
2528         // There may be multiple strings in the data[] array which begin with
2529         // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2530         // We keep track of the longest match, and return that.  Note that this
2531         // unfortunately requires us to test all array elements.
2532         int bestMatchLength = 0, bestMatch = -1;
2533         int isLeapMonth = 0;
2534         int matchLength = 0;
2535
2536         for (; i<count; ++i)
2537             {
2538                 int length = data[i].length();
2539                 // Always compare if we have no match yet; otherwise only compare
2540                 // against potentially better matches (longer strings).
2541                 if (length > bestMatchLength &&
2542                     (matchLength = regionMatchesWithOptionalDot(text, start, data[i], length)) >= 0)
2543                     {
2544                         bestMatch = i;
2545                         bestMatchLength = matchLength;
2546                         isLeapMonth = 0;
2547                     }
2548                 if (monthPattern != null) {
2549                     String leapMonthName = MessageFormat.format(monthPattern, data[i]);
2550                     length = leapMonthName.length();
2551                     if (length > bestMatchLength &&
2552                         (matchLength = regionMatchesWithOptionalDot(text, start, leapMonthName, length)) >= 0)
2553                         {
2554                             bestMatch = i;
2555                             bestMatchLength = matchLength;
2556                             isLeapMonth = 1;
2557                         }
2558                  }
2559             }
2560         if (bestMatch >= 0)
2561             {
2562                 if (field == Calendar.YEAR) {
2563                     bestMatch++; // only get here for cyclic year names, which match 1-based years 1-60
2564                 }
2565                 cal.set(field, bestMatch);
2566                 if (monthPattern != null) {
2567                     cal.set(Calendar.IS_LEAP_MONTH, isLeapMonth);
2568                 }
2569                 return start + bestMatchLength;
2570             }
2571         return ~start;
2572     }
2573
2574     private int regionMatchesWithOptionalDot(String text, int start, String data, int length) {
2575         boolean matches = text.regionMatches(true, start, data, 0, length);
2576         if (matches) {
2577             return length;
2578         }
2579         if (data.length() > 0 && data.charAt(data.length()-1) == '.') {
2580             if (text.regionMatches(true, start, data, 0, length-1)) {
2581                 return length - 1;
2582             }
2583         }
2584         return -1;
2585     }
2586
2587     /**
2588      * Attempt to match the text at a given position against an array of quarter
2589      * strings.  Since multiple strings in the array may match (for
2590      * example, if the array contains "a", "ab", and "abc", all will match
2591      * the input string "abcd") the longest match is returned.  As a side
2592      * effect, the given field of <code>cal</code> is set to the index
2593      * of the best match, if there is one.
2594      * @param text the time text being parsed.
2595      * @param start where to start parsing.
2596      * @param field the date field being parsed.
2597      * @param data the string array to parsed.
2598      * @return the new start position if matching succeeded; a negative
2599      * number indicating matching failure, otherwise.  As a side effect,
2600      * sets the <code>cal</code> field <code>field</code> to the index
2601      * of the best match, if matching succeeded.
2602      * @stable ICU 2.0
2603      */
2604     protected int matchQuarterString(String text, int start, int field, String[] data, Calendar cal)
2605     {
2606         int i = 0;
2607         int count = data.length;
2608
2609         // There may be multiple strings in the data[] array which begin with
2610         // the same prefix (e.g., Cerven and Cervenec (June and July) in Czech).
2611         // We keep track of the longest match, and return that.  Note that this
2612         // unfortunately requires us to test all array elements.
2613         int bestMatchLength = 0, bestMatch = -1;
2614         int matchLength = 0;
2615         for (; i<count; ++i) {
2616             int length = data[i].length();
2617             // Always compare if we have no match yet; otherwise only compare
2618             // against potentially better matches (longer strings).
2619             if (length > bestMatchLength &&
2620                 (matchLength = regionMatchesWithOptionalDot(text, start, data[i], length)) >= 0) {
2621
2622                 bestMatch = i;
2623                 bestMatchLength = matchLength;
2624             }
2625         }
2626
2627         if (bestMatch >= 0) {
2628             cal.set(field, bestMatch * 3);
2629             return start + bestMatchLength;
2630         }
2631
2632         return -start;
2633     }
2634
2635     /**
2636      * Protected method that converts one field of the input string into a
2637      * numeric field value in <code>cal</code>.  Returns -start (for
2638      * ParsePosition) if failed.  Subclasses may override this method to
2639      * modify or add parsing capabilities.
2640      * @param text the time text to be parsed.
2641      * @param start where to start parsing.
2642      * @param ch the pattern character for the date field text to be parsed.
2643      * @param count the count of a pattern character.
2644      * @param obeyCount if true, then the next field directly abuts this one,
2645      * and we should use the count to know when to stop parsing.
2646      * @param ambiguousYear return parameter; upon return, if ambiguousYear[0]
2647      * is true, then a two-digit year was parsed and may need to be readjusted.
2648      * @param cal
2649      * @return the new start position if matching succeeded; a negative
2650      * number indicating matching failure, otherwise.  As a side effect,
2651      * set the appropriate field of <code>cal</code> with the parsed
2652      * value.
2653      * @stable ICU 2.0
2654      */
2655     protected int subParse(String text, int start, char ch, int count,
2656                            boolean obeyCount, boolean allowNegative,
2657                            boolean[] ambiguousYear, Calendar cal)
2658     {
2659         return subParse(text, start, ch, count, obeyCount, allowNegative, ambiguousYear, cal, null);
2660     }
2661
2662     /**
2663      * Protected method that converts one field of the input string into a
2664      * numeric field value in <code>cal</code>.  Returns -start (for
2665      * ParsePosition) if failed.  Subclasses may override this method to
2666      * modify or add parsing capabilities.
2667      * @param text the time text to be parsed.
2668      * @param start where to start parsing.
2669      * @param ch the pattern character for the date field text to be parsed.
2670      * @param count the count of a pattern character.
2671      * @param obeyCount if true, then the next field directly abuts this one,
2672      * and we should use the count to know when to stop parsing.
2673      * @param ambiguousYear return parameter; upon return, if ambiguousYear[0]
2674      * is true, then a two-digit year was parsed and may need to be readjusted.
2675      * @param cal
2676      * @param numericLeapMonthFormatter if non-null, used to parse numeric leap months. 
2677      * @return the new start position if matching succeeded; a negative
2678      * number indicating matching failure, otherwise.  As a side effect,
2679      * set the appropriate field of <code>cal</code> with the parsed
2680      * value.
2681      * @internal
2682      * @deprecated This API is ICU internal only.
2683      */
2684     private int subParse(String text, int start, char ch, int count,
2685                            boolean obeyCount, boolean allowNegative,
2686                            boolean[] ambiguousYear, Calendar cal, MessageFormat numericLeapMonthFormatter)
2687     {
2688         Number number = null;
2689         NumberFormat currentNumberFormat = null;
2690         int value = 0;
2691         int i;
2692         ParsePosition pos = new ParsePosition(0);
2693
2694         //int patternCharIndex = DateFormatSymbols.patternChars.indexOf(ch);c
2695         int patternCharIndex = -1;
2696         if ('A' <= ch && ch <= 'z') {
2697             patternCharIndex = PATTERN_CHAR_TO_INDEX[(int)ch - PATTERN_CHAR_BASE];
2698         }
2699
2700         if (patternCharIndex == -1) {
2701             return ~start;
2702         }
2703
2704         currentNumberFormat = getNumberFormat(ch);
2705
2706         int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
2707         
2708         if (numericLeapMonthFormatter != null) {
2709             numericLeapMonthFormatter.setFormatByArgumentIndex(0, currentNumberFormat);
2710         }
2711         boolean isChineseCalendar = ( cal.getType().equals("chinese") || cal.getType().equals("dangi") );
2712
2713         // If there are any spaces here, skip over them.  If we hit the end
2714         // of the string, then fail.
2715         for (;;) {
2716             if (start >= text.length()) {
2717                 return ~start;
2718             }
2719             int c = UTF16.charAt(text, start);
2720             if (!UCharacter.isUWhiteSpace(c) || !PatternProps.isWhiteSpace(c)) {
2721                 break;
2722             }
2723             start += UTF16.getCharCount(c);
2724         }
2725         pos.setIndex(start);
2726
2727         // We handle a few special cases here where we need to parse
2728         // a number value.  We handle further, more generic cases below.  We need
2729         // to handle some of them here because some fields require extra processing on
2730         // the parsed value.
2731         if (patternCharIndex == 4 /*'k' HOUR_OF_DAY1_FIELD*/ ||
2732             patternCharIndex == 15 /*'h' HOUR1_FIELD*/ ||
2733             (patternCharIndex == 2 /*'M' MONTH_FIELD*/ && count <= 2) ||
2734             (patternCharIndex == 26 /*'L' STAND_ALONE_MONTH*/ && count <= 2) ||
2735             patternCharIndex == 1 /*'y' YEAR */ || patternCharIndex == 18 /*'Y' YEAR_WOY */ ||
2736             patternCharIndex == 30 /*'U' YEAR_NAME_FIELD, falls back to numeric */ ||
2737             (patternCharIndex == 0 /*'G' ERA */ && isChineseCalendar) ||
2738             patternCharIndex == 8 /*'S' FRACTIONAL_SECOND */ )
2739             {
2740                 // It would be good to unify this with the obeyCount logic below,
2741                 // but that's going to be difficult.
2742                 
2743                 boolean parsedNumericLeapMonth = false;
2744                 if (numericLeapMonthFormatter != null && (patternCharIndex == 2 || patternCharIndex == 26)) {
2745                     // First see if we can parse month number with leap month pattern
2746                     Object[] args = numericLeapMonthFormatter.parse(text, pos);
2747                     if (args != null && pos.getIndex() > start && (args[0] instanceof Number)) {
2748                         parsedNumericLeapMonth = true;
2749                         number = (Number)args[0];
2750                         cal.set(Calendar.IS_LEAP_MONTH, 1);
2751                     } else {
2752                         pos.setIndex(start);
2753                         cal.set(Calendar.IS_LEAP_MONTH, 0);
2754                    }
2755                 }
2756                 
2757                 if (!parsedNumericLeapMonth) {
2758                     if (obeyCount) {
2759                         if ((start+count) > text.length()) {
2760                             return ~start;
2761                         }
2762                         number = parseInt(text, count, pos, allowNegative,currentNumberFormat);
2763                     } else {
2764                         number = parseInt(text, pos, allowNegative,currentNumberFormat);
2765                     }
2766                     if (number == null && patternCharIndex != 30) {
2767                         return ~start;
2768                     }
2769                 }
2770
2771                 if (number != null) {
2772                     value = number.intValue();
2773                 }
2774             }
2775
2776         switch (patternCharIndex)
2777             {
2778             case 0: // 'G' - ERA
2779                 if ( isChineseCalendar ) {
2780                     // Numeric era handling moved from ChineseDateFormat,
2781                     // If we didn't have a number, already returned -start above
2782                     cal.set(Calendar.ERA, value);
2783                     return pos.getIndex();
2784                 }
2785                 int ps = 0;
2786                 if (count == 5) {
2787                     ps = matchString(text, start, Calendar.ERA, formatData.narrowEras, null, cal);
2788                 } else if (count == 4) {
2789                     ps = matchString(text, start, Calendar.ERA, formatData.eraNames, null, cal);
2790                 } else {
2791                     ps = matchString(text, start, Calendar.ERA, formatData.eras, null, cal);
2792                 }
2793
2794                 // check return position, if it equals -start, then matchString error
2795                 // special case the return code so we don't necessarily fail out until we 
2796                 // verify no year information also
2797                 if (ps == ~start)
2798                     ps = ISOSpecialEra;
2799
2800                 return ps;  
2801                 
2802             case 1: // 'y' - YEAR
2803             case 18: // 'Y' - YEAR_WOY
2804                 // If there are 3 or more YEAR pattern characters, this indicates
2805                 // that the year value is to be treated literally, without any
2806                 // two-digit year adjustments (e.g., from "01" to 2001).  Otherwise
2807                 // we made adjustments to place the 2-digit year in the proper
2808                 // century, for parsed strings from "00" to "99".  Any other string
2809                 // is treated literally:  "2250", "-1", "1", "002".
2810                 /* 'yy' is the only special case, 'y' is interpreted as number. [Richard/GCL]*/
2811                 /* Skip this for Chinese calendar, moved from ChineseDateFormat */
2812                 if ( override != null && (override.compareTo("hebr") == 0 || override.indexOf("y=hebr") >= 0) && value < 1000 ) {
2813                     value += HEBREW_CAL_CUR_MILLENIUM_START_YEAR;
2814                 } else if (count == 2 && (pos.getIndex() - start) == 2 && !isChineseCalendar
2815                     && UCharacter.isDigit(text.charAt(start))
2816                     && UCharacter.isDigit(text.charAt(start+1)))
2817                     {
2818                         // Assume for example that the defaultCenturyStart is 6/18/1903.
2819                         // This means that two-digit years will be forced into the range
2820                         // 6/18/1903 to 6/17/2003.  As a result, years 00, 01, and 02
2821                         // correspond to 2000, 2001, and 2002.  Years 04, 05, etc. correspond
2822                         // to 1904, 1905, etc.  If the year is 03, then it is 2003 if the
2823                         // other fields specify a date before 6/18, or 1903 if they specify a
2824                         // date afterwards.  As a result, 03 is an ambiguous year.  All other
2825                         // two-digit years are unambiguous.
2826                         int ambiguousTwoDigitYear = getDefaultCenturyStartYear() % 100;
2827                         ambiguousYear[0] = value == ambiguousTwoDigitYear;
2828                         value += (getDefaultCenturyStartYear()/100)*100 +
2829                             (value < ambiguousTwoDigitYear ? 100 : 0);
2830                     }
2831                 cal.set(field, value);
2832
2833                 // Delayed checking for adjustment of Hebrew month numbers in non-leap years.
2834                 if (DelayedHebrewMonthCheck) {
2835                     if (!HebrewCalendar.isLeapYear(value)) {
2836                         cal.add(Calendar.MONTH,1);
2837                     }
2838                     DelayedHebrewMonthCheck = false;
2839                 }
2840                 return pos.getIndex();
2841             case 30: // 'U' - YEAR_NAME_FIELD
2842                 if (formatData.shortYearNames != null) {
2843                     int newStart = matchString(text, start, Calendar.YEAR, formatData.shortYearNames, null, cal);
2844                     if (newStart > 0) {
2845                         return newStart;
2846                     }
2847                 }
2848                 if ( number != null && (getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_NUMERIC) || formatData.shortYearNames == null || value > formatData.shortYearNames.length) ) {
2849                     cal.set(Calendar.YEAR, value);
2850                     return pos.getIndex();
2851                 }
2852                 return ~start;
2853             case 2: // 'M' - MONTH
2854             case 26: // 'L' - STAND_ALONE_MONTH
2855                 if (count <= 2) { // i.e., M/MM, L/LL
2856                     // Don't want to parse the month if it is a string
2857                     // while pattern uses numeric style: M/MM, L/LL.
2858                     // [We computed 'value' above.]
2859                     cal.set(Calendar.MONTH, value - 1);
2860                     // When parsing month numbers from the Hebrew Calendar, we might need
2861                     // to adjust the month depending on whether or not it was a leap year.
2862                     // We may or may not yet know what year it is, so might have to delay
2863                     // checking until the year is parsed.
2864                     if (cal.getType().equals("hebrew") && value >= 6) {
2865                         if (cal.isSet(Calendar.YEAR)) {
2866                             if (!HebrewCalendar.isLeapYear(cal.get(Calendar.YEAR))) {
2867                                 cal.set(Calendar.MONTH, value);
2868                             }
2869                         } else {
2870                             DelayedHebrewMonthCheck = true;
2871                         }
2872                     }
2873                     return pos.getIndex();
2874                 } else {
2875                     // count >= 3 // i.e., MMM/MMMM or LLL/LLLL
2876                     // Want to be able to parse both short and long forms.
2877                     boolean haveMonthPat = (formatData.leapMonthPatterns != null && formatData.leapMonthPatterns.length >= DateFormatSymbols.DT_MONTH_PATTERN_COUNT);
2878                     // Try count == 4 first:
2879                     int newStart = (patternCharIndex == 2)?
2880                             matchString(text, start, Calendar.MONTH, formatData.months,
2881                                     (haveMonthPat)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_FORMAT_WIDE]: null, cal):
2882                             matchString(text, start, Calendar.MONTH, formatData.standaloneMonths,
2883                                     (haveMonthPat)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_STANDALONE_WIDE]: null, cal);
2884                     if (newStart > 0) {
2885                         return newStart;
2886                     } else { // count == 4 failed, now try count == 3
2887                         return (patternCharIndex == 2)?
2888                                 matchString(text, start, Calendar.MONTH, formatData.shortMonths,
2889                                         (haveMonthPat)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_FORMAT_ABBREV]: null, cal):
2890                                 matchString(text, start, Calendar.MONTH, formatData.standaloneShortMonths,
2891                                         (haveMonthPat)? formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_STANDALONE_ABBREV]: null, cal);
2892                     }
2893                 }
2894             case 4: // 'k' - HOUR_OF_DAY (1..24)
2895                 // [We computed 'value' above.]
2896                 if (value == cal.getMaximum(Calendar.HOUR_OF_DAY)+1) {
2897                     value = 0;
2898                 }
2899                 cal.set(Calendar.HOUR_OF_DAY, value);
2900                 return pos.getIndex();
2901             case 8: // 'S' - FRACTIONAL_SECOND
2902                 // Fractional seconds left-justify
2903                 i = pos.getIndex() - start;
2904                 if (i < 3) {
2905                     while (i < 3) {
2906                         value *= 10;
2907                         i++;
2908                     }
2909                 } else {
2910                     int a = 1;
2911                     while (i > 3) {
2912                         a *= 10;
2913                         i--;
2914                     }
2915                     value /= a;
2916                 }
2917                 cal.set(Calendar.MILLISECOND, value);
2918                 return pos.getIndex();
2919             case 9: { // 'E' - DAY_OF_WEEK
2920                 // Want to be able to parse at least wide, abbrev, short forms.
2921                 int newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.weekdays, null, cal); // try EEEE wide
2922                 if (newStart > 0) {
2923                     return newStart;
2924                 } else if ((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.shortWeekdays, null, cal)) > 0) { // try EEE abbrev
2925                     return newStart;
2926                 } else if (formatData.shorterWeekdays != null) {
2927                     return matchString(text, start, Calendar.DAY_OF_WEEK, formatData.shorterWeekdays, null, cal); // try EEEEEE short
2928                 }
2929                 return newStart;
2930             }
2931             case 25: { // 'c' - STAND_ALONE_DAY_OF_WEEK
2932                 // Want to be able to parse at least wide, abbrev, short forms.
2933                 int newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.standaloneWeekdays, null, cal); // try cccc wide
2934                 if (newStart > 0) {
2935                     return newStart;
2936                 } else if ((newStart = matchString(text, start, Calendar.DAY_OF_WEEK, formatData.standaloneShortWeekdays, null, cal)) > 0) { // try ccc abbrev
2937                     return newStart;
2938                 } else if (formatData.standaloneShorterWeekdays != null) {
2939                     return matchString(text, start, Calendar.DAY_OF_WEEK, formatData.standaloneShorterWeekdays, null, cal); // try cccccc short
2940                 }
2941                 return newStart;
2942             }
2943             case 14: // 'a' - AM_PM
2944                 return matchString(text, start, Calendar.AM_PM, formatData.ampms, null, cal);
2945             case 15: // 'h' - HOUR (1..12)
2946                 // [We computed 'value' above.]
2947                 if (value == cal.getLeastMaximum(Calendar.HOUR)+1) {
2948                     value = 0;
2949                 }
2950                 cal.set(Calendar.HOUR, value);
2951                 return pos.getIndex();
2952             case 17: // 'z' - ZONE_OFFSET
2953             {
2954                 Output<TimeType> tzTimeType = new Output<TimeType>();
2955                 Style style = (count < 4) ? Style.SPECIFIC_SHORT : Style.SPECIFIC_LONG;
2956                 TimeZone tz = tzFormat().parse(style, text, pos, tzTimeType);
2957                 if (tz != null) {
2958                     tztype = tzTimeType.value;
2959                     cal.setTimeZone(tz);
2960                     return pos.getIndex();
2961                 }
2962                 return ~start;
2963             }
2964             case 23: // 'Z' - TIMEZONE_RFC
2965             {
2966                 Output<TimeType> tzTimeType = new Output<TimeType>();
2967                 Style style = (count < 4) ? Style.ISO_BASIC_LOCAL_FULL : ((count == 5) ? Style.ISO_EXTENDED_FULL : Style.LOCALIZED_GMT);
2968                 TimeZone tz = tzFormat().parse(style, text, pos, tzTimeType);
2969                 if (tz != null) {
2970                     tztype = tzTimeType.value;
2971                     cal.setTimeZone(tz);
2972                     return pos.getIndex();
2973                     }
2974                 return ~start;
2975                 }
2976             case 24: // 'v' - TIMEZONE_GENERIC
2977             {
2978                 Output<TimeType> tzTimeType = new Output<TimeType>();
2979                 // Note: 'v' only supports count 1 and 4
2980                 Style style = (count < 4) ? Style.GENERIC_SHORT : Style.GENERIC_LONG;
2981                 TimeZone tz = tzFormat().parse(style, text, pos, tzTimeType);
2982                 if (tz != null) {
2983                     tztype = tzTimeType.value;
2984                     cal.setTimeZone(tz);
2985                     return pos.getIndex();
2986                 }
2987                 return ~start;
2988             }
2989             case 29: // 'V' - TIMEZONE_SPECIAL
2990             {
2991                 Output<TimeType> tzTimeType = new Output<TimeType>();
2992                 Style style = null;
2993                 switch (count) {
2994                 case 1:
2995                     style = Style.ZONE_ID_SHORT;
2996                     break;
2997                 case 2:
2998                     style = Style.ZONE_ID;
2999                     break;
3000                 case 3:
3001                     style = Style.EXEMPLAR_LOCATION;
3002                     break;
3003                 default:
3004                     style = Style.GENERIC_LOCATION;
3005                     break;
3006                 }
3007                 TimeZone tz = tzFormat().parse(style, text, pos, tzTimeType);
3008                 if (tz != null) {
3009                     tztype = tzTimeType.value;
3010                     cal.setTimeZone(tz);
3011                     return pos.getIndex();
3012                 }
3013                 return ~start;
3014             }
3015             case 31: // 'O' - TIMEZONE_LOCALIZED_GMT_OFFSET
3016             {
3017                 Output<TimeType> tzTimeType = new Output<TimeType>();
3018                 Style style = (count < 4) ? Style.LOCALIZED_GMT_SHORT : Style.LOCALIZED_GMT;
3019                 TimeZone tz = tzFormat().parse(style, text, pos, tzTimeType);
3020                 if (tz != null) {
3021                     tztype = tzTimeType.value;
3022                     cal.setTimeZone(tz);
3023                     return pos.getIndex();
3024                 }
3025                 return ~start;
3026             }
3027             case 32: // 'X' - TIMEZONE_ISO
3028             {
3029                 Output<TimeType> tzTimeType = new Output<TimeType>();
3030                 Style style;
3031                 switch (count) {
3032                 case 1:
3033                     style = Style.ISO_BASIC_SHORT;
3034                     break;
3035                 case 2:
3036                     style = Style.ISO_BASIC_FIXED;
3037                     break;
3038                 case 3:
3039                     style = Style.ISO_EXTENDED_FIXED;
3040                     break;
3041                 case 4:
3042                     style = Style.ISO_BASIC_FULL;
3043                     break;
3044                 default: // count >= 5
3045                     style = Style.ISO_EXTENDED_FULL;
3046                     break;
3047                 }
3048                 TimeZone tz = tzFormat().parse(style, text, pos, tzTimeType);
3049                 if (tz != null) {
3050                     tztype = tzTimeType.value;
3051                     cal.setTimeZone(tz);
3052                     return pos.getIndex();
3053                 }
3054                 return ~start;
3055             }
3056             case 33: // 'x' - TIMEZONE_ISO_LOCAL
3057             {
3058                 Output<TimeType> tzTimeType = new Output<TimeType>();
3059                 Style style;
3060                 switch (count) {
3061                 case 1:
3062                     style = Style.ISO_BASIC_LOCAL_SHORT;
3063                     break;
3064                 case 2:
3065                     style = Style.ISO_BASIC_LOCAL_FIXED;
3066                     break;
3067                 case 3:
3068                     style = Style.ISO_EXTENDED_LOCAL_FIXED;
3069                     break;
3070                 case 4:
3071                     style = Style.ISO_BASIC_LOCAL_FULL;
3072                     break;
3073                 default: // count >= 5
3074                     style = Style.ISO_EXTENDED_LOCAL_FULL;
3075                     break;
3076                 }
3077                 TimeZone tz = tzFormat().parse(style, text, pos, tzTimeType);
3078                 if (tz != null) {
3079                     tztype = tzTimeType.value;
3080                     cal.setTimeZone(tz);
3081                     return pos.getIndex();
3082                 }
3083                 return ~start;
3084             }
3085             case 27: // 'Q' - QUARTER
3086                 if (count <= 2) { // i.e., Q or QQ.
3087                     // Don't want to parse the quarter if it is a string
3088                     // while pattern uses numeric style: Q or QQ.
3089                     // [We computed 'value' above.]
3090                     cal.set(Calendar.MONTH, (value - 1) * 3);
3091                     return pos.getIndex();
3092                 } else {
3093                     // count >= 3 // i.e., QQQ or QQQQ
3094                     // Want to be able to parse both short and long forms.
3095                     // Try count == 4 first:
3096                     int newStart = matchQuarterString(text, start, Calendar.MONTH,
3097                                                formatData.quarters, cal);
3098                     if (newStart > 0) {
3099                         return newStart;
3100                     } else { // count == 4 failed, now try count == 3
3101                         return matchQuarterString(text, start, Calendar.MONTH,
3102                                            formatData.shortQuarters, cal);
3103                     }
3104                 }
3105
3106             case 28: // 'q' - STANDALONE QUARTER
3107                 if (count <= 2) { // i.e., q or qq.
3108                     // Don't want to parse the quarter if it is a string
3109                     // while pattern uses numeric style: q or qq.
3110                     // [We computed 'value' above.]
3111                     cal.set(Calendar.MONTH, (value - 1) * 3);
3112                     return pos.getIndex();
3113                 } else {
3114                     // count >= 3 // i.e., qqq or qqqq
3115                     // Want to be able to parse both short and long forms.
3116                     // Try count == 4 first:
3117                     int newStart = matchQuarterString(text, start, Calendar.MONTH,
3118                                                formatData.standaloneQuarters, cal);
3119                     if (newStart > 0) {
3120                         return newStart;
3121                     } else { // count == 4 failed, now try count == 3
3122                         return matchQuarterString(text, start, Calendar.MONTH,
3123                                            formatData.standaloneShortQuarters, cal);
3124                     }
3125                 }
3126
3127             default:
3128                 // case 3: // 'd' - DATE
3129                 // case 5: // 'H' - HOUR_OF_DAY (0..23)
3130                 // case 6: // 'm' - MINUTE
3131                 // case 7: // 's' - SECOND
3132                 // case 10: // 'D' - DAY_OF_YEAR
3133                 // case 11: // 'F' - DAY_OF_WEEK_IN_MONTH
3134                 // case 12: // 'w' - WEEK_OF_YEAR
3135                 // case 13: // 'W' - WEEK_OF_MONTH
3136                 // case 16: // 'K' - HOUR (0..11)
3137                 // case 19: // 'e' - DOW_LOCAL
3138                 // case 20: // 'u' - EXTENDED_YEAR
3139                 // case 21: // 'g' - JULIAN_DAY
3140                 // case 22: // 'A' - MILLISECONDS_IN_DAY
3141
3142                 // Handle "generic" fields
3143                 if (obeyCount) {
3144                     if ((start+count) > text.length()) return -start;
3145                     number = parseInt(text, count, pos, allowNegative,currentNumberFormat);
3146                 } else {
3147                     number = parseInt(text, pos, allowNegative,currentNumberFormat);
3148                 }
3149                 if (number != null) {
3150                     cal.set(field, number.intValue());
3151                     return pos.getIndex();
3152                 }
3153                 return ~start;
3154             }
3155     }
3156
3157     /**
3158      * Parse an integer using numberFormat.  This method is semantically
3159      * const, but actually may modify fNumberFormat.
3160      */
3161     private Number parseInt(String text,
3162                             ParsePosition pos,
3163                             boolean allowNegative,
3164                             NumberFormat fmt) {
3165         return parseInt(text, -1, pos, allowNegative, fmt);
3166     }
3167
3168     /**
3169      * Parse an integer using numberFormat up to maxDigits.
3170      */
3171     private Number parseInt(String text,
3172                             int maxDigits,
3173                             ParsePosition pos,
3174                             boolean allowNegative,
3175                             NumberFormat fmt) {
3176         Number number;
3177         int oldPos = pos.getIndex();
3178         if (allowNegative) {
3179             number = fmt.parse(text, pos);
3180         } else {
3181             // Invalidate negative numbers
3182             if (fmt instanceof DecimalFormat) {
3183                 String oldPrefix = ((DecimalFormat)fmt).getNegativePrefix();
3184                 ((DecimalFormat)fmt).setNegativePrefix(SUPPRESS_NEGATIVE_PREFIX);
3185                 number = fmt.parse(text, pos);
3186                 ((DecimalFormat)fmt).setNegativePrefix(oldPrefix);
3187             } else {
3188                 boolean dateNumberFormat = (fmt instanceof DateNumberFormat);
3189                 if (dateNumberFormat) {
3190                     ((DateNumberFormat)fmt).setParsePositiveOnly(true);
3191                 }
3192                 number = fmt.parse(text, pos);
3193                 if (dateNumberFormat) {
3194                     ((DateNumberFormat)fmt).setParsePositiveOnly(false);
3195                 }
3196             }
3197         }
3198         if (maxDigits > 0) {
3199             // adjust the result to fit into
3200             // the maxDigits and move the position back
3201             int nDigits = pos.getIndex() - oldPos;
3202             if (nDigits > maxDigits) {
3203                 double val = number.doubleValue();
3204                 nDigits -= maxDigits;
3205                 while (nDigits > 0) {
3206                     val /= 10;
3207                     nDigits--;
3208                 }
3209                 pos.setIndex(oldPos + maxDigits);
3210                 number = Integer.valueOf((int)val);
3211             }
3212         }
3213         return number;
3214     }
3215
3216
3217     /**
3218      * Translate a pattern, mapping each character in the from string to the
3219      * corresponding character in the to string.
3220      */
3221     private String translatePattern(String pat, String from, String to) {
3222         StringBuilder result = new StringBuilder();
3223         boolean inQuote = false;
3224         for (int i = 0; i < pat.length(); ++i) {
3225             char c = pat.charAt(i);
3226             if (inQuote) {
3227                 if (c == '\'')
3228                     inQuote = false;
3229             } else {
3230                 if (c == '\'') {
3231                     inQuote = true;
3232                 } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
3233                     int ci = from.indexOf(c);
3234                     if (ci != -1) {
3235                         c = to.charAt(ci);
3236                     }
3237                     // do not worry on translatepattern if the character is not listed
3238                     // we do the validity check elsewhere
3239                 }
3240             }
3241             result.append(c);
3242         }
3243         if (inQuote) {
3244             throw new IllegalArgumentException("Unfinished quote in pattern");
3245         }
3246         return result.toString();
3247     }
3248
3249     /**
3250      * Return a pattern string describing this date format.
3251      * @stable ICU 2.0
3252      */
3253     public String toPattern() {
3254         return pattern;
3255     }
3256
3257     /**
3258      * Return a localized pattern string describing this date format.
3259      * @stable ICU 2.0
3260      */
3261     public String toLocalizedPattern() {
3262         return translatePattern(pattern,
3263                                 DateFormatSymbols.patternChars,
3264                                 formatData.localPatternChars);
3265     }
3266
3267     /**
3268      * Apply the given unlocalized pattern string to this date format.
3269      * @stable ICU 2.0
3270      */
3271     public void applyPattern(String pat)
3272     {
3273         this.pattern = pat;
3274         setLocale(null, null);
3275         // reset parsed pattern items
3276         patternItems = null;
3277     }
3278
3279     /**
3280      * Apply the given localized pattern string to this date format.
3281      * @stable ICU 2.0
3282      */
3283     public void applyLocalizedPattern(String pat) {
3284         this.pattern = translatePattern(pat,
3285                                         formatData.localPatternChars,
3286                                         DateFormatSymbols.patternChars);
3287         setLocale(null, null);
3288     }
3289
3290     /**
3291      * Gets the date/time formatting data.
3292      * @return a copy of the date-time formatting data associated
3293      * with this date-time formatter.
3294      * @stable ICU 2.0
3295      */
3296     public DateFormatSymbols getDateFormatSymbols()
3297     {
3298         return (DateFormatSymbols)formatData.clone();
3299     }
3300
3301     /**
3302      * Allows you to set the date/time formatting data.
3303      * @param newFormatSymbols the new symbols
3304      * @stable ICU 2.0
3305      */
3306     public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols)
3307     {
3308         this.formatData = (DateFormatSymbols)newFormatSymbols.clone();
3309     }
3310
3311     /**
3312      * Method for subclasses to access the DateFormatSymbols.
3313      * @stable ICU 2.0
3314      */
3315     protected DateFormatSymbols getSymbols() {
3316         return formatData;
3317     }
3318
3319     /**
3320      * {@icu} Gets the time zone formatter which this date/time
3321      * formatter uses to format and parse a time zone.
3322      * 
3323      * @return the time zone formatter which this date/time
3324      * formatter uses.
3325      * @stable ICU 49
3326      */
3327     public TimeZoneFormat getTimeZoneFormat() {
3328         return tzFormat().freeze();
3329     }
3330
3331     /**
3332      * {@icu} Allows you to set the time zone formatter.
3333      * 
3334      * @param tzfmt the new time zone formatter
3335      * @stable ICU 49
3336      */
3337     public void setTimeZoneFormat(TimeZoneFormat tzfmt) {
3338         if (tzfmt.isFrozen()) {
3339             // If frozen, use it as is.
3340             tzFormat = tzfmt;
3341         } else {
3342             // If not frozen, clone and freeze.
3343             tzFormat = tzfmt.cloneAsThawed().freeze();
3344         }
3345     }
3346
3347     /**
3348      * {@icu} Set a particular DisplayContext value in the formatter,
3349      * such as CAPITALIZATION_FOR_STANDALONE. 
3350      * 
3351      * @param context The DisplayContext value to set. 
3352      * @draft ICU 51
3353      * @provisional This API might change or be removed in a future release.
3354      */
3355     public void setContext(DisplayContext context) {
3356         if (context.type() == DisplayContext.Type.CAPITALIZATION) {
3357             capitalizationSetting = context;
3358         }
3359     }
3360
3361     /**
3362      * {@icu} Get the formatter's DisplayContext value for the specified DisplayContext.Type,
3363      * such as CAPITALIZATION.
3364      * 
3365      * @param type the DisplayContext.Type whose value to return
3366      * @return the current DisplayContext setting for the specified type
3367      * @draft ICU 51
3368      * @provisional This API might change or be removed in a future release.
3369      */
3370     public DisplayContext getContext(DisplayContext.Type type) {
3371         return (type == DisplayContext.Type.CAPITALIZATION && capitalizationSetting != null)?
3372                 capitalizationSetting: DisplayContext.CAPITALIZATION_NONE;
3373     }
3374
3375     /**
3376      * Overrides Cloneable
3377      * @stable ICU 2.0
3378      */
3379     public Object clone() {
3380         SimpleDateFormat other = (SimpleDateFormat) super.clone();
3381         other.formatData = (DateFormatSymbols) formatData.clone();
3382         return other;
3383     }
3384
3385     /**
3386      * Override hashCode.
3387      * Generates the hash code for the SimpleDateFormat object
3388      * @stable ICU 2.0
3389      */
3390     public int hashCode()
3391     {
3392         return pattern.hashCode();
3393         // just enough fields for a reasonable distribution
3394     }
3395
3396     /**
3397      * Override equals.
3398      * @stable ICU 2.0
3399      */
3400     public boolean equals(Object obj)
3401     {
3402         if (!super.equals(obj)) return false; // super does class check
3403         SimpleDateFormat that = (SimpleDateFormat) obj;
3404         return (pattern.equals(that.pattern)
3405                 && formatData.equals(that.formatData));
3406     }
3407
3408     /**
3409      * Override writeObject.
3410      * See http://docs.oracle.com/javase/6/docs/api/java/io/ObjectOutputStream.html
3411      */
3412     private void writeObject(ObjectOutputStream stream) throws IOException{
3413         if (defaultCenturyStart == null) {
3414             // if defaultCenturyStart is not yet initialized,
3415             // calculate and set value before serialization.
3416             initializeDefaultCenturyStart(defaultCenturyBase);
3417         }
3418         initializeTimeZoneFormat(false);
3419         stream.defaultWriteObject();
3420         stream.writeInt(capitalizationSetting.value());
3421     }
3422
3423     /**
3424      * Override readObject.
3425      * See http://docs.oracle.com/javase/6/docs/api/java/io/ObjectInputStream.html
3426      */
3427     private void readObject(ObjectInputStream stream)
3428         throws IOException, ClassNotFoundException {
3429         stream.defaultReadObject();
3430         int capitalizationSettingValue = (serialVersionOnStream > 1)? stream.readInt(): -1;
3431         ///CLOVER:OFF
3432         // don't have old serial data to test with
3433         if (serialVersionOnStream < 1) {
3434             // didn't have defaultCenturyStart field
3435             defaultCenturyBase = System.currentTimeMillis();
3436         }
3437         ///CLOVER:ON
3438         else {
3439             // fill in dependent transient field
3440             parseAmbiguousDatesAsAfter(defaultCenturyStart);
3441         }
3442         serialVersionOnStream = currentSerialVersion;
3443         locale = getLocale(ULocale.VALID_LOCALE);
3444         if (locale == null) {
3445             // ICU4J 3.6 or older versions did not have UFormat locales
3446             // in the serialized data. This is just for preventing the
3447             // worst case scenario...
3448             locale = ULocale.getDefault(Category.FORMAT);
3449         }
3450
3451         initLocalZeroPaddingNumberFormat();
3452
3453         capitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
3454         if (capitalizationSettingValue >= 0) {
3455             for (DisplayContext context: DisplayContext.values()) {
3456                 if (context.value() == capitalizationSettingValue) {
3457                     capitalizationSetting = context;
3458                     break;
3459                 }
3460             }
3461         }
3462     }
3463
3464     /**
3465      * Format the object to an attributed string, and return the corresponding iterator
3466      * Overrides superclass method.
3467      *
3468      * @param obj The object to format
3469      * @return <code>AttributedCharacterIterator</code> describing the formatted value.
3470      *
3471      * @stable ICU 3.8
3472      */
3473     public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
3474         Calendar cal = calendar;
3475         if (obj instanceof Calendar) {
3476             cal = (Calendar)obj;
3477         } else if (obj instanceof Date) {
3478             calendar.setTime((Date)obj);
3479         } else if (obj instanceof Number) {
3480             calendar.setTimeInMillis(((Number)obj).longValue());
3481         } else {
3482             throw new IllegalArgumentException("Cannot format given Object as a Date");
3483         }
3484         StringBuffer toAppendTo = new StringBuffer();
3485         FieldPosition pos = new FieldPosition(0);
3486         List<FieldPosition> attributes = new ArrayList<FieldPosition>();
3487         format(cal, capitalizationSetting, toAppendTo, pos, attributes);
3488
3489         AttributedString as = new AttributedString(toAppendTo.toString());
3490
3491         // add DateFormat field attributes to the AttributedString
3492         for (int i = 0; i < attributes.size(); i++) {
3493             FieldPosition fp = attributes.get(i);
3494             Format.Field attribute = fp.getFieldAttribute();
3495             as.addAttribute(attribute, attribute, fp.getBeginIndex(), fp.getEndIndex());
3496         }
3497         // return the CharacterIterator from AttributedString
3498         return as.getIterator();
3499     }
3500
3501     /**
3502      * Get the locale of this simple date formatter.
3503      * It is package accessible. also used in DateIntervalFormat.
3504      *
3505      * @return   locale in this simple date formatter
3506      */
3507     ULocale getLocale()
3508     {
3509         return locale;
3510     }
3511
3512
3513
3514     /**
3515      * Check whether the 'field' is smaller than all the fields covered in
3516      * pattern, return true if it is.
3517      * The sequence of calendar field,
3518      * from large to small is: ERA, YEAR, MONTH, DATE, AM_PM, HOUR, MINUTE,...
3519      * @param field    the calendar field need to check against
3520      * @return         true if the 'field' is smaller than all the fields
3521      *                 covered in pattern. false otherwise.
3522      */
3523
3524     boolean isFieldUnitIgnored(int field) {
3525         return isFieldUnitIgnored(pattern, field);
3526     }
3527
3528
3529     /*
3530      * Check whether the 'field' is smaller than all the fields covered in
3531      * pattern, return true if it is.
3532      * The sequence of calendar field,
3533      * from large to small is: ERA, YEAR, MONTH, DATE, AM_PM, HOUR, MINUTE,...
3534      * @param pattern  the pattern to check against
3535      * @param field    the calendar field need to check against
3536      * @return         true if the 'field' is smaller than all the fields
3537      *                 covered in pattern. false otherwise.
3538      */
3539     static boolean isFieldUnitIgnored(String pattern, int field) {
3540         int fieldLevel = CALENDAR_FIELD_TO_LEVEL[field];
3541         int level;
3542         char ch;
3543         boolean inQuote = false;
3544         char prevCh = 0;
3545         int count = 0;
3546
3547         for (int i = 0; i < pattern.length(); ++i) {
3548             ch = pattern.charAt(i);
3549             if (ch != prevCh && count > 0) {
3550                 level = PATTERN_CHAR_TO_LEVEL[prevCh - PATTERN_CHAR_BASE];
3551                 if ( fieldLevel <= level ) {
3552                     return false;
3553                 }
3554                 count = 0;
3555             }
3556             if (ch == '\'') {
3557                 if ((i+1) < pattern.length() && pattern.charAt(i+1) == '\'') {
3558                     ++i;
3559                 } else {
3560                     inQuote = ! inQuote;
3561                 }
3562             } else if ( ! inQuote && ((ch >= 0x0061 /*'a'*/ && ch <= 0x007A /*'z'*/)
3563                         || (ch >= 0x0041 /*'A'*/ && ch <= 0x005A /*'Z'*/))) {
3564                 prevCh = ch;
3565                 ++count;
3566             }
3567         }
3568         if (count > 0) {
3569             // last item
3570             level = PATTERN_CHAR_TO_LEVEL[prevCh - PATTERN_CHAR_BASE];
3571             if ( fieldLevel <= level ) {
3572                 return false;
3573             }
3574         }
3575         return true;
3576     }
3577
3578
3579     /**
3580      * Format date interval by algorithm.
3581      * It is supposed to be used only by CLDR survey tool.
3582      *
3583      * @param fromCalendar      calendar set to the from date in date interval
3584      *                          to be formatted into date interval stirng
3585      * @param toCalendar        calendar set to the to date in date interval
3586      *                          to be formatted into date interval stirng
3587      * @param appendTo          Output parameter to receive result.
3588      *                          Result is appended to existing contents.
3589      * @param pos               On input: an alignment field, if desired.
3590      *                          On output: the offsets of the alignment field.
3591      * @exception IllegalArgumentException when there is non-recognized
3592      *                                     pattern letter
3593      * @return                  Reference to 'appendTo' parameter.
3594      * @internal
3595      * @deprecated This API is ICU internal only.
3596      */
3597     public final StringBuffer intervalFormatByAlgorithm(Calendar fromCalendar,
3598                                                         Calendar toCalendar,
3599                                                         StringBuffer appendTo,
3600                                                         FieldPosition pos)
3601                               throws IllegalArgumentException
3602     {
3603         // not support different calendar types and time zones
3604         if ( !fromCalendar.isEquivalentTo(toCalendar) ) {
3605             throw new IllegalArgumentException("can not format on two different calendars");
3606         }
3607
3608         Object[] items = getPatternItems();
3609         int diffBegin = -1;
3610         int diffEnd = -1;
3611
3612         /* look for different formatting string range */
3613         // look for start of difference
3614         try {
3615             for (int i = 0; i < items.length; i++) {
3616                 if ( diffCalFieldValue(fromCalendar, toCalendar, items, i) ) {
3617                     diffBegin = i;
3618                     break;
3619                 }
3620             }
3621
3622             if ( diffBegin == -1 ) {
3623                 // no difference, single date format
3624                 return format(fromCalendar, appendTo, pos);
3625             }
3626
3627             // look for end of difference
3628             for (int i = items.length-1; i >= diffBegin; i--) {
3629                 if ( diffCalFieldValue(fromCalendar, toCalendar, items, i) ) {
3630                     diffEnd = i;
3631                     break;
3632                 }
3633             }
3634         } catch ( IllegalArgumentException e ) {
3635             throw new IllegalArgumentException(e.toString());
3636         }
3637
3638         // full range is different
3639         if ( diffBegin == 0 && diffEnd == items.length-1 ) {
3640             format(fromCalendar, appendTo, pos);
3641             appendTo.append(" \u2013 "); // default separator
3642             format(toCalendar, appendTo, pos);
3643             return appendTo;
3644         }
3645
3646
3647         /* search for largest calendar field within the different range */
3648         int highestLevel = 1000;
3649         for (int i = diffBegin; i <= diffEnd; i++) {
3650             if ( items[i] instanceof String) {
3651                 continue;
3652             }
3653             PatternItem item = (PatternItem)items[i];
3654             char ch = item.type;
3655             int patternCharIndex = -1;
3656             if ('A' <= ch && ch <= 'z') {
3657                 patternCharIndex = PATTERN_CHAR_TO_LEVEL[(int)ch - PATTERN_CHAR_BASE];
3658             }
3659
3660             if (patternCharIndex == -1) {
3661                 throw new IllegalArgumentException("Illegal pattern character " +
3662                                                    "'" + ch + "' in \"" +
3663                                                    pattern + '"');
3664             }
3665
3666             if ( patternCharIndex < highestLevel ) {
3667                 highestLevel = patternCharIndex;
3668             }
3669         }
3670
3671         /* re-calculate diff range, including those calendar field which
3672            is in lower level than the largest calendar field covered
3673            in diff range calculated. */
3674         try {
3675             for (int i = 0; i < diffBegin; i++) {
3676                 if ( lowerLevel(items, i, highestLevel) ) {
3677                     diffBegin = i;
3678                     break;
3679                 }
3680             }
3681
3682
3683             for (int i = items.length-1; i > diffEnd; i--) {
3684                 if ( lowerLevel(items, i, highestLevel) ) {
3685                     diffEnd = i;
3686                     break;
3687                 }
3688             }
3689         } catch ( IllegalArgumentException e ) {
3690             throw new IllegalArgumentException(e.toString());
3691         }
3692
3693
3694         // full range is different
3695         if ( diffBegin == 0 && diffEnd == items.length-1 ) {
3696             format(fromCalendar, appendTo, pos);
3697             appendTo.append(" \u2013 "); // default separator
3698             format(toCalendar, appendTo, pos);
3699             return appendTo;
3700         }
3701
3702
3703         // formatting
3704         // Initialize
3705         pos.setBeginIndex(0);
3706         pos.setEndIndex(0);
3707
3708         // formatting date 1
3709         for (int i = 0; i <= diffEnd; i++) {
3710             if (items[i] instanceof String) {
3711                 appendTo.append((String)items[i]);
3712             } else {
3713                 PatternItem item = (PatternItem)items[i];
3714                 if (useFastFormat) {
3715                     subFormat(appendTo, item.type, item.length, appendTo.length(),
3716                               i, capitalizationSetting, pos, fromCalendar);
3717                 } else {
3718                     appendTo.append(subFormat(item.type, item.length, appendTo.length(),
3719                                               i, capitalizationSetting, pos, fromCalendar));
3720                 }
3721             }
3722         }
3723
3724         appendTo.append(" \u2013 "); // default separator
3725
3726         // formatting date 2
3727         for (int i = diffBegin; i < items.length; i++) {
3728             if (items[i] instanceof String) {
3729                 appendTo.append((String)items[i]);
3730             } else {
3731                 PatternItem item = (PatternItem)items[i];
3732                 if (useFastFormat) {
3733                     subFormat(appendTo, item.type, item.length, appendTo.length(),
3734                               i, capitalizationSetting, pos, toCalendar);
3735                 } else {
3736                     appendTo.append(subFormat(item.type, item.length, appendTo.length(),
3737                                               i, capitalizationSetting, pos, toCalendar));
3738                 }
3739             }
3740         }
3741         return appendTo;
3742     }
3743
3744
3745     /**
3746      * check whether the i-th item in 2 calendar is in different value.
3747      *
3748      * It is supposed to be used only by CLDR survey tool.
3749      * It is used by intervalFormatByAlgorithm().
3750      *
3751      * @param fromCalendar   one calendar
3752      * @param toCalendar     the other calendar
3753      * @param items          pattern items
3754      * @param i              the i-th item in pattern items
3755      * @exception IllegalArgumentException when there is non-recognized
3756      *                                     pattern letter
3757      * @return               true is i-th item in 2 calendar is in different
3758      *                       value, false otherwise.
3759      */
3760     private boolean diffCalFieldValue(Calendar fromCalendar,
3761                                       Calendar toCalendar,
3762                                       Object[] items,
3763                                       int i) throws IllegalArgumentException {
3764         if ( items[i] instanceof String) {
3765             return false;
3766         }
3767         PatternItem item = (PatternItem)items[i];
3768         char ch = item.type;
3769         int patternCharIndex = -1;
3770         if ('A' <= ch && ch <= 'z') {
3771             patternCharIndex = PATTERN_CHAR_TO_INDEX[(int)ch - PATTERN_CHAR_BASE];
3772         }
3773
3774         if (patternCharIndex == -1) {
3775             throw new IllegalArgumentException("Illegal pattern character " +
3776                                                "'" + ch + "' in \"" +
3777                                                pattern + '"');
3778         }
3779
3780         final int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex];
3781         int value = fromCalendar.get(field);
3782         int value_2 = toCalendar.get(field);
3783         if ( value != value_2 ) {
3784             return true;
3785         }
3786         return false;
3787     }
3788
3789
3790     /**
3791      * check whether the i-th item's level is lower than the input 'level'
3792      *
3793      * It is supposed to be used only by CLDR survey tool.
3794      * It is used by intervalFormatByAlgorithm().
3795      *
3796      * @param items  the pattern items
3797      * @param i      the i-th item in pattern items
3798      * @param level  the level with which the i-th pattern item compared to
3799      * @exception IllegalArgumentException when there is non-recognized
3800      *                                     pattern letter
3801      * @return       true if i-th pattern item is lower than 'level',
3802      *               false otherwise
3803      */
3804     private boolean lowerLevel(Object[] items, int i, int level)
3805                     throws IllegalArgumentException {
3806         if ( items[i] instanceof String) {
3807             return false;
3808         }
3809         PatternItem item = (PatternItem)items[i];
3810         char ch = item.type;
3811         int patternCharIndex = -1;
3812         if ('A' <= ch && ch <= 'z') {
3813             patternCharIndex = PATTERN_CHAR_TO_LEVEL[(int)ch - PATTERN_CHAR_BASE];
3814         }
3815
3816         if (patternCharIndex == -1) {
3817             throw new IllegalArgumentException("Illegal pattern character " +
3818                                                "'" + ch + "' in \"" +
3819                                                pattern + '"');
3820         }
3821
3822         if ( patternCharIndex >= level ) {
3823             return true;
3824         }
3825         return false;
3826     }
3827
3828     /**
3829      * @internal
3830      * @deprecated This API is ICU internal only.
3831      */
3832     protected NumberFormat getNumberFormat(char ch) {
3833
3834        Character ovrField;
3835        ovrField = Character.valueOf(ch);
3836        if (overrideMap != null && overrideMap.containsKey(ovrField)) {
3837            String nsName = overrideMap.get(ovrField).toString();
3838            NumberFormat nf = numberFormatters.get(nsName);
3839            return nf;
3840        } else {
3841            return numberFormat;
3842        }
3843     }
3844
3845     private void initNumberFormatters(ULocale loc) {
3846
3847        numberFormatters = new HashMap<String, NumberFormat>();
3848        overrideMap = new HashMap<Character, String>();
3849        processOverrideString(loc,override);
3850
3851     }
3852
3853     private void processOverrideString(ULocale loc, String str) {
3854
3855         if ( str == null || str.length() == 0 )
3856             return;
3857
3858         int start = 0;
3859         int end;
3860         String nsName;
3861         Character ovrField;
3862         boolean moreToProcess = true;
3863         boolean fullOverride;
3864
3865         while (moreToProcess) {
3866             int delimiterPosition = str.indexOf(";",start);
3867             if (delimiterPosition == -1) {
3868                 moreToProcess = false;
3869                 end = str.length();
3870             } else {
3871                 end = delimiterPosition;
3872             }
3873
3874             String currentString = str.substring(start,end);
3875             int equalSignPosition = currentString.indexOf("=");
3876             if (equalSignPosition == -1) { // Simple override string such as "hebrew"
3877                nsName = currentString;
3878                fullOverride = true;
3879             } else { // Field specific override string such as "y=hebrew"
3880                nsName = currentString.substring(equalSignPosition+1);
3881                ovrField = Character.valueOf(currentString.charAt(0));
3882                overrideMap.put(ovrField,nsName);
3883                fullOverride = false;
3884             }
3885
3886             ULocale ovrLoc = new ULocale(loc.getBaseName()+"@numbers="+nsName);
3887             NumberFormat nf = NumberFormat.createInstance(ovrLoc,NumberFormat.NUMBERSTYLE);
3888             nf.setGroupingUsed(false);
3889             
3890             if (fullOverride) {
3891                 setNumberFormat(nf);
3892             } else {
3893                 // Since one or more of the override number formatters might be complex,
3894                 // we can't rely on the fast numfmt where we have a partial field override.
3895                 useLocalZeroPaddingNumberFormat = false;
3896             }
3897
3898             if (!numberFormatters.containsKey(nsName)) {
3899                   numberFormatters.put(nsName,nf);
3900             }
3901
3902             start = delimiterPosition + 1;
3903         }
3904     }
3905 }