mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-10 07:39:16 +00:00
ICU-10274 Add compound duration formatting for JAVA.
X-SVN-Rev: 33980
This commit is contained in:
parent
a26088f951
commit
4b1b47ddb0
4 changed files with 246 additions and 136 deletions
|
@ -13,6 +13,9 @@ import java.util.HashMap;
|
|||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
|
||||
import javax.management.StandardEmitterMBean;
|
||||
|
||||
import com.ibm.icu.impl.ICUCache;
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
|
@ -34,6 +37,43 @@ final public class ListFormatter {
|
|||
private final String middle;
|
||||
private final String end;
|
||||
private final ULocale locale;
|
||||
|
||||
/**
|
||||
* Indicates the style of Listformatter
|
||||
* @deprecated internal use only.
|
||||
* @internal
|
||||
*/
|
||||
public enum Style {
|
||||
/**
|
||||
* Standard style.
|
||||
* @deprecated
|
||||
* @internal
|
||||
*/
|
||||
STANDARD("standard"),
|
||||
/**
|
||||
* Style for full durations
|
||||
* @deprecated
|
||||
* @internal
|
||||
*/
|
||||
DURATION("duration"),
|
||||
/**
|
||||
* Style for durations in abbrevated form
|
||||
* @deprecated
|
||||
* @internal
|
||||
*/
|
||||
DURATION_SHORT("duration-short");
|
||||
|
||||
private final String name;
|
||||
|
||||
Style(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>Internal:</b> Create a ListFormatter from component strings,
|
||||
|
@ -75,7 +115,7 @@ final public class ListFormatter {
|
|||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static ListFormatter getInstance(ULocale locale) {
|
||||
return cache.get(locale);
|
||||
return getInstance(locale, Style.STANDARD);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,7 +128,20 @@ final public class ListFormatter {
|
|||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static ListFormatter getInstance(Locale locale) {
|
||||
return getInstance(ULocale.forLocale(locale));
|
||||
return getInstance(ULocale.forLocale(locale), Style.STANDARD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list formatter that is appropriate for a locale and style.
|
||||
*
|
||||
* @param locale the locale in question.
|
||||
* @param style the style
|
||||
* @return ListFormatter
|
||||
* @deprecated Internal use only.
|
||||
* @internal
|
||||
*/
|
||||
public static ListFormatter getInstance(ULocale locale, Style style) {
|
||||
return cache.get(locale, style.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -199,28 +252,39 @@ final public class ListFormatter {
|
|||
}
|
||||
|
||||
private static class Cache {
|
||||
private final ICUCache<ULocale, ListFormatter> cache =
|
||||
new SimpleCache<ULocale, ListFormatter>();
|
||||
private final ICUCache<String, ListFormatter> cache =
|
||||
new SimpleCache<String, ListFormatter>();
|
||||
|
||||
public ListFormatter get(ULocale locale) {
|
||||
ListFormatter result = cache.get(locale);
|
||||
public ListFormatter get(ULocale locale, String style) {
|
||||
String key = String.format("%s:%s", locale.toString(), style);
|
||||
ListFormatter result = cache.get(key);
|
||||
if (result == null) {
|
||||
result = load(locale);
|
||||
cache.put(locale, result);
|
||||
result = load(locale, style);
|
||||
cache.put(key, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ListFormatter load(ULocale ulocale) {
|
||||
private static ListFormatter load(ULocale ulocale, String style) {
|
||||
ICUResourceBundle r = (ICUResourceBundle)UResourceBundle.
|
||||
getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, ulocale);
|
||||
r = r.getWithFallback("listPattern/standard");
|
||||
return new ListFormatter(
|
||||
r.getWithFallback("2").getString(),
|
||||
r.getWithFallback("start").getString(),
|
||||
r.getWithFallback("middle").getString(),
|
||||
r.getWithFallback("end").getString(),
|
||||
ulocale);
|
||||
// TODO(Travis Keep): This try-catch is a hack to cover missing aliases
|
||||
// for listPattern/duration and listPattern/duration-narrow in root.txt.
|
||||
try {
|
||||
return new ListFormatter(
|
||||
r.getWithFallback("listPattern/" + style + "/2").getString(),
|
||||
r.getWithFallback("listPattern/" + style + "/start").getString(),
|
||||
r.getWithFallback("listPattern/" + style + "/middle").getString(),
|
||||
r.getWithFallback("listPattern/" + style + "/end").getString(),
|
||||
ulocale);
|
||||
} catch (MissingResourceException e) {
|
||||
return new ListFormatter(
|
||||
r.getWithFallback("listPattern/standard/2").getString(),
|
||||
r.getWithFallback("listPattern/standard/start").getString(),
|
||||
r.getWithFallback("listPattern/standard/middle").getString(),
|
||||
r.getWithFallback("listPattern/standard/end").getString(),
|
||||
ulocale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
*/
|
||||
package com.ibm.icu.text;
|
||||
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
@ -20,6 +22,7 @@ import com.ibm.icu.impl.ICUResourceBundle;
|
|||
import com.ibm.icu.util.TimePeriod;
|
||||
import com.ibm.icu.util.TimeUnit;
|
||||
import com.ibm.icu.util.TimeUnitAmount;
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.ULocale.Category;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
|
@ -96,9 +99,9 @@ public class TimeUnitFormat extends MeasureFormat {
|
|||
private transient Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns;
|
||||
private transient PluralRules pluralRules;
|
||||
private transient ListFormatter listFormatter;
|
||||
private transient MessageFormat hourMinute;
|
||||
private transient MessageFormat minuteSecond;
|
||||
private transient MessageFormat hourMinuteSecond;
|
||||
private transient DateFormat hourMinute;
|
||||
private transient DateFormat minuteSecond;
|
||||
private transient DateFormat hourMinuteSecond;
|
||||
private transient boolean isReady;
|
||||
private int style;
|
||||
|
||||
|
@ -132,8 +135,7 @@ public class TimeUnitFormat extends MeasureFormat {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create TimeUnitFormat given a ULocale and a formatting style: full or
|
||||
* abbreviated.
|
||||
* Create TimeUnitFormat given a ULocale and a formatting style.
|
||||
* @param locale locale of this time unit formatter.
|
||||
* @param style format style, either FULL_NAME or ABBREVIATED_NAME style.
|
||||
* @throws IllegalArgumentException if the style is not FULL_NAME or
|
||||
|
@ -150,8 +152,7 @@ public class TimeUnitFormat extends MeasureFormat {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create TimeUnitFormat given a Locale and a formatting style: full or
|
||||
* abbreviated.
|
||||
* Create TimeUnitFormat given a Locale and a formatting style.
|
||||
* @stable ICU 4.2
|
||||
*/
|
||||
public TimeUnitFormat(Locale locale, int style) {
|
||||
|
@ -226,12 +227,18 @@ public class TimeUnitFormat extends MeasureFormat {
|
|||
*/
|
||||
public StringBuffer format(Object obj, StringBuffer toAppendTo,
|
||||
FieldPosition pos) {
|
||||
if ( !(obj instanceof TimeUnitAmount) ) {
|
||||
throw new IllegalArgumentException("can not format non TimeUnitAmount object");
|
||||
if ( !(obj instanceof TimeUnitAmount) && !(obj instanceof TimePeriod)) {
|
||||
throw new IllegalArgumentException(
|
||||
"can only format TimeUnitAmount or TimePeriod objects");
|
||||
}
|
||||
if (!isReady) {
|
||||
setup();
|
||||
}
|
||||
if (obj instanceof TimePeriod) {
|
||||
// TODO: set FieldPosition, see ICU tickets 10156 and 10157.
|
||||
toAppendTo.append(formatTimePeriod((TimePeriod) obj));
|
||||
return toAppendTo;
|
||||
}
|
||||
TimeUnitAmount amount = (TimeUnitAmount) obj;
|
||||
Map<String, Object[]> countToPattern = timeUnitToCountToPatterns.get(amount.getTimeUnit());
|
||||
double number = amount.getNumber().doubleValue();
|
||||
|
@ -242,13 +249,7 @@ public class TimeUnitFormat extends MeasureFormat {
|
|||
return pattern.format(new Object[]{amount.getNumber()}, toAppendTo, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a TimePeriod. Currently there is no way to parse a formatted TimePeriod.
|
||||
* @param timePeriod the TimePeriod to format.
|
||||
* @return the formatted string.
|
||||
* @draft ICU 52
|
||||
*/
|
||||
public String formatTimePeriod(TimePeriod timePeriod) {
|
||||
private String formatTimePeriod(TimePeriod timePeriod) {
|
||||
if (!isReady) {
|
||||
setup();
|
||||
}
|
||||
|
@ -258,7 +259,7 @@ public class TimeUnitFormat extends MeasureFormat {
|
|||
return result;
|
||||
}
|
||||
}
|
||||
String[] items = new String[timePeriod.size()];
|
||||
String[] items = new String[timePeriod.length()];
|
||||
int idx = 0;
|
||||
for (TimeUnitAmount amount : timePeriod) {
|
||||
items[idx++] = format(amount);
|
||||
|
@ -267,7 +268,12 @@ public class TimeUnitFormat extends MeasureFormat {
|
|||
}
|
||||
|
||||
/**
|
||||
* Parse a TimeUnitAmount.
|
||||
* Parse a TimeUnitAmount. Parsing TimePeriod objects is not supported.
|
||||
* If parseObject is called on a formatted TimePeriod string, it try to parse it
|
||||
* as a TimeUnitAmount. For example,
|
||||
* <code>parseObject("5 hours and 34 minutes", pos)</code>
|
||||
* returns a TimeUnitAmount representing 5 hours and updates pos to point to the
|
||||
* space after the s in hours.
|
||||
* @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition)
|
||||
* @stable ICU 4.0
|
||||
*/
|
||||
|
@ -373,75 +379,112 @@ public class TimeUnitFormat extends MeasureFormat {
|
|||
format = NumberFormat.getNumberInstance(locale);
|
||||
}
|
||||
pluralRules = PluralRules.forLocale(locale);
|
||||
listFormatter = ListFormatter.getInstance(locale);
|
||||
DateTimePatternGenerator df = DateTimePatternGenerator.getInstance(locale);
|
||||
hourMinute = getPattern(df, "hm", locale, "{0}", "{1,number,00.###}", null);
|
||||
minuteSecond = getPattern(df, "ms", locale, null, "{1}", "{2,number,00.###}");
|
||||
hourMinuteSecond = getPattern(df, "hms", locale, "{0}", "{1,number,00}", "{2,number,00.###}");
|
||||
if (style == FULL_NAME) {
|
||||
listFormatter = ListFormatter.getInstance(locale, ListFormatter.Style.DURATION);
|
||||
} else {
|
||||
listFormatter = ListFormatter.getInstance(locale, ListFormatter.Style.DURATION_SHORT);
|
||||
}
|
||||
hourMinute = loadNumericDurationFormat(locale, "hm");
|
||||
minuteSecond = loadNumericDurationFormat(locale, "ms");
|
||||
hourMinuteSecond = loadNumericDurationFormat(locale, "hms");
|
||||
timeUnitToCountToPatterns = new HashMap<TimeUnit, Map<String, Object[]>>();
|
||||
|
||||
Set<String> pluralKeywords = pluralRules.getKeywords();
|
||||
setup("units/duration", timeUnitToCountToPatterns, FULL_NAME, pluralKeywords);
|
||||
setup("unitsShort/duration", timeUnitToCountToPatterns, ABBREVIATED_NAME, pluralKeywords);
|
||||
isReady = true;
|
||||
}
|
||||
|
||||
private MessageFormat getPattern(DateTimePatternGenerator dtpg, String skeleton, ULocale locale,
|
||||
String h, String m, String s) {
|
||||
String pat = dtpg.getBestPattern(skeleton);
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
for (Object item : new DateTimePatternGenerator.FormatParser().set(pat).getItems()) {
|
||||
if (item instanceof DateTimePatternGenerator.VariableField) {
|
||||
DateTimePatternGenerator.VariableField fld = (DateTimePatternGenerator.VariableField)item;
|
||||
switch (fld.getType()) {
|
||||
case DateTimePatternGenerator.HOUR: buffer.append(h); break;
|
||||
case DateTimePatternGenerator.MINUTE: buffer.append(m); break;
|
||||
case DateTimePatternGenerator.SECOND: buffer.append(s); break;
|
||||
}
|
||||
} else {
|
||||
buffer.append(item);
|
||||
}
|
||||
}
|
||||
return new MessageFormat(buffer.toString(), locale);
|
||||
// type is one of "hm", "ms" or "hms"
|
||||
private static DateFormat loadNumericDurationFormat(ULocale ulocale, String type) {
|
||||
ICUResourceBundle r = (ICUResourceBundle)UResourceBundle.
|
||||
getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, ulocale);
|
||||
r = r.getWithFallback(String.format("durationUnits/%s", type));
|
||||
// We replace 'h' with 'H' because 'h' does not make sense in the context of durations.
|
||||
DateFormat result = new SimpleDateFormat(r.getString().replace("h", "H"));
|
||||
result.setTimeZone(TimeZone.GMT_ZONE);
|
||||
return result;
|
||||
}
|
||||
|
||||
private String formatPeriodAsNumeric(TimePeriod timePeriod) {
|
||||
TimeUnit biggestUnit = null, smallestUnit = null;
|
||||
Number smallestUnitAmount = null;
|
||||
for (TimeUnitAmount tua : timePeriod) {
|
||||
if (biggestUnit == null) {
|
||||
biggestUnit = tua.getTimeUnit();
|
||||
}
|
||||
smallestUnit = tua.getTimeUnit();
|
||||
smallestUnitAmount = tua.getNumber();
|
||||
}
|
||||
// We have to trim the result of MessageFormat.format() not sure why.
|
||||
long millis = (long) (((getAmountOrZero(timePeriod, TimeUnit.HOUR) * 60.0
|
||||
+ getAmountOrZero(timePeriod, TimeUnit.MINUTE)) * 60.0
|
||||
+ getAmountOrZero(timePeriod, TimeUnit.SECOND)) * 1000.0);
|
||||
Date d = new Date(millis);
|
||||
if (biggestUnit == TimeUnit.HOUR && smallestUnit == TimeUnit.SECOND) {
|
||||
return hourMinuteSecond.format(new Object[]{
|
||||
getZeroedAmount(timePeriod, TimeUnit.HOUR),
|
||||
getZeroedAmount(timePeriod, TimeUnit.MINUTE),
|
||||
getZeroedAmount(timePeriod, TimeUnit.SECOND)}).trim();
|
||||
|
||||
return numericFormat(
|
||||
d, hourMinuteSecond, DateFormat.Field.SECOND, smallestUnitAmount);
|
||||
}
|
||||
if (biggestUnit == TimeUnit.MINUTE && smallestUnit == TimeUnit.SECOND) {
|
||||
return minuteSecond.format(new Object[]{
|
||||
null,
|
||||
getZeroedAmount(timePeriod, TimeUnit.MINUTE),
|
||||
getZeroedAmount(timePeriod, TimeUnit.SECOND)}).trim();
|
||||
|
||||
return numericFormat(
|
||||
d, minuteSecond, DateFormat.Field.SECOND, smallestUnitAmount);
|
||||
}
|
||||
if (biggestUnit == TimeUnit.HOUR && smallestUnit == TimeUnit.MINUTE) {
|
||||
return hourMinute.format(new Object[]{
|
||||
getZeroedAmount(timePeriod, TimeUnit.HOUR),
|
||||
getZeroedAmount(timePeriod, TimeUnit.MINUTE)}).trim();
|
||||
return numericFormat(d, hourMinute, DateFormat.Field.MINUTE, smallestUnitAmount);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Number getZeroedAmount(TimePeriod timePeriod, TimeUnit timeUnit) {
|
||||
|
||||
/**
|
||||
* numericFormat allows us to show fractional durations using numeric
|
||||
* style e.g 12:34:56.7. This function is necessary because there is no way to express
|
||||
* fractions of durations other than seconds with current DateFormat objects.
|
||||
*
|
||||
* After formatting the duration using a DateFormat object in the usual way, it
|
||||
* replaces the smallest field in the formatted string with the exact fractional
|
||||
* amount of that smallest field formatted with this object's NumberFormat object.
|
||||
*
|
||||
* @param duration The duration to format in milliseconds. The loss of precision here
|
||||
* is ok because we also pass in the exact amount of the smallest field.
|
||||
* @param formatter formats the date.
|
||||
* @param smallestField the smallest defined field in duration to be formatted.
|
||||
* @param smallestAmount the exact fractional value of the smallest amount.
|
||||
* @return duration formatted numeric style.
|
||||
*/
|
||||
private String numericFormat(
|
||||
Date duration,
|
||||
DateFormat formatter,
|
||||
DateFormat.Field smallestField,
|
||||
Number smallestAmount) {
|
||||
// Format the smallest amount ahead of time.
|
||||
String smallestAmountFormatted = format.format(smallestAmount);
|
||||
|
||||
// Format the duration using the provided DateFormat object. The smallest
|
||||
// field in this result will be missing the fractional part.
|
||||
AttributedCharacterIterator iterator = formatter.formatToCharacterIterator(duration);
|
||||
|
||||
// The final formatted duration will be written here.
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
// iterate through formatted text copying to 'builder' one character at a time.
|
||||
// When we get to the smallest amount, skip over it and copy
|
||||
// 'smallestAmountFormatted' to the builder instead.
|
||||
for (iterator.first(); iterator.getIndex() < iterator.getEndIndex();) {
|
||||
if (iterator.getAttributes().containsKey(smallestField)) {
|
||||
builder.append(smallestAmountFormatted);
|
||||
iterator.setIndex(iterator.getRunLimit(smallestField));
|
||||
} else {
|
||||
builder.append(iterator.current());
|
||||
iterator.next();
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static double getAmountOrZero(TimePeriod timePeriod, TimeUnit timeUnit) {
|
||||
TimeUnitAmount tua = timePeriod.getAmount(timeUnit);
|
||||
if (tua == null) {
|
||||
return Double.valueOf(0);
|
||||
return 0.0;
|
||||
}
|
||||
return tua.getNumber();
|
||||
return tua.getNumber().doubleValue();
|
||||
}
|
||||
|
||||
private void setup(String resourceKey, Map<TimeUnit, Map<String, Object[]>> timeUnitToCountToPatterns,
|
||||
|
|
|
@ -26,68 +26,40 @@ import java.util.NoSuchElementException;
|
|||
public final class TimePeriod implements Iterable<TimeUnitAmount> {
|
||||
|
||||
private final TimeUnitAmount[] fields;
|
||||
private final int size;
|
||||
private final int length;
|
||||
private final int hash;
|
||||
|
||||
private TimePeriod(TimeUnitAmount[] fields, int size, int hash) {
|
||||
this.fields = fields;
|
||||
this.size = size;
|
||||
this.hash = hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new TimePeriod that matches the given time unit amounts.
|
||||
* @param amounts the TimeUnitAmounts. Must be non-empty. Normalization of the
|
||||
* amounts and inclusion/exclusion of 0 amounts is up to caller. The Number
|
||||
* in each TimeUnitAmount must either be a Byte, Short, Integer, Long, Float,
|
||||
* Double, BigInteger, or BigDecimal or it must implement Cloneable and have
|
||||
* a public clone method.
|
||||
* @return the new TimePeriod object
|
||||
* @throws IllegalArgumentException if multiple TimeUnitAmount objects match
|
||||
* the same time unit or if any but the smallest TimeUnit has a fractional value
|
||||
* Or if amounts is empty.
|
||||
* @draft ICU 52
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static TimePeriod forAmounts(TimeUnitAmount ...amounts) {
|
||||
return forAmounts(Arrays.asList(amounts));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new TimePeriod that matches the given time unit amounts.
|
||||
* Constructor.
|
||||
* @param amounts the TimeUnitAmounts. Must be non-empty. Normalization of the
|
||||
* amounts and inclusion/exclusion of 0 amounts is up to caller. The Number
|
||||
* object in each TimeUnitAmount must not change. Otherwise the created
|
||||
* TimePeriod object may not work as expected.
|
||||
* @return the new TimePeriod object
|
||||
* @throws IllegalArgumentException if multiple TimeUnitAmount objects match
|
||||
* the same time unit or if any but the smallest TimeUnit has a fractional value
|
||||
* Or if amounts is empty.
|
||||
* @draft ICU 52
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static TimePeriod forAmounts(Iterable<TimeUnitAmount> amounts) {
|
||||
TimeUnitAmount[] fields = new TimeUnitAmount[TimeUnit.TIME_UNIT_COUNT];
|
||||
int size = 0;
|
||||
public TimePeriod(TimeUnitAmount ...amounts) {
|
||||
fields = new TimeUnitAmount[TimeUnit.TIME_UNIT_COUNT];
|
||||
int tempSize = 0;
|
||||
for (TimeUnitAmount tua : amounts) {
|
||||
int index = tua.getTimeUnit().getIndex();
|
||||
if (fields[index] != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Only one TimeUnitAmount per unit allowed.");
|
||||
}
|
||||
// This line is necessary to guarantee immutability of the TimePeriod
|
||||
// class. A Number object, which is in TimeUnitAmount, need not be immutable,
|
||||
// but Double is immutable.
|
||||
fields[index] = tua;
|
||||
size++;
|
||||
tempSize++;
|
||||
}
|
||||
if (size == 0) {
|
||||
length = tempSize;
|
||||
if (length == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"There must be at least one TimeUnitAmount.");
|
||||
}
|
||||
TimePeriod result = new TimePeriod(fields, size, computeHash(fields));
|
||||
boolean fractionalFieldEncountered = false;
|
||||
for (TimeUnitAmount tua : result) {
|
||||
for (TimeUnitAmount tua : this) {
|
||||
if (fractionalFieldEncountered) {
|
||||
throw new IllegalArgumentException(
|
||||
"Only the smallest time unit can have a fractional amount.");
|
||||
|
@ -97,7 +69,7 @@ public final class TimePeriod implements Iterable<TimeUnitAmount> {
|
|||
fractionalFieldEncountered = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
hash = computeHash(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,11 +98,10 @@ public final class TimePeriod implements Iterable<TimeUnitAmount> {
|
|||
|
||||
/**
|
||||
* Returns the number of TimeUnitAmount objects in this object.
|
||||
* @internal
|
||||
* @deprecated This API is ICU internal only.
|
||||
* @draft ICU 52
|
||||
*/
|
||||
public int size() {
|
||||
return size;
|
||||
public int length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,26 +23,39 @@ import com.ibm.icu.util.ULocale;
|
|||
*
|
||||
*/
|
||||
public class TimeUnitTest extends TestFmwk {
|
||||
private static final TimePeriod _19m = TimePeriod.forAmounts(
|
||||
private static final TimePeriod _19m = new TimePeriod(
|
||||
new TimeUnitAmount(19.0, TimeUnit.MINUTE));
|
||||
private static final TimePeriod _19m_28s = TimePeriod.forAmounts(
|
||||
private static final TimePeriod _19m_28s = new TimePeriod(
|
||||
new TimeUnitAmount(19.0, TimeUnit.MINUTE),
|
||||
new TimeUnitAmount(28.0, TimeUnit.SECOND));
|
||||
private static final TimePeriod _1h_23_5s = TimePeriod.forAmounts(
|
||||
private static final TimePeriod _1h_23_5s = new TimePeriod(
|
||||
new TimeUnitAmount(1.0, TimeUnit.HOUR),
|
||||
new TimeUnitAmount(23.5, TimeUnit.SECOND));
|
||||
private static final TimePeriod _1h_0m_23s = TimePeriod.forAmounts(
|
||||
private static final TimePeriod _1h_23_5m = new TimePeriod(
|
||||
new TimeUnitAmount(1.0, TimeUnit.HOUR),
|
||||
new TimeUnitAmount(23.5, TimeUnit.MINUTE));
|
||||
private static final TimePeriod _1h_0m_23s = new TimePeriod(
|
||||
new TimeUnitAmount(1.0, TimeUnit.HOUR),
|
||||
new TimeUnitAmount(0.0, TimeUnit.MINUTE),
|
||||
new TimeUnitAmount(23.0, TimeUnit.SECOND));
|
||||
private static final TimePeriod _5h_17m = TimePeriod.forAmounts(
|
||||
private static final TimePeriod _5h_17m = new TimePeriod(
|
||||
new TimeUnitAmount(5.0, TimeUnit.HOUR),
|
||||
new TimeUnitAmount(17.0, TimeUnit.MINUTE));
|
||||
private static final TimePeriod _2y_5M_3w_4d = TimePeriod.forAmounts(
|
||||
private static final TimePeriod _2y_5M_3w_4d = new TimePeriod(
|
||||
new TimeUnitAmount(2.0, TimeUnit.YEAR),
|
||||
new TimeUnitAmount(5.0, TimeUnit.MONTH),
|
||||
new TimeUnitAmount(3.0, TimeUnit.WEEK),
|
||||
new TimeUnitAmount(4.0, TimeUnit.DAY));
|
||||
private static final TimePeriod _0h_0m_17s = new TimePeriod(
|
||||
new TimeUnitAmount(0.0, TimeUnit.HOUR),
|
||||
new TimeUnitAmount(0.0, TimeUnit.MINUTE),
|
||||
new TimeUnitAmount(17.0, TimeUnit.SECOND));
|
||||
private static final TimePeriod _6h_56_92m = new TimePeriod(
|
||||
new TimeUnitAmount(6.0, TimeUnit.HOUR),
|
||||
new TimeUnitAmount(56.92, TimeUnit.MINUTE));
|
||||
private static final TimePeriod _1m_59_9996s = new TimePeriod(
|
||||
new TimeUnitAmount(1.0, TimeUnit.MINUTE),
|
||||
new TimeUnitAmount(59.9996, TimeUnit.SECOND));
|
||||
|
||||
public static void main(String[] args) throws Exception{
|
||||
new TimeUnitTest().run(args);
|
||||
|
@ -358,33 +371,52 @@ public class TimeUnitTest extends TestFmwk {
|
|||
|
||||
public void TestFormatPeriodEn() {
|
||||
Object[][] fullData = {
|
||||
{_1m_59_9996s, "1 minute, 59.9996 seconds"},
|
||||
{_19m, "19 minutes"},
|
||||
{_1h_23_5s, "1 hour and 23.5 seconds"},
|
||||
{_1h_0m_23s, "1 hour, 0 minutes, and 23 seconds"},
|
||||
{_2y_5M_3w_4d, "2 years, 5 months, 3 weeks, and 4 days"}};
|
||||
{_1h_23_5s, "1 hour, 23.5 seconds"},
|
||||
{_1h_23_5m, "1 hour, 23.5 minutes"},
|
||||
{_1h_0m_23s, "1 hour, 0 minutes, 23 seconds"},
|
||||
{_2y_5M_3w_4d, "2 years, 5 months, 3 weeks, 4 days"}};
|
||||
Object[][] abbrevData = {
|
||||
{_1m_59_9996s, "1 min, 59.9996 secs"},
|
||||
{_19m, "19 mins"},
|
||||
{_1h_23_5s, "1 hr and 23.5 secs"},
|
||||
{_1h_0m_23s, "1 hr, 0 mins, and 23 secs"},
|
||||
{_2y_5M_3w_4d, "2 yrs, 5 mths, 3 wks, and 4 days"}};
|
||||
{_1h_23_5s, "1 hr, 23.5 secs"},
|
||||
{_1h_23_5m, "1 hr, 23.5 mins"},
|
||||
{_1h_0m_23s, "1 hr, 0 mins, 23 secs"},
|
||||
{_2y_5M_3w_4d, "2 yrs, 5 mths, 3 wks, 4 days"}};
|
||||
Object[][] numericData = {
|
||||
{_1m_59_9996s, "1:59.9996"},
|
||||
{_19m, "19 mins"},
|
||||
{_1h_23_5s, "1:00:23.5"},
|
||||
{_1h_0m_23s, "1:00:23"},
|
||||
{_1h_23_5m, "1:23.5"},
|
||||
{_5h_17m, "5:17"},
|
||||
{_19m_28s, "19:28"},
|
||||
{_2y_5M_3w_4d, "2 yrs, 5 mths, 3 wks, and 4 days"}};
|
||||
{_2y_5M_3w_4d, "2 yrs, 5 mths, 3 wks, 4 days"},
|
||||
{_0h_0m_17s, "0:00:17"},
|
||||
{_6h_56_92m, "6:56.92"}};
|
||||
TimeUnitFormat tuf = new TimeUnitFormat(ULocale.ENGLISH, TimeUnitFormat.FULL_NAME);
|
||||
NumberFormat nf = NumberFormat.getNumberInstance(ULocale.ENGLISH);
|
||||
nf.setMaximumFractionDigits(4);
|
||||
tuf.setNumberFormat(nf);
|
||||
verifyFormatPeriod("en FULL", tuf, fullData);
|
||||
tuf = new TimeUnitFormat(ULocale.ENGLISH, TimeUnitFormat.ABBREVIATED_NAME);
|
||||
tuf.setNumberFormat(nf);
|
||||
verifyFormatPeriod("en ABBREV", tuf, abbrevData);
|
||||
tuf = new TimeUnitFormat(ULocale.ENGLISH, TimeUnitFormat.NUMERIC);
|
||||
tuf.setNumberFormat(nf);
|
||||
verifyFormatPeriod("en NUMERIC", tuf, numericData);
|
||||
}
|
||||
|
||||
public void TestTimePeriodLength() {
|
||||
assertEquals("length", 2, new TimePeriod(
|
||||
new TimeUnitAmount(3.0, TimeUnit.HOUR),
|
||||
new TimeUnitAmount(5.0, TimeUnit.MINUTE)).length());
|
||||
}
|
||||
|
||||
public void TestTimePeriodForAmounts() {
|
||||
try {
|
||||
TimePeriod.forAmounts(
|
||||
new TimePeriod(
|
||||
new TimeUnitAmount(3.0, TimeUnit.HOUR),
|
||||
new TimeUnitAmount(5.0, TimeUnit.HOUR));
|
||||
errln("Expected IllegalArgumentException on duplicate TimeUnits.");
|
||||
|
@ -392,13 +424,13 @@ public class TimeUnitTest extends TestFmwk {
|
|||
// expected
|
||||
}
|
||||
try {
|
||||
TimePeriod.forAmounts();
|
||||
new TimePeriod();
|
||||
errln("Expected IllegalArgumentException on missing TimeUnitAmounts.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
try {
|
||||
TimePeriod.forAmounts(
|
||||
new TimePeriod(
|
||||
new TimeUnitAmount(3.5, TimeUnit.HOUR),
|
||||
new TimeUnitAmount(5.0, TimeUnit.MINUTE));
|
||||
errln("Expected IllegalArgumentException. Only smallest time unit can have a fractional amount.");
|
||||
|
@ -408,12 +440,12 @@ public class TimeUnitTest extends TestFmwk {
|
|||
}
|
||||
|
||||
public void TestTimePeriodEqualsHashCode() {
|
||||
TimePeriod our_19m_28s = TimePeriod.forAmounts(
|
||||
TimePeriod our_19m_28s = new TimePeriod(
|
||||
new TimeUnitAmount(28.0, TimeUnit.SECOND),
|
||||
new TimeUnitAmount(19.0, TimeUnit.MINUTE));
|
||||
assertEquals("TimePeriod equals", _19m_28s, our_19m_28s);
|
||||
assertEquals("Hash code", _19m_28s.hashCode(), our_19m_28s.hashCode());
|
||||
TimePeriod our_19m_29s = TimePeriod.forAmounts(
|
||||
TimePeriod our_19m_29s = new TimePeriod(
|
||||
new TimeUnitAmount(29.0, TimeUnit.SECOND),
|
||||
new TimeUnitAmount(19.0, TimeUnit.MINUTE));
|
||||
assertNotEquals("TimePeriod not equals", _19m_28s, our_19m_29s);
|
||||
|
@ -427,7 +459,7 @@ public class TimeUnitTest extends TestFmwk {
|
|||
StringBuilder builder = new StringBuilder();
|
||||
boolean failure = false;
|
||||
for (Object[] testCase : testData) {
|
||||
String actual = tuf.formatTimePeriod((TimePeriod) testCase[0]);
|
||||
String actual = tuf.format(testCase[0]);
|
||||
if (!testCase[1].equals(actual)) {
|
||||
builder.append(String.format("%s: Expected: '%s', got: '%s'\n", desc, testCase[1], actual));
|
||||
failure = true;
|
||||
|
|
Loading…
Add table
Reference in a new issue