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