Refactoring FastDateFormat per LANG-462 to use the FormatCache class created for an upcoming DateParser functionality. I've kept FormatCache package-private for now.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@1095299 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
5a7a09256e
commit
9ef322c33c
|
@ -23,16 +23,14 @@ import java.text.DateFormatSymbols;
|
|||
import java.text.FieldPosition;
|
||||
import java.text.Format;
|
||||
import java.text.ParsePosition;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
|
@ -99,15 +97,15 @@ public class FastDateFormat extends Format {
|
|||
*/
|
||||
public static final int SHORT = DateFormat.SHORT;
|
||||
|
||||
//@GuardedBy("this")
|
||||
private static String cDefaultPattern; // lazily initialised by getInstance()
|
||||
private static final FormatCache<FastDateFormat> cache= new FormatCache<FastDateFormat>() {
|
||||
@Override
|
||||
protected FastDateFormat createInstance(String pattern, TimeZone timeZone, Locale locale) {
|
||||
return new FastDateFormat(pattern, timeZone, locale);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Map<FastDateFormat, FastDateFormat> cInstanceCache =
|
||||
new HashMap<FastDateFormat, FastDateFormat>(7);
|
||||
private static final Map<Object, FastDateFormat> cDateInstanceCache = new HashMap<Object, FastDateFormat>(7);
|
||||
private static final Map<Object, FastDateFormat> cTimeInstanceCache = new HashMap<Object, FastDateFormat>(7);
|
||||
private static final Map<Object, FastDateFormat> cDateTimeInstanceCache = new HashMap<Object, FastDateFormat>(7);
|
||||
private static final Map<Object, String> cTimeZoneDisplayCache = new HashMap<Object, String>(7);
|
||||
private static ConcurrentMap<TimeZoneDisplayKey, String> cTimeZoneDisplayCache =
|
||||
new ConcurrentHashMap<TimeZoneDisplayKey, String>(7);
|
||||
|
||||
/**
|
||||
* The pattern.
|
||||
|
@ -117,18 +115,10 @@ public class FastDateFormat extends Format {
|
|||
* The time zone.
|
||||
*/
|
||||
private final TimeZone mTimeZone;
|
||||
/**
|
||||
* Whether the time zone overrides any on Calendars.
|
||||
*/
|
||||
private final boolean mTimeZoneForced;
|
||||
/**
|
||||
* The locale.
|
||||
*/
|
||||
private final Locale mLocale;
|
||||
/**
|
||||
* Whether the locale overrides the default.
|
||||
*/
|
||||
private final boolean mLocaleForced;
|
||||
/**
|
||||
* The parsed rules.
|
||||
*/
|
||||
|
@ -146,7 +136,7 @@ public class FastDateFormat extends Format {
|
|||
* @return a date/time formatter
|
||||
*/
|
||||
public static FastDateFormat getInstance() {
|
||||
return getInstance(getDefaultPattern(), null, null);
|
||||
return cache.getDateTimeInstance(SHORT, SHORT, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,7 +149,7 @@ public class FastDateFormat extends Format {
|
|||
* @throws IllegalArgumentException if pattern is invalid
|
||||
*/
|
||||
public static FastDateFormat getInstance(String pattern) {
|
||||
return getInstance(pattern, null, null);
|
||||
return cache.getInstance(pattern, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,7 +164,7 @@ public class FastDateFormat extends Format {
|
|||
* @throws IllegalArgumentException if pattern is invalid
|
||||
*/
|
||||
public static FastDateFormat getInstance(String pattern, TimeZone timeZone) {
|
||||
return getInstance(pattern, timeZone, null);
|
||||
return cache.getInstance(pattern, timeZone, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -188,7 +178,7 @@ public class FastDateFormat extends Format {
|
|||
* @throws IllegalArgumentException if pattern is invalid
|
||||
*/
|
||||
public static FastDateFormat getInstance(String pattern, Locale locale) {
|
||||
return getInstance(pattern, null, locale);
|
||||
return cache.getInstance(pattern, null, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -204,15 +194,8 @@ public class FastDateFormat extends Format {
|
|||
* @throws IllegalArgumentException if pattern is invalid
|
||||
* or <code>null</code>
|
||||
*/
|
||||
public static synchronized FastDateFormat getInstance(String pattern, TimeZone timeZone, Locale locale) {
|
||||
FastDateFormat emptyFormat = new FastDateFormat(pattern, timeZone, locale);
|
||||
FastDateFormat format = cInstanceCache.get(emptyFormat);
|
||||
if (format == null) {
|
||||
format = emptyFormat;
|
||||
format.init(); // convert shell format into usable one
|
||||
cInstanceCache.put(format, format); // this is OK!
|
||||
}
|
||||
return format;
|
||||
public static FastDateFormat getInstance(String pattern, TimeZone timeZone, Locale locale) {
|
||||
return cache.getInstance(pattern, timeZone, locale);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
@ -227,7 +210,7 @@ public class FastDateFormat extends Format {
|
|||
* @since 2.1
|
||||
*/
|
||||
public static FastDateFormat getDateInstance(int style) {
|
||||
return getDateInstance(style, null, null);
|
||||
return cache.getDateTimeInstance(style, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -242,7 +225,7 @@ public class FastDateFormat extends Format {
|
|||
* @since 2.1
|
||||
*/
|
||||
public static FastDateFormat getDateInstance(int style, Locale locale) {
|
||||
return getDateInstance(style, null, locale);
|
||||
return cache.getDateTimeInstance(style, null, null, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -258,8 +241,9 @@ public class FastDateFormat extends Format {
|
|||
* @since 2.1
|
||||
*/
|
||||
public static FastDateFormat getDateInstance(int style, TimeZone timeZone) {
|
||||
return getDateInstance(style, timeZone, null);
|
||||
return cache.getDateTimeInstance(style, null, timeZone, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets a date formatter instance using the specified style, time
|
||||
* zone and locale.</p>
|
||||
|
@ -272,31 +256,8 @@ public class FastDateFormat extends Format {
|
|||
* @throws IllegalArgumentException if the Locale has no date
|
||||
* pattern defined
|
||||
*/
|
||||
public static synchronized FastDateFormat getDateInstance(int style, TimeZone timeZone, Locale locale) {
|
||||
Object key = Integer.valueOf(style);
|
||||
if (timeZone != null) {
|
||||
key = new Pair(key, timeZone);
|
||||
}
|
||||
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
|
||||
key = new Pair(key, locale);
|
||||
|
||||
FastDateFormat format = cDateInstanceCache.get(key);
|
||||
if (format == null) {
|
||||
try {
|
||||
SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getDateInstance(style, locale);
|
||||
String pattern = formatter.toPattern();
|
||||
format = getInstance(pattern, timeZone, locale);
|
||||
cDateInstanceCache.put(key, format);
|
||||
|
||||
} catch (ClassCastException ex) {
|
||||
throw new IllegalArgumentException("No date pattern for locale: " + locale);
|
||||
}
|
||||
}
|
||||
return format;
|
||||
public static FastDateFormat getDateInstance(int style, TimeZone timeZone, Locale locale) {
|
||||
return cache.getDateTimeInstance(style, null, timeZone, locale);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
@ -311,7 +272,7 @@ public class FastDateFormat extends Format {
|
|||
* @since 2.1
|
||||
*/
|
||||
public static FastDateFormat getTimeInstance(int style) {
|
||||
return getTimeInstance(style, null, null);
|
||||
return cache.getDateTimeInstance(null, style, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -326,7 +287,7 @@ public class FastDateFormat extends Format {
|
|||
* @since 2.1
|
||||
*/
|
||||
public static FastDateFormat getTimeInstance(int style, Locale locale) {
|
||||
return getTimeInstance(style, null, locale);
|
||||
return cache.getDateTimeInstance(null, style, null, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -342,7 +303,7 @@ public class FastDateFormat extends Format {
|
|||
* @since 2.1
|
||||
*/
|
||||
public static FastDateFormat getTimeInstance(int style, TimeZone timeZone) {
|
||||
return getTimeInstance(style, timeZone, null);
|
||||
return cache.getDateTimeInstance(null, style, timeZone, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -357,32 +318,8 @@ public class FastDateFormat extends Format {
|
|||
* @throws IllegalArgumentException if the Locale has no time
|
||||
* pattern defined
|
||||
*/
|
||||
public static synchronized FastDateFormat getTimeInstance(int style, TimeZone timeZone, Locale locale) {
|
||||
Object key = Integer.valueOf(style);
|
||||
if (timeZone != null) {
|
||||
key = new Pair(key, timeZone);
|
||||
}
|
||||
if (locale != null) {
|
||||
key = new Pair(key, locale);
|
||||
}
|
||||
|
||||
FastDateFormat format = cTimeInstanceCache.get(key);
|
||||
if (format == null) {
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
|
||||
try {
|
||||
SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getTimeInstance(style, locale);
|
||||
String pattern = formatter.toPattern();
|
||||
format = getInstance(pattern, timeZone, locale);
|
||||
cTimeInstanceCache.put(key, format);
|
||||
|
||||
} catch (ClassCastException ex) {
|
||||
throw new IllegalArgumentException("No date pattern for locale: " + locale);
|
||||
}
|
||||
}
|
||||
return format;
|
||||
public static FastDateFormat getTimeInstance(int style, TimeZone timeZone, Locale locale) {
|
||||
return cache.getDateTimeInstance(null, style, timeZone, locale);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
@ -397,9 +334,8 @@ public class FastDateFormat extends Format {
|
|||
* pattern defined
|
||||
* @since 2.1
|
||||
*/
|
||||
public static FastDateFormat getDateTimeInstance(
|
||||
int dateStyle, int timeStyle) {
|
||||
return getDateTimeInstance(dateStyle, timeStyle, null, null);
|
||||
public static FastDateFormat getDateTimeInstance(int dateStyle, int timeStyle) {
|
||||
return cache.getDateTimeInstance(dateStyle, timeStyle, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -414,9 +350,8 @@ public class FastDateFormat extends Format {
|
|||
* pattern defined
|
||||
* @since 2.1
|
||||
*/
|
||||
public static FastDateFormat getDateTimeInstance(
|
||||
int dateStyle, int timeStyle, Locale locale) {
|
||||
return getDateTimeInstance(dateStyle, timeStyle, null, locale);
|
||||
public static FastDateFormat getDateTimeInstance(int dateStyle, int timeStyle, Locale locale) {
|
||||
return cache.getDateTimeInstance(dateStyle, timeStyle, null, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -432,8 +367,7 @@ public class FastDateFormat extends Format {
|
|||
* pattern defined
|
||||
* @since 2.1
|
||||
*/
|
||||
public static FastDateFormat getDateTimeInstance(
|
||||
int dateStyle, int timeStyle, TimeZone timeZone) {
|
||||
public static FastDateFormat getDateTimeInstance(int dateStyle, int timeStyle, TimeZone timeZone) {
|
||||
return getDateTimeInstance(dateStyle, timeStyle, timeZone, null);
|
||||
}
|
||||
/**
|
||||
|
@ -449,32 +383,9 @@ public class FastDateFormat extends Format {
|
|||
* @throws IllegalArgumentException if the Locale has no date/time
|
||||
* pattern defined
|
||||
*/
|
||||
public static synchronized FastDateFormat getDateTimeInstance(int dateStyle, int timeStyle, TimeZone timeZone,
|
||||
Locale locale) {
|
||||
|
||||
Object key = new Pair(Integer.valueOf(dateStyle), Integer.valueOf(timeStyle));
|
||||
if (timeZone != null) {
|
||||
key = new Pair(key, timeZone);
|
||||
}
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
key = new Pair(key, locale);
|
||||
|
||||
FastDateFormat format = cDateTimeInstanceCache.get(key);
|
||||
if (format == null) {
|
||||
try {
|
||||
SimpleDateFormat formatter = (SimpleDateFormat) DateFormat.getDateTimeInstance(dateStyle, timeStyle,
|
||||
locale);
|
||||
String pattern = formatter.toPattern();
|
||||
format = getInstance(pattern, timeZone, locale);
|
||||
cDateTimeInstanceCache.put(key, format);
|
||||
|
||||
} catch (ClassCastException ex) {
|
||||
throw new IllegalArgumentException("No date time pattern for locale: " + locale);
|
||||
}
|
||||
}
|
||||
return format;
|
||||
public static FastDateFormat getDateTimeInstance(
|
||||
int dateStyle, int timeStyle, TimeZone timeZone, Locale locale) {
|
||||
return cache.getDateTimeInstance(dateStyle, timeStyle, timeZone, locale);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
@ -488,68 +399,42 @@ public class FastDateFormat extends Format {
|
|||
* @param locale the locale to use
|
||||
* @return the textual name of the time zone
|
||||
*/
|
||||
static synchronized String getTimeZoneDisplay(TimeZone tz, boolean daylight, int style, Locale locale) {
|
||||
Object key = new TimeZoneDisplayKey(tz, daylight, style, locale);
|
||||
static String getTimeZoneDisplay(TimeZone tz, boolean daylight, int style, Locale locale) {
|
||||
TimeZoneDisplayKey key = new TimeZoneDisplayKey(tz, daylight, style, locale);
|
||||
String value = cTimeZoneDisplayCache.get(key);
|
||||
if (value == null) {
|
||||
// This is a very slow call, so cache the results.
|
||||
value = tz.getDisplayName(daylight, style, locale);
|
||||
cTimeZoneDisplayCache.put(key, value);
|
||||
String prior = cTimeZoneDisplayCache.putIfAbsent(key, value);
|
||||
if (prior != null) {
|
||||
value= prior;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the default pattern.</p>
|
||||
*
|
||||
* @return the default pattern
|
||||
*/
|
||||
private static synchronized String getDefaultPattern() {
|
||||
if (cDefaultPattern == null) {
|
||||
cDefaultPattern = new SimpleDateFormat().toPattern();
|
||||
}
|
||||
return cDefaultPattern;
|
||||
}
|
||||
|
||||
// Constructor
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Constructs a new FastDateFormat.</p>
|
||||
*
|
||||
* @param pattern {@link java.text.SimpleDateFormat} compatible
|
||||
* pattern
|
||||
* @param timeZone time zone to use, <code>null</code> means use
|
||||
* default for <code>Date</code> and value within for
|
||||
* <code>Calendar</code>
|
||||
* @param locale locale, <code>null</code> means use system
|
||||
* default
|
||||
* @throws IllegalArgumentException if pattern is invalid or
|
||||
* <code>null</code>
|
||||
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern
|
||||
* @param timeZone non-null time zone to use
|
||||
* @param locale non-null locale to use
|
||||
* @throws NullPointerException if pattern, timeZone, or locale is null.
|
||||
*/
|
||||
protected FastDateFormat(String pattern, TimeZone timeZone, Locale locale) {
|
||||
super();
|
||||
if (pattern == null) {
|
||||
throw new IllegalArgumentException("The pattern must not be null");
|
||||
}
|
||||
mPattern = pattern;
|
||||
|
||||
mTimeZoneForced = (timeZone != null);
|
||||
if (timeZone == null) {
|
||||
timeZone = TimeZone.getDefault();
|
||||
}
|
||||
mTimeZone = timeZone;
|
||||
|
||||
mLocaleForced = (locale != null);
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
mLocale = locale;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Initializes the instance for first use.</p>
|
||||
*/
|
||||
protected void init() {
|
||||
private void init() {
|
||||
List<Rule> rulesList = parsePattern();
|
||||
mRules = rulesList.toArray(new Rule[rulesList.size()]);
|
||||
|
||||
|
@ -662,9 +547,9 @@ public class FastDateFormat extends Format {
|
|||
break;
|
||||
case 'z': // time zone (text)
|
||||
if (tokenLen >= 4) {
|
||||
rule = new TimeZoneNameRule(mTimeZone, mTimeZoneForced, mLocale, TimeZone.LONG);
|
||||
rule = new TimeZoneNameRule(mTimeZone, mLocale, TimeZone.LONG);
|
||||
} else {
|
||||
rule = new TimeZoneNameRule(mTimeZone, mTimeZoneForced, mLocale, TimeZone.SHORT);
|
||||
rule = new TimeZoneNameRule(mTimeZone, mLocale, TimeZone.SHORT);
|
||||
}
|
||||
break;
|
||||
case 'Z': // time zone (value)
|
||||
|
@ -812,7 +697,7 @@ public class FastDateFormat extends Format {
|
|||
* @return the formatted string
|
||||
*/
|
||||
public String format(Date date) {
|
||||
Calendar c = new GregorianCalendar(mTimeZone, mLocale);
|
||||
Calendar c = Calendar.getInstance(mTimeZone, mLocale);
|
||||
c.setTime(date);
|
||||
return applyRules(c, new StringBuffer(mMaxLengthEstimate)).toString();
|
||||
}
|
||||
|
@ -849,7 +734,7 @@ public class FastDateFormat extends Format {
|
|||
* @return the specified string buffer
|
||||
*/
|
||||
public StringBuffer format(Date date, StringBuffer buf) {
|
||||
Calendar c = new GregorianCalendar(mTimeZone);
|
||||
Calendar c = Calendar.getInstance(mTimeZone, mLocale);
|
||||
c.setTime(date);
|
||||
return applyRules(c, buf);
|
||||
}
|
||||
|
@ -863,11 +748,6 @@ public class FastDateFormat extends Format {
|
|||
* @return the specified string buffer
|
||||
*/
|
||||
public StringBuffer format(Calendar calendar, StringBuffer buf) {
|
||||
if (mTimeZoneForced) {
|
||||
calendar.getTimeInMillis(); /// LANG-538
|
||||
calendar = (Calendar) calendar.clone();
|
||||
calendar.setTimeZone(mTimeZone);
|
||||
}
|
||||
return applyRules(calendar, buf);
|
||||
}
|
||||
|
||||
|
@ -880,10 +760,8 @@ public class FastDateFormat extends Format {
|
|||
* @return the specified string buffer
|
||||
*/
|
||||
protected StringBuffer applyRules(Calendar calendar, StringBuffer buf) {
|
||||
Rule[] rules = mRules;
|
||||
int len = mRules.length;
|
||||
for (int i = 0; i < len; i++) {
|
||||
rules[i].appendTo(buf, calendar);
|
||||
for (Rule rule : mRules) {
|
||||
rule.appendTo(buf, calendar);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
@ -918,10 +796,7 @@ public class FastDateFormat extends Format {
|
|||
/**
|
||||
* <p>Gets the time zone used by this formatter.</p>
|
||||
*
|
||||
* <p>This zone is always used for <code>Date</code> formatting.
|
||||
* If a <code>Calendar</code> is passed in to be formatted, the
|
||||
* time zone on that may be used depending on
|
||||
* {@link #getTimeZoneOverridesCalendar()}.</p>
|
||||
* <p>This zone is always used for <code>Date</code> formatting. </p>
|
||||
*
|
||||
* @return the time zone
|
||||
*/
|
||||
|
@ -929,17 +804,6 @@ public class FastDateFormat extends Format {
|
|||
return mTimeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns <code>true</code> if the time zone of the
|
||||
* calendar overrides the time zone of the formatter.</p>
|
||||
*
|
||||
* @return <code>true</code> if time zone of formatter
|
||||
* overridden for calendars
|
||||
*/
|
||||
public boolean getTimeZoneOverridesCalendar() {
|
||||
return mTimeZoneForced;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets the locale used by this formatter.</p>
|
||||
*
|
||||
|
@ -976,16 +840,9 @@ public class FastDateFormat extends Format {
|
|||
return false;
|
||||
}
|
||||
FastDateFormat other = (FastDateFormat) obj;
|
||||
if (
|
||||
(mPattern == other.mPattern || mPattern.equals(other.mPattern)) &&
|
||||
(mTimeZone == other.mTimeZone || mTimeZone.equals(other.mTimeZone)) &&
|
||||
(mLocale == other.mLocale || mLocale.equals(other.mLocale)) &&
|
||||
(mTimeZoneForced == other.mTimeZoneForced) &&
|
||||
(mLocaleForced == other.mLocaleForced)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return mPattern.equals(other.mPattern)
|
||||
&& mTimeZone.equals(other.mTimeZone)
|
||||
&& mLocale.equals(other.mLocale);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -995,13 +852,7 @@ public class FastDateFormat extends Format {
|
|||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int total = 0;
|
||||
total += mPattern.hashCode();
|
||||
total += mTimeZone.hashCode();
|
||||
total += (mTimeZoneForced ? 1 : 0);
|
||||
total += mLocale.hashCode();
|
||||
total += (mLocaleForced ? 1 : 0);
|
||||
return total;
|
||||
return mPattern.hashCode() + 13 * (mTimeZone.hashCode() + 13 * mLocale.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1517,9 +1368,6 @@ public class FastDateFormat extends Format {
|
|||
*/
|
||||
private static class TimeZoneNameRule implements Rule {
|
||||
private final TimeZone mTimeZone;
|
||||
private final boolean mTimeZoneForced;
|
||||
private final Locale mLocale;
|
||||
private final int mStyle;
|
||||
private final String mStandard;
|
||||
private final String mDaylight;
|
||||
|
||||
|
@ -1527,55 +1375,31 @@ public class FastDateFormat extends Format {
|
|||
* Constructs an instance of <code>TimeZoneNameRule</code> with the specified properties.
|
||||
*
|
||||
* @param timeZone the time zone
|
||||
* @param timeZoneForced if <code>true</code> the time zone is forced into standard and daylight
|
||||
* @param locale the locale
|
||||
* @param style the style
|
||||
*/
|
||||
TimeZoneNameRule(TimeZone timeZone, boolean timeZoneForced, Locale locale, int style) {
|
||||
TimeZoneNameRule(TimeZone timeZone, Locale locale, int style) {
|
||||
mTimeZone = timeZone;
|
||||
mTimeZoneForced = timeZoneForced;
|
||||
mLocale = locale;
|
||||
mStyle = style;
|
||||
|
||||
if (timeZoneForced) {
|
||||
mStandard = getTimeZoneDisplay(timeZone, false, style, locale);
|
||||
mDaylight = getTimeZoneDisplay(timeZone, true, style, locale);
|
||||
} else {
|
||||
mStandard = null;
|
||||
mDaylight = null;
|
||||
}
|
||||
mStandard = getTimeZoneDisplay(timeZone, false, style, locale);
|
||||
mDaylight = getTimeZoneDisplay(timeZone, true, style, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public int estimateLength() {
|
||||
if (mTimeZoneForced) {
|
||||
return Math.max(mStandard.length(), mDaylight.length());
|
||||
} else if (mStyle == TimeZone.SHORT) {
|
||||
return 4;
|
||||
} else {
|
||||
return 40;
|
||||
}
|
||||
return Math.max(mStandard.length(), mDaylight.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void appendTo(StringBuffer buffer, Calendar calendar) {
|
||||
if (mTimeZoneForced) {
|
||||
if (mTimeZone.useDaylightTime() && calendar.get(Calendar.DST_OFFSET) != 0) {
|
||||
buffer.append(mDaylight);
|
||||
} else {
|
||||
buffer.append(mStandard);
|
||||
}
|
||||
if (mTimeZone.useDaylightTime() && calendar.get(Calendar.DST_OFFSET) != 0) {
|
||||
buffer.append(mDaylight);
|
||||
} else {
|
||||
TimeZone timeZone = calendar.getTimeZone();
|
||||
if (timeZone.useDaylightTime() && calendar.get(Calendar.DST_OFFSET) != 0) {
|
||||
buffer.append(getTimeZoneDisplay(timeZone, true, mStyle, mLocale));
|
||||
} else {
|
||||
buffer.append(getTimeZoneDisplay(timeZone, false, mStyle, mLocale));
|
||||
}
|
||||
buffer.append(mStandard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1665,7 +1489,7 @@ public class FastDateFormat extends Format {
|
|||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mStyle * 31 + mLocale.hashCode();
|
||||
return (mStyle * 31 + mLocale.hashCode() ) * 31 + mTimeZone.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1686,67 +1510,4 @@ public class FastDateFormat extends Format {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Helper class for creating compound objects.</p>
|
||||
*
|
||||
* <p>One use for this class is to create a hashtable key
|
||||
* out of multiple objects.</p>
|
||||
*/
|
||||
private static class Pair {
|
||||
private final Object mObj1;
|
||||
private final Object mObj2;
|
||||
|
||||
/**
|
||||
* Constructs an instance of <code>Pair</code> to hold the specified objects.
|
||||
* @param obj1 one object in the pair
|
||||
* @param obj2 second object in the pair
|
||||
*/
|
||||
public Pair(Object obj1, Object obj2) {
|
||||
mObj1 = obj1;
|
||||
mObj2 = obj2;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(obj instanceof Pair)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Pair key = (Pair)obj;
|
||||
|
||||
return
|
||||
(mObj1 == null ?
|
||||
key.mObj1 == null : mObj1.equals(key.mObj1)) &&
|
||||
(mObj2 == null ?
|
||||
key.mObj2 == null : mObj2.equals(key.mObj2));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return
|
||||
(mObj1 == null ? 0 : mObj1.hashCode()) +
|
||||
(mObj2 == null ? 0 : mObj2.hashCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + mObj1 + ':' + mObj2 + ']';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.commons.lang3.time;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.Format;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* <p>FormatCache is a cache and factory for {@link Format}s.</p>
|
||||
*
|
||||
* @since 3.0
|
||||
* @version $Id: FormatCache 892161 2009-12-18 07:21:10Z $
|
||||
*/
|
||||
abstract class FormatCache<F extends Format> {
|
||||
/**
|
||||
* No date or no time. Used in same parameters as DateFormat.SHORT or DateFormat.LONG
|
||||
*/
|
||||
static final int NONE= -1;
|
||||
|
||||
private final ConcurrentMap<MultipartKey, F> cInstanceCache
|
||||
= new ConcurrentHashMap<MultipartKey, F>(7);
|
||||
|
||||
private final ConcurrentMap<MultipartKey, String> cDateTimeInstanceCache
|
||||
= new ConcurrentHashMap<MultipartKey, String>(7);
|
||||
|
||||
/**
|
||||
* <p>Gets a formatter instance using the default pattern in the
|
||||
* default timezone and locale.</p>
|
||||
*
|
||||
* @return a date/time formatter
|
||||
*/
|
||||
public F getInstance() {
|
||||
return getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, TimeZone.getDefault(), Locale.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Gets a formatter instance using the specified pattern, time zone
|
||||
* and locale.</p>
|
||||
*
|
||||
* @param pattern {@link java.text.SimpleDateFormat} compatible
|
||||
* pattern
|
||||
* @param timeZone the non-null time zone
|
||||
* @param locale the non-null locale
|
||||
* @return a pattern based date/time formatter
|
||||
* @throws IllegalArgumentException if pattern is invalid
|
||||
* or <code>null</code>
|
||||
*/
|
||||
public F getInstance(String pattern, TimeZone timeZone, Locale locale) {
|
||||
if (pattern == null) {
|
||||
throw new NullPointerException("pattern must not be null");
|
||||
}
|
||||
if (timeZone == null) {
|
||||
timeZone = TimeZone.getDefault();
|
||||
}
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
MultipartKey key = new MultipartKey(pattern, timeZone, locale);
|
||||
F format = cInstanceCache.get(key);
|
||||
if (format == null) {
|
||||
format = createInstance(pattern, timeZone, locale);
|
||||
F previousValue= cInstanceCache.putIfAbsent(key, format);
|
||||
if (previousValue != null) {
|
||||
// another thread snuck in and did the same work
|
||||
// we should return the instance that is in ConcurrentMap
|
||||
format= previousValue;
|
||||
}
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Create a format instance using the specified pattern, time zone
|
||||
* and locale.</p>
|
||||
*
|
||||
* @param pattern {@link java.text.SimpleDateFormat} compatible pattern, this will not be null.
|
||||
* @param timeZone time zone, this will not be null.
|
||||
* @param locale locale, this will not be null.
|
||||
* @return a pattern based date/time formatter
|
||||
* @throws IllegalArgumentException if pattern is invalid
|
||||
* or <code>null</code>
|
||||
*/
|
||||
abstract protected F createInstance(String pattern, TimeZone timeZone, Locale locale);
|
||||
|
||||
/**
|
||||
* <p>Gets a date/time formatter instance using the specified style,
|
||||
* time zone and locale.</p>
|
||||
*
|
||||
* @param dateStyle date style: FULL, LONG, MEDIUM, or SHORT
|
||||
* @param timeStyle time style: FULL, LONG, MEDIUM, or SHORT
|
||||
* @param timeZone optional time zone, overrides time zone of
|
||||
* formatted date
|
||||
* @param locale optional locale, overrides system locale
|
||||
* @return a localized standard date/time formatter
|
||||
* @throws IllegalArgumentException if the Locale has no date/time
|
||||
* pattern defined
|
||||
*/
|
||||
public F getDateTimeInstance(Integer dateStyle, Integer timeStyle, TimeZone timeZone, Locale locale) {
|
||||
if (locale == null) {
|
||||
locale = Locale.getDefault();
|
||||
}
|
||||
MultipartKey key = new MultipartKey(dateStyle, timeStyle, locale);
|
||||
|
||||
String pattern = cDateTimeInstanceCache.get(key);
|
||||
if (pattern == null) {
|
||||
try {
|
||||
DateFormat formatter;
|
||||
if (dateStyle == null) {
|
||||
formatter = DateFormat.getTimeInstance(timeStyle, locale);
|
||||
}
|
||||
else if (timeStyle == null) {
|
||||
formatter = DateFormat.getDateInstance(dateStyle, locale);
|
||||
}
|
||||
else {
|
||||
formatter = DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
|
||||
}
|
||||
pattern = ((SimpleDateFormat)formatter).toPattern();
|
||||
String previous = cDateTimeInstanceCache.putIfAbsent(key, pattern);
|
||||
if (previous != null) {
|
||||
// even though it doesn't matter if another thread put the pattern
|
||||
// it's still good practice to return the String instance that is
|
||||
// actually in the ConcurrentMap
|
||||
pattern= previous;
|
||||
}
|
||||
} catch (ClassCastException ex) {
|
||||
throw new IllegalArgumentException("No date time pattern for locale: " + locale);
|
||||
}
|
||||
}
|
||||
|
||||
return getInstance(pattern, timeZone, locale);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
/**
|
||||
* <p>Helper class to hold multi-part Map keys</p>
|
||||
*/
|
||||
private static class MultipartKey {
|
||||
private final Object[] keys;
|
||||
private int hashCode;
|
||||
|
||||
/**
|
||||
* Constructs an instance of <code>MultipartKey</code> to hold the specified objects.
|
||||
* @param keys the set of objects that make up the key. Each key may be null.
|
||||
*/
|
||||
public MultipartKey(Object... keys) {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if ( obj instanceof MultipartKey == false ) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.equals(keys, ((MultipartKey)obj).keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if(hashCode==0) {
|
||||
int rc= 0;
|
||||
for(Object key : keys) {
|
||||
if(key!=null) {
|
||||
rc= rc*7 + key.hashCode();
|
||||
}
|
||||
}
|
||||
hashCode= rc;
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -56,8 +56,6 @@ public class FastDateFormatTest extends TestCase {
|
|||
assertEquals("MM/DD/yyyy", format1.getPattern());
|
||||
assertEquals(TimeZone.getDefault(), format1.getTimeZone());
|
||||
assertEquals(TimeZone.getDefault(), format2.getTimeZone());
|
||||
assertEquals(false, format1.getTimeZoneOverridesCalendar());
|
||||
assertEquals(false, format2.getTimeZoneOverridesCalendar());
|
||||
}
|
||||
|
||||
public void test_getInstance_String_TimeZone() {
|
||||
|
@ -77,9 +75,7 @@ public class FastDateFormatTest extends TestCase {
|
|||
|
||||
assertTrue(format1 != format2); // -- junit 3.8 version -- assertFalse(format1 == format2);
|
||||
assertEquals(TimeZone.getTimeZone("Atlantic/Reykjavik"), format1.getTimeZone());
|
||||
assertEquals(true, format1.getTimeZoneOverridesCalendar());
|
||||
assertEquals(TimeZone.getDefault(), format2.getTimeZone());
|
||||
assertEquals(false, format2.getTimeZoneOverridesCalendar());
|
||||
assertSame(format3, format4);
|
||||
assertTrue(format3 != format5); // -- junit 3.8 version -- assertFalse(format3 == format5);
|
||||
assertTrue(format4 != format6); // -- junit 3.8 version -- assertFalse(format3 == format5);
|
||||
|
@ -164,9 +160,6 @@ public class FastDateFormatTest extends TestCase {
|
|||
assertEquals(TimeZone.getTimeZone("Atlantic/Reykjavik"), format1.getTimeZone());
|
||||
assertEquals(TimeZone.getDefault(), format2.getTimeZone());
|
||||
assertEquals(TimeZone.getDefault(), format3.getTimeZone());
|
||||
assertEquals(true, format1.getTimeZoneOverridesCalendar());
|
||||
assertEquals(false, format2.getTimeZoneOverridesCalendar());
|
||||
assertEquals(true, format3.getTimeZoneOverridesCalendar());
|
||||
assertEquals(Locale.GERMANY, format1.getLocale());
|
||||
assertEquals(Locale.GERMANY, format2.getLocale());
|
||||
assertEquals(Locale.GERMANY, format3.getLocale());
|
||||
|
@ -183,8 +176,6 @@ public class FastDateFormatTest extends TestCase {
|
|||
try {
|
||||
Locale.setDefault(Locale.US);
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("America/New_York"));
|
||||
FastDateFormat fdf = null;
|
||||
SimpleDateFormat sdf = null;
|
||||
|
||||
GregorianCalendar cal1 = new GregorianCalendar(2003, 0, 10, 15, 33, 20);
|
||||
GregorianCalendar cal2 = new GregorianCalendar(2003, 6, 10, 9, 00, 00);
|
||||
|
@ -193,8 +184,8 @@ public class FastDateFormatTest extends TestCase {
|
|||
long millis1 = date1.getTime();
|
||||
long millis2 = date2.getTime();
|
||||
|
||||
fdf = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
|
||||
sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||
assertEquals(sdf.format(date1), fdf.format(date1));
|
||||
assertEquals("2003-01-10T15:33:20", fdf.format(date1));
|
||||
assertEquals("2003-01-10T15:33:20", fdf.format(cal1));
|
||||
|
@ -208,7 +199,6 @@ public class FastDateFormatTest extends TestCase {
|
|||
assertEquals("-0500", fdf.format(cal1));
|
||||
assertEquals("-0500", fdf.format(millis1));
|
||||
|
||||
fdf = FastDateFormat.getInstance("Z");
|
||||
assertEquals("-0400", fdf.format(date2));
|
||||
assertEquals("-0400", fdf.format(cal2));
|
||||
assertEquals("-0400", fdf.format(millis2));
|
||||
|
@ -218,7 +208,6 @@ public class FastDateFormatTest extends TestCase {
|
|||
assertEquals("-05:00", fdf.format(cal1));
|
||||
assertEquals("-05:00", fdf.format(millis1));
|
||||
|
||||
fdf = FastDateFormat.getInstance("ZZ");
|
||||
assertEquals("-04:00", fdf.format(date2));
|
||||
assertEquals("-04:00", fdf.format(cal2));
|
||||
assertEquals("-04:00", fdf.format(millis2));
|
||||
|
@ -228,14 +217,13 @@ public class FastDateFormatTest extends TestCase {
|
|||
fdf = FastDateFormat.getInstance(pattern);
|
||||
sdf = new SimpleDateFormat(pattern);
|
||||
assertEquals(sdf.format(date1), fdf.format(date1));
|
||||
assertEquals(sdf.format(date2), fdf.format(date2));
|
||||
|
||||
assertEquals(sdf.format(date2), fdf.format(date2));
|
||||
} finally {
|
||||
Locale.setDefault(realDefaultLocale);
|
||||
TimeZone.setDefault(realDefaultZone);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test case for {@link FastDateFormat#getDateInstance(int, java.util.Locale)}.
|
||||
*/
|
||||
|
@ -283,7 +271,6 @@ public class FastDateFormatTest extends TestCase {
|
|||
* testLowYearPadding showed that the date was buggy
|
||||
* This test confirms it, getting 366 back as a date
|
||||
*/
|
||||
// TODO: Fix this problem
|
||||
public void testSimpleDate() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
FastDateFormat format = FastDateFormat.getInstance("yyyy/MM/dd");
|
||||
|
@ -308,8 +295,6 @@ public class FastDateFormatTest extends TestCase {
|
|||
}
|
||||
|
||||
public void testLang538() {
|
||||
final String dateTime = "2009-10-16T16:42:16.000Z";
|
||||
|
||||
// more commonly constructed with: cal = new GregorianCalendar(2009, 9, 16, 8, 42, 16)
|
||||
// for the unit test to work in any time zone, constructing with GMT-8 rather than default locale time zone
|
||||
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT-8"));
|
||||
|
@ -317,7 +302,8 @@ public class FastDateFormatTest extends TestCase {
|
|||
cal.set(2009, 9, 16, 8, 42, 16);
|
||||
|
||||
FastDateFormat format = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone("GMT"));
|
||||
assertEquals("dateTime", dateTime, format.format(cal));
|
||||
assertEquals("dateTime", "2009-10-16T16:42:16.000Z", format.format(cal.getTime()));
|
||||
assertEquals("dateTime", "2009-10-16T08:42:16.000Z", format.format(cal));
|
||||
}
|
||||
|
||||
public void testLang645() {
|
||||
|
|
Loading…
Reference in New Issue