1 /*
2 * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
516 * The symbols used by this formatter for week names, month names,
517 * etc. May not be null.
518 * @serial
519 * @see java.text.DateFormatSymbols
520 */
521 private DateFormatSymbols formatData;
522
523 /**
524 * We map dates with two-digit years into the century starting at
525 * <code>defaultCenturyStart</code>, which may be any date. May
526 * not be null.
527 * @serial
528 * @since 1.1.4
529 */
530 private Date defaultCenturyStart;
531
532 private transient int defaultCenturyStartYear;
533
534 private static final int MILLIS_PER_MINUTE = 60 * 1000;
535
536 // For time zones that have no names, use strings GMT+minutes and
537 // GMT-minutes. For instance, in France the time zone is GMT+60.
538 private static final String GMT = "GMT";
539
540 /**
541 * Cache NumberFormat instances with Locale key.
542 */
543 private static final ConcurrentMap<Locale, NumberFormat> cachedNumberFormatData
544 = new ConcurrentHashMap<>(3);
545
546 /**
547 * The Locale used to instantiate this
548 * <code>SimpleDateFormat</code>. The value may be null if this object
549 * has been created by an older <code>SimpleDateFormat</code> and
550 * deserialized.
551 *
552 * @serial
553 * @since 1.6
554 */
555 private Locale locale;
1172 current = "";
1173 }
1174 break;
1175
1176 case PATTERN_WEEK_YEAR: // 'Y'
1177 case PATTERN_YEAR: // 'y'
1178 if (calendar instanceof GregorianCalendar) {
1179 if (count != 2) {
1180 zeroPaddingNumber(value, count, maxIntCount, buffer);
1181 } else {
1182 zeroPaddingNumber(value, 2, 2, buffer);
1183 } // clip 1996 to 96
1184 } else {
1185 if (current == null) {
1186 zeroPaddingNumber(value, style == Calendar.LONG ? 1 : count,
1187 maxIntCount, buffer);
1188 }
1189 }
1190 break;
1191
1192 case PATTERN_MONTH: // 'M' (context seinsive)
1193 if (useDateFormatSymbols) {
1194 String[] months;
1195 if (count >= 4) {
1196 months = formatData.getMonths();
1197 current = months[value];
1198 } else if (count == 3) {
1199 months = formatData.getShortMonths();
1200 current = months[value];
1201 }
1202 } else {
1203 if (count < 3) {
1204 current = null;
1205 } else if (forceStandaloneForm) {
1206 current = calendar.getDisplayName(field, style | 0x8000, locale);
1207 if (current == null) {
1208 current = calendar.getDisplayName(field, style, locale);
1209 }
1210 }
1211 }
1212 if (current == null) {
1213 zeroPaddingNumber(value+1, count, maxIntCount, buffer);
1214 }
1215 break;
1216
1217 case PATTERN_MONTH_STANDALONE: // 'L'
1218 assert current == null;
1219 if (locale == null) {
1220 String[] months;
1221 if (count >= 4) {
1222 months = formatData.getMonths();
1223 current = months[value];
1224 } else if (count == 3) {
1225 months = formatData.getShortMonths();
1226 current = months[value];
1227 }
1228 } else {
1229 if (count >= 3) {
1230 current = calendar.getDisplayName(field, style | 0x8000, locale);
1231 }
1232 }
1233 if (current == null) {
1234 zeroPaddingNumber(value+1, count, maxIntCount, buffer);
1235 }
1236 break;
1237
1238 case PATTERN_HOUR_OF_DAY1: // 'k' 1-based. eg, 23:59 + 1 hour =>> 24:59
1239 if (current == null) {
1240 if (value == 0) {
1241 zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1,
1242 count, maxIntCount, buffer);
1243 } else {
1244 zeroPaddingNumber(value, count, maxIntCount, buffer);
1245 }
1246 }
1247 break;
1248
1249 case PATTERN_DAY_OF_WEEK: // 'E'
1250 if (useDateFormatSymbols) {
2016 // [We computed 'value' above.]
2017 calb.set(Calendar.MONTH, value - 1);
2018 return pos.index;
2019 }
2020
2021 if (useDateFormatSymbols) {
2022 // count >= 3 // i.e., MMM or MMMM
2023 // Want to be able to parse both short and long forms.
2024 // Try count == 4 first:
2025 int newStart;
2026 if ((newStart = matchString(text, start, Calendar.MONTH,
2027 formatData.getMonths(), calb)) > 0) {
2028 return newStart;
2029 }
2030 // count == 4 failed, now try count == 3
2031 if ((index = matchString(text, start, Calendar.MONTH,
2032 formatData.getShortMonths(), calb)) > 0) {
2033 return index;
2034 }
2035 } else {
2036 Map<String, Integer> map = getDisplayNamesMap(field, locale);
2037 if ((index = matchString(text, start, field, map, calb)) > 0) {
2038 return index;
2039 }
2040 }
2041 break parsing;
2042
2043 case PATTERN_MONTH_STANDALONE: // 'L'
2044 if (count <= 2) {
2045 // Don't want to parse the month if it is a string
2046 // while pattern uses numeric style: L or LL
2047 //[we computed 'value' above.]
2048 calb.set(Calendar.MONTH, value - 1);
2049 return pos.index;
2050 }
2051 Map<String, Integer> maps = getDisplayNamesMap(field, locale);
2052 if ((index = matchString(text, start, field, maps, calb)) > 0) {
2053 return index;
2054 }
2055 break parsing;
2056
2432 SimpleDateFormat that = (SimpleDateFormat) obj;
2433 return (pattern.equals(that.pattern)
2434 && formatData.equals(that.formatData));
2435 }
2436
2437 private static final int[] REST_OF_STYLES = {
2438 Calendar.SHORT_STANDALONE, Calendar.LONG_FORMAT, Calendar.LONG_STANDALONE,
2439 };
2440 private Map<String, Integer> getDisplayNamesMap(int field, Locale locale) {
2441 Map<String, Integer> map = calendar.getDisplayNames(field, Calendar.SHORT_FORMAT, locale);
2442 // Get all SHORT and LONG styles (avoid NARROW styles).
2443 for (int style : REST_OF_STYLES) {
2444 Map<String, Integer> m = calendar.getDisplayNames(field, style, locale);
2445 if (m != null) {
2446 map.putAll(m);
2447 }
2448 }
2449 return map;
2450 }
2451
2452 /**
2453 * After reading an object from the input stream, the format
2454 * pattern in the object is verified.
2455 *
2456 * @exception InvalidObjectException if the pattern is invalid
2457 */
2458 private void readObject(ObjectInputStream stream)
2459 throws IOException, ClassNotFoundException {
2460 stream.defaultReadObject();
2461
2462 try {
2463 compiledPattern = compile(pattern);
2464 } catch (Exception e) {
2465 throw new InvalidObjectException("invalid pattern");
2466 }
2467
2468 if (serialVersionOnStream < 1) {
2469 // didn't have defaultCenturyStart field
2470 initializeDefaultCentury();
2471 }
|
1 /*
2 * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
516 * The symbols used by this formatter for week names, month names,
517 * etc. May not be null.
518 * @serial
519 * @see java.text.DateFormatSymbols
520 */
521 private DateFormatSymbols formatData;
522
523 /**
524 * We map dates with two-digit years into the century starting at
525 * <code>defaultCenturyStart</code>, which may be any date. May
526 * not be null.
527 * @serial
528 * @since 1.1.4
529 */
530 private Date defaultCenturyStart;
531
532 private transient int defaultCenturyStartYear;
533
534 private static final int MILLIS_PER_MINUTE = 60 * 1000;
535
536 private static final int STANDALONE_MASK = 0x8000; // Calendar.STANDALONE_MASK
537
538 // For time zones that have no names, use strings GMT+minutes and
539 // GMT-minutes. For instance, in France the time zone is GMT+60.
540 private static final String GMT = "GMT";
541
542 /**
543 * Cache NumberFormat instances with Locale key.
544 */
545 private static final ConcurrentMap<Locale, NumberFormat> cachedNumberFormatData
546 = new ConcurrentHashMap<>(3);
547
548 /**
549 * The Locale used to instantiate this
550 * <code>SimpleDateFormat</code>. The value may be null if this object
551 * has been created by an older <code>SimpleDateFormat</code> and
552 * deserialized.
553 *
554 * @serial
555 * @since 1.6
556 */
557 private Locale locale;
1174 current = "";
1175 }
1176 break;
1177
1178 case PATTERN_WEEK_YEAR: // 'Y'
1179 case PATTERN_YEAR: // 'y'
1180 if (calendar instanceof GregorianCalendar) {
1181 if (count != 2) {
1182 zeroPaddingNumber(value, count, maxIntCount, buffer);
1183 } else {
1184 zeroPaddingNumber(value, 2, 2, buffer);
1185 } // clip 1996 to 96
1186 } else {
1187 if (current == null) {
1188 zeroPaddingNumber(value, style == Calendar.LONG ? 1 : count,
1189 maxIntCount, buffer);
1190 }
1191 }
1192 break;
1193
1194 case PATTERN_MONTH: // 'M' (context sensitive)
1195 if (useDateFormatSymbols) {
1196 String[] months;
1197 if (count >= 4) {
1198 months = formatData.getMonths();
1199 current = months[value];
1200 } else if (count == 3) {
1201 months = formatData.getShortMonths();
1202 current = months[value];
1203 }
1204 } else {
1205 if (count < 3) {
1206 current = null;
1207 } else if (forceStandaloneForm) {
1208 current = calendar.getDisplayName(field, style | STANDALONE_MASK, locale);
1209 if (current == null) {
1210 current = calendar.getDisplayName(field, style, locale);
1211 }
1212 }
1213 }
1214 if (current == null) {
1215 zeroPaddingNumber(value+1, count, maxIntCount, buffer);
1216 }
1217 break;
1218
1219 case PATTERN_MONTH_STANDALONE: // 'L'
1220 assert current == null;
1221 if (locale == null) {
1222 String[] months;
1223 if (count >= 4) {
1224 months = formatData.getMonths();
1225 current = months[value];
1226 } else if (count == 3) {
1227 months = formatData.getShortMonths();
1228 current = months[value];
1229 }
1230 } else {
1231 if (count >= 3) {
1232 current = calendar.getDisplayName(field, style | STANDALONE_MASK, locale);
1233 }
1234 }
1235 if (current == null) {
1236 zeroPaddingNumber(value+1, count, maxIntCount, buffer);
1237 }
1238 break;
1239
1240 case PATTERN_HOUR_OF_DAY1: // 'k' 1-based. eg, 23:59 + 1 hour =>> 24:59
1241 if (current == null) {
1242 if (value == 0) {
1243 zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1,
1244 count, maxIntCount, buffer);
1245 } else {
1246 zeroPaddingNumber(value, count, maxIntCount, buffer);
1247 }
1248 }
1249 break;
1250
1251 case PATTERN_DAY_OF_WEEK: // 'E'
1252 if (useDateFormatSymbols) {
2018 // [We computed 'value' above.]
2019 calb.set(Calendar.MONTH, value - 1);
2020 return pos.index;
2021 }
2022
2023 if (useDateFormatSymbols) {
2024 // count >= 3 // i.e., MMM or MMMM
2025 // Want to be able to parse both short and long forms.
2026 // Try count == 4 first:
2027 int newStart;
2028 if ((newStart = matchString(text, start, Calendar.MONTH,
2029 formatData.getMonths(), calb)) > 0) {
2030 return newStart;
2031 }
2032 // count == 4 failed, now try count == 3
2033 if ((index = matchString(text, start, Calendar.MONTH,
2034 formatData.getShortMonths(), calb)) > 0) {
2035 return index;
2036 }
2037 } else {
2038 Map<String, Integer> map = getDisplayContextNamesMap(field, locale);
2039 if ((index = matchString(text, start, field, map, calb)) > 0) {
2040 return index;
2041 }
2042 }
2043 break parsing;
2044
2045 case PATTERN_MONTH_STANDALONE: // 'L'
2046 if (count <= 2) {
2047 // Don't want to parse the month if it is a string
2048 // while pattern uses numeric style: L or LL
2049 //[we computed 'value' above.]
2050 calb.set(Calendar.MONTH, value - 1);
2051 return pos.index;
2052 }
2053 Map<String, Integer> maps = getDisplayNamesMap(field, locale);
2054 if ((index = matchString(text, start, field, maps, calb)) > 0) {
2055 return index;
2056 }
2057 break parsing;
2058
2434 SimpleDateFormat that = (SimpleDateFormat) obj;
2435 return (pattern.equals(that.pattern)
2436 && formatData.equals(that.formatData));
2437 }
2438
2439 private static final int[] REST_OF_STYLES = {
2440 Calendar.SHORT_STANDALONE, Calendar.LONG_FORMAT, Calendar.LONG_STANDALONE,
2441 };
2442 private Map<String, Integer> getDisplayNamesMap(int field, Locale locale) {
2443 Map<String, Integer> map = calendar.getDisplayNames(field, Calendar.SHORT_FORMAT, locale);
2444 // Get all SHORT and LONG styles (avoid NARROW styles).
2445 for (int style : REST_OF_STYLES) {
2446 Map<String, Integer> m = calendar.getDisplayNames(field, style, locale);
2447 if (m != null) {
2448 map.putAll(m);
2449 }
2450 }
2451 return map;
2452 }
2453
2454 // for 'M' pattern only
2455 private Map<String, Integer> getDisplayContextNamesMap(int field, Locale locale) {
2456 Map<String, Integer> map = calendar.getDisplayNames(field,
2457 Calendar.SHORT_FORMAT | (forceStandaloneForm ? STANDALONE_MASK : 0), locale);
2458 // Get LONG styles
2459 Map<String, Integer> m = calendar.getDisplayNames(field,
2460 Calendar.LONG_FORMAT | (forceStandaloneForm ? STANDALONE_MASK : 0), locale);
2461 if (m != null) {
2462 map.putAll(m);
2463 }
2464 return map;
2465 }
2466
2467 /**
2468 * After reading an object from the input stream, the format
2469 * pattern in the object is verified.
2470 *
2471 * @exception InvalidObjectException if the pattern is invalid
2472 */
2473 private void readObject(ObjectInputStream stream)
2474 throws IOException, ClassNotFoundException {
2475 stream.defaultReadObject();
2476
2477 try {
2478 compiledPattern = compile(pattern);
2479 } catch (Exception e) {
2480 throw new InvalidObjectException("invalid pattern");
2481 }
2482
2483 if (serialVersionOnStream < 1) {
2484 // didn't have defaultCenturyStart field
2485 initializeDefaultCentury();
2486 }
|