1 /*
2 * Copyright (c) 2012, 2016, 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
103 import java.util.AbstractMap.SimpleImmutableEntry;
104 import java.util.ArrayList;
105 import java.util.Arrays;
106 import java.util.Collections;
107 import java.util.Comparator;
108 import java.util.HashMap;
109 import java.util.HashSet;
110 import java.util.Iterator;
111 import java.util.LinkedHashMap;
112 import java.util.List;
113 import java.util.Locale;
114 import java.util.Map;
115 import java.util.Map.Entry;
116 import java.util.Objects;
117 import java.util.Set;
118 import java.util.TimeZone;
119 import java.util.concurrent.ConcurrentHashMap;
120 import java.util.concurrent.ConcurrentMap;
121
122 import sun.text.spi.JavaTimeDateTimePatternProvider;
123 import sun.util.locale.provider.LocaleProviderAdapter;
124 import sun.util.locale.provider.LocaleResources;
125 import sun.util.locale.provider.TimeZoneNameUtility;
126
127 /**
128 * Builder to create date-time formatters.
129 * <p>
130 * This allows a {@code DateTimeFormatter} to be created.
131 * All date-time formatters are created ultimately using this builder.
132 * <p>
133 * The basic elements of date-time can all be added:
134 * <ul>
135 * <li>Value - a numeric value</li>
136 * <li>Fraction - a fractional value including the decimal place. Always use this when
137 * outputting fractions to ensure that the fraction is parsed correctly</li>
138 * <li>Text - the textual equivalent for the value</li>
139 * <li>OffsetId/Offset - the {@linkplain ZoneOffset zone offset}</li>
140 * <li>ZoneId - the {@linkplain ZoneId time-zone} id</li>
141 * <li>ZoneText - the name of the time-zone</li>
142 * <li>ChronologyId - the {@linkplain Chronology chronology} id</li>
199 * The locale and chronology are used to lookup the locale specific format
200 * for the requested dateStyle and/or timeStyle.
201 *
202 * @param dateStyle the FormatStyle for the date, null for time-only pattern
203 * @param timeStyle the FormatStyle for the time, null for date-only pattern
204 * @param chrono the Chronology, non-null
205 * @param locale the locale, non-null
206 * @return the locale and Chronology specific formatting pattern
207 * @throws IllegalArgumentException if both dateStyle and timeStyle are null
208 */
209 public static String getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle,
210 Chronology chrono, Locale locale) {
211 Objects.requireNonNull(locale, "locale");
212 Objects.requireNonNull(chrono, "chrono");
213 if (dateStyle == null && timeStyle == null) {
214 throw new IllegalArgumentException("Either dateStyle or timeStyle must be non-null");
215 }
216 LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(JavaTimeDateTimePatternProvider.class, locale);
217 JavaTimeDateTimePatternProvider provider = adapter.getJavaTimeDateTimePatternProvider();
218 String pattern = provider.getJavaTimeDateTimePattern(convertStyle(timeStyle),
219 convertStyle(dateStyle), chrono.getCalendarType(), locale);
220 return pattern;
221 }
222
223 /**
224 * Converts the given FormatStyle to the java.text.DateFormat style.
225 *
226 * @param style the FormatStyle style
227 * @return the int style, or -1 if style is null, indicating un-required
228 */
229 private static int convertStyle(FormatStyle style) {
230 if (style == null) {
231 return -1;
232 }
233 return style.ordinal(); // indices happen to align
234 }
235
236 /**
237 * Constructs a new instance of the builder.
238 */
239 public DateTimeFormatterBuilder() {
2143 if (active.padNextWidth > 0) {
2144 if (pp != null) {
2145 pp = new PadPrinterParserDecorator(pp, active.padNextWidth, active.padNextChar);
2146 }
2147 active.padNextWidth = 0;
2148 active.padNextChar = 0;
2149 }
2150 active.printerParsers.add(pp);
2151 active.valueParserIndex = -1;
2152 return active.printerParsers.size() - 1;
2153 }
2154
2155 //-----------------------------------------------------------------------
2156 /**
2157 * Completes this builder by creating the {@code DateTimeFormatter}
2158 * using the default locale.
2159 * <p>
2160 * This will create a formatter with the {@linkplain Locale#getDefault(Locale.Category) default FORMAT locale}.
2161 * Numbers will be printed and parsed using the standard DecimalStyle.
2162 * The resolver style will be {@link ResolverStyle#SMART SMART}.
2163 * <p>
2164 * Calling this method will end any open optional sections by repeatedly
2165 * calling {@link #optionalEnd()} before creating the formatter.
2166 * <p>
2167 * This builder can still be used after creating the formatter if desired,
2168 * although the state may have been changed by calls to {@code optionalEnd}.
2169 *
2170 * @return the created formatter, not null
2171 */
2172 public DateTimeFormatter toFormatter() {
2173 return toFormatter(Locale.getDefault(Locale.Category.FORMAT));
2174 }
2175
2176 /**
2177 * Completes this builder by creating the {@code DateTimeFormatter}
2178 * using the specified locale.
2179 * <p>
2180 * This will create a formatter with the specified locale.
2181 * Numbers will be printed and parsed using the standard DecimalStyle.
2182 * The resolver style will be {@link ResolverStyle#SMART SMART}.
2183 * <p>
2184 * Calling this method will end any open optional sections by repeatedly
2185 * calling {@link #optionalEnd()} before creating the formatter.
2186 * <p>
2187 * This builder can still be used after creating the formatter if desired,
2188 * although the state may have been changed by calls to {@code optionalEnd}.
2189 *
2190 * @param locale the locale to use for formatting, not null
2191 * @return the created formatter, not null
2192 */
2193 public DateTimeFormatter toFormatter(Locale locale) {
2194 return toFormatter(locale, ResolverStyle.SMART, null);
2195 }
2196
2197 /**
2198 * Completes this builder by creating the formatter.
2199 * This uses the default locale.
2200 *
2201 * @param resolverStyle the resolver style to use, not null
2202 * @return the created formatter, not null
2203 */
2204 DateTimeFormatter toFormatter(ResolverStyle resolverStyle, Chronology chrono) {
2205 return toFormatter(Locale.getDefault(Locale.Category.FORMAT), resolverStyle, chrono);
2206 }
2207
2208 /**
2209 * Completes this builder by creating the formatter.
2210 *
2211 * @param locale the locale to use for formatting, not null
2212 * @param chrono the chronology to use, may be null
2213 * @return the created formatter, not null
2214 */
2215 private DateTimeFormatter toFormatter(Locale locale, ResolverStyle resolverStyle, Chronology chrono) {
2216 Objects.requireNonNull(locale, "locale");
2217 while (active.parent != null) {
2218 optionalEnd();
2219 }
2220 CompositePrinterParser pp = new CompositePrinterParser(printerParsers, false);
2221 return new DateTimeFormatter(pp, locale, DecimalStyle.STANDARD,
2222 resolverStyle, null, chrono, null);
2223 }
2224
2225 //-----------------------------------------------------------------------
2226 /**
2227 * Strategy for formatting/parsing date-time information.
2228 * <p>
2229 * The printer may format any part, or the whole, of the input date-time object.
2230 * Typically, a complete format is constructed from a number of smaller
2231 * units, each outputting a single field.
2232 * <p>
2233 * The parser may parse any piece of text from the input, storing the result
2234 * in the context. Typically, each individual parser will just parse one
2235 * field, such as the day-of-month, storing the value in the context.
2236 * Once the parse is complete, the caller will then resolve the parsed values
2237 * to create the desired object, such as a {@code LocalDate}.
2238 * <p>
2239 * The parse position will be updated during the parse. Parsing will start at
2240 * the specified index and the return value specifies the new parse position
2241 * for the next parser. If an error occurs, the returned index will be negative
2242 * and will have the error position encoded using the complement operator.
|
1 /*
2 * Copyright (c) 2012, 2017, 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
103 import java.util.AbstractMap.SimpleImmutableEntry;
104 import java.util.ArrayList;
105 import java.util.Arrays;
106 import java.util.Collections;
107 import java.util.Comparator;
108 import java.util.HashMap;
109 import java.util.HashSet;
110 import java.util.Iterator;
111 import java.util.LinkedHashMap;
112 import java.util.List;
113 import java.util.Locale;
114 import java.util.Map;
115 import java.util.Map.Entry;
116 import java.util.Objects;
117 import java.util.Set;
118 import java.util.TimeZone;
119 import java.util.concurrent.ConcurrentHashMap;
120 import java.util.concurrent.ConcurrentMap;
121
122 import sun.text.spi.JavaTimeDateTimePatternProvider;
123 import sun.util.locale.provider.CalendarDataUtility;
124 import sun.util.locale.provider.LocaleProviderAdapter;
125 import sun.util.locale.provider.LocaleResources;
126 import sun.util.locale.provider.TimeZoneNameUtility;
127
128 /**
129 * Builder to create date-time formatters.
130 * <p>
131 * This allows a {@code DateTimeFormatter} to be created.
132 * All date-time formatters are created ultimately using this builder.
133 * <p>
134 * The basic elements of date-time can all be added:
135 * <ul>
136 * <li>Value - a numeric value</li>
137 * <li>Fraction - a fractional value including the decimal place. Always use this when
138 * outputting fractions to ensure that the fraction is parsed correctly</li>
139 * <li>Text - the textual equivalent for the value</li>
140 * <li>OffsetId/Offset - the {@linkplain ZoneOffset zone offset}</li>
141 * <li>ZoneId - the {@linkplain ZoneId time-zone} id</li>
142 * <li>ZoneText - the name of the time-zone</li>
143 * <li>ChronologyId - the {@linkplain Chronology chronology} id</li>
200 * The locale and chronology are used to lookup the locale specific format
201 * for the requested dateStyle and/or timeStyle.
202 *
203 * @param dateStyle the FormatStyle for the date, null for time-only pattern
204 * @param timeStyle the FormatStyle for the time, null for date-only pattern
205 * @param chrono the Chronology, non-null
206 * @param locale the locale, non-null
207 * @return the locale and Chronology specific formatting pattern
208 * @throws IllegalArgumentException if both dateStyle and timeStyle are null
209 */
210 public static String getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle,
211 Chronology chrono, Locale locale) {
212 Objects.requireNonNull(locale, "locale");
213 Objects.requireNonNull(chrono, "chrono");
214 if (dateStyle == null && timeStyle == null) {
215 throw new IllegalArgumentException("Either dateStyle or timeStyle must be non-null");
216 }
217 LocaleProviderAdapter adapter = LocaleProviderAdapter.getAdapter(JavaTimeDateTimePatternProvider.class, locale);
218 JavaTimeDateTimePatternProvider provider = adapter.getJavaTimeDateTimePatternProvider();
219 String pattern = provider.getJavaTimeDateTimePattern(convertStyle(timeStyle),
220 convertStyle(dateStyle), chrono.getCalendarType(),
221 CalendarDataUtility.findRegionOverride(locale).orElse(locale));
222 return pattern;
223 }
224
225 /**
226 * Converts the given FormatStyle to the java.text.DateFormat style.
227 *
228 * @param style the FormatStyle style
229 * @return the int style, or -1 if style is null, indicating un-required
230 */
231 private static int convertStyle(FormatStyle style) {
232 if (style == null) {
233 return -1;
234 }
235 return style.ordinal(); // indices happen to align
236 }
237
238 /**
239 * Constructs a new instance of the builder.
240 */
241 public DateTimeFormatterBuilder() {
2145 if (active.padNextWidth > 0) {
2146 if (pp != null) {
2147 pp = new PadPrinterParserDecorator(pp, active.padNextWidth, active.padNextChar);
2148 }
2149 active.padNextWidth = 0;
2150 active.padNextChar = 0;
2151 }
2152 active.printerParsers.add(pp);
2153 active.valueParserIndex = -1;
2154 return active.printerParsers.size() - 1;
2155 }
2156
2157 //-----------------------------------------------------------------------
2158 /**
2159 * Completes this builder by creating the {@code DateTimeFormatter}
2160 * using the default locale.
2161 * <p>
2162 * This will create a formatter with the {@linkplain Locale#getDefault(Locale.Category) default FORMAT locale}.
2163 * Numbers will be printed and parsed using the standard DecimalStyle.
2164 * The resolver style will be {@link ResolverStyle#SMART SMART}.
2165 * If the default locale contains "ca" (calendar), "rg" (region override)
2166 * and/or "tz" (timezone)
2167 * <a href="../../util/Locale.html#def_locale_extension">Unicode extensions</a>,
2168 * the chronology and/or the zone are overriden. If both "ca" and "rg" are
2169 * specified, the chronology from "ca" extension supersedes the implicit one
2170 * from "rg" extension.
2171 * <p>
2172 * Calling this method will end any open optional sections by repeatedly
2173 * calling {@link #optionalEnd()} before creating the formatter.
2174 * <p>
2175 * This builder can still be used after creating the formatter if desired,
2176 * although the state may have been changed by calls to {@code optionalEnd}.
2177 *
2178 * @return the created formatter, not null
2179 */
2180 public DateTimeFormatter toFormatter() {
2181 return toFormatter(Locale.getDefault(Locale.Category.FORMAT));
2182 }
2183
2184 /**
2185 * Completes this builder by creating the {@code DateTimeFormatter}
2186 * using the specified locale.
2187 * <p>
2188 * This will create a formatter with the specified locale.
2189 * Numbers will be printed and parsed using the standard DecimalStyle.
2190 * The resolver style will be {@link ResolverStyle#SMART SMART}.
2191 * If the specified locale contains "ca" (calendar), "rg" (region override)
2192 * and/or "tz" (timezone)
2193 * <a href="../../util/Locale.html#def_locale_extension">Unicode extensions</a>,
2194 * the chronology and/or the zone are overriden. If both "ca" and "rg" are
2195 * specified, the chronology from "ca" extension supersedes the implicit one
2196 * from "rg" extension.
2197 * <p>
2198 * Calling this method will end any open optional sections by repeatedly
2199 * calling {@link #optionalEnd()} before creating the formatter.
2200 * <p>
2201 * This builder can still be used after creating the formatter if desired,
2202 * although the state may have been changed by calls to {@code optionalEnd}.
2203 *
2204 * @param locale the locale to use for formatting, not null
2205 * @return the created formatter, not null
2206 */
2207 public DateTimeFormatter toFormatter(Locale locale) {
2208 return toFormatter(locale, ResolverStyle.SMART, null);
2209 }
2210
2211 /**
2212 * Completes this builder by creating the formatter.
2213 * This uses the default locale.
2214 *
2215 * @param resolverStyle the resolver style to use, not null
2216 * @return the created formatter, not null
2217 */
2218 DateTimeFormatter toFormatter(ResolverStyle resolverStyle, Chronology chrono) {
2219 return toFormatter(Locale.getDefault(Locale.Category.FORMAT), resolverStyle, chrono);
2220 }
2221
2222 /**
2223 * Completes this builder by creating the formatter.
2224 *
2225 * @param locale the locale to use for formatting, not null
2226 * @param chrono the chronology to use, may be null
2227 * @return the created formatter, not null
2228 */
2229 private DateTimeFormatter toFormatter(Locale locale, ResolverStyle resolverStyle, Chronology chrono) {
2230 Objects.requireNonNull(locale, "locale");
2231 while (active.parent != null) {
2232 optionalEnd();
2233 }
2234 CompositePrinterParser pp = new CompositePrinterParser(printerParsers, false);
2235
2236 // Check for chronology/timezone in locale object
2237 Chronology c = locale.getUnicodeLocaleType("ca") != null ?
2238 Chronology.ofLocale(locale) : chrono;
2239 String tzType = locale.getUnicodeLocaleType("tz");
2240 ZoneId z = tzType != null ?
2241 TimeZoneNameUtility.convertLDMLShortID(tzType)
2242 .map(ZoneId::of)
2243 .orElse(null) :
2244 null;
2245 return new DateTimeFormatter(pp, locale, DecimalStyle.STANDARD,
2246 resolverStyle, null, c, z);
2247 }
2248
2249 //-----------------------------------------------------------------------
2250 /**
2251 * Strategy for formatting/parsing date-time information.
2252 * <p>
2253 * The printer may format any part, or the whole, of the input date-time object.
2254 * Typically, a complete format is constructed from a number of smaller
2255 * units, each outputting a single field.
2256 * <p>
2257 * The parser may parse any piece of text from the input, storing the result
2258 * in the context. Typically, each individual parser will just parse one
2259 * field, such as the day-of-month, storing the value in the context.
2260 * Once the parse is complete, the caller will then resolve the parsed values
2261 * to create the desired object, such as a {@code LocalDate}.
2262 * <p>
2263 * The parse position will be updated during the parse. Parsing will start at
2264 * the specified index and the return value specifies the new parse position
2265 * for the next parser. If an error occurs, the returned index will be negative
2266 * and will have the error position encoded using the complement operator.
|