Java源码示例:android.icu.text.DisplayContext

示例1
@Override
protected void onLocaleChanged(Locale locale) {
    final TextView headerYear = mHeaderYear;
    if (headerYear == null) {
        // Abort, we haven't initialized yet. This method will get called
        // again later after everything has been set up.
        return;
    }

    // Update the date formatter.
    mMonthDayFormat = DateFormat.getInstanceForSkeleton("EMMMd", locale);
    mMonthDayFormat.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE);
    mYearFormat = DateFormat.getInstanceForSkeleton("y", locale);

    // Update the header text.
    onCurrentDateChanged(false);
}
 
示例2
@Override
protected void onLocaleChanged(Locale locale) {
    final TextView headerYear = mHeaderYear;
    if (headerYear == null) {
        // Abort, we haven't initialized yet. This method will get called
        // again later after everything has been set up.
        return;
    }

    // Update the date formatter.
    final String datePattern = DateFormatFix.getBestDateTimePattern(mContext, locale, "EMMMd");
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        mMonthDayFormat = new SimpleDateFormat(datePattern, locale);
        ((SimpleDateFormat) mMonthDayFormat).setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE);
        mYearFormat = new SimpleDateFormat("y", locale);
    } else {
        mMonthDayFormat = new java.text.SimpleDateFormat(datePattern, locale);
        mYearFormat = new java.text.SimpleDateFormat("y", locale);
    }

    // Clear out the lazily-initialized accessibility event formatter.
    mAccessibilityEventFormat = null;

    // Update the header text.
    onCurrentDateChanged(false);
}
 
示例3
@Override
public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
    UResource.Table contextsTable = value.getTable();
    for (int i = 0; contextsTable.getKeyAndValue(i, key, value); ++i) {

        CapitalizationContextUsage usage = contextUsageTypeMap.get(key.toString());
        if (usage == null) { continue; };

        int[] intVector = value.getIntVector();
        if (intVector.length < 2) { continue; }

        int titlecaseInt = (capitalization == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU)
                ? intVector[0] : intVector[1];
        if (titlecaseInt == 0) { continue; }

        capitalizationUsage[usage.ordinal()] = true;
        hasCapitalizationUsage = true;
    }
}
 
示例4
@Override
public DisplayContext getContext(DisplayContext.Type type) {
    DisplayContext result;
    switch (type) {
    case DIALECT_HANDLING:
        result = (dialectHandling==DialectHandling.STANDARD_NAMES)? DisplayContext.STANDARD_NAMES: DisplayContext.DIALECT_NAMES;
        break;
    case CAPITALIZATION:
        result = capitalization;
        break;
    case DISPLAY_LENGTH:
        result = nameLength;
        break;
    case SUBSTITUTE_HANDLING:
        result = substituteHandling;
        break;
    default:
        result = DisplayContext.STANDARD_NAMES; // hmm, we should do something else here
        break;
    }
    return result;
}
 
示例5
private String adjustForUsageAndContext(CapitalizationContextUsage usage, String name) {
    if (name != null && name.length() > 0 && UCharacter.isLowerCase(name.codePointAt(0)) &&
            (capitalization==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
            (capitalizationUsage != null && capitalizationUsage[usage.ordinal()]) )) {
        // Note, won't have capitalizationUsage != null && capitalizationUsage[usage.ordinal()]
        // unless capitalization is CAPITALIZATION_FOR_UI_LIST_OR_MENU or CAPITALIZATION_FOR_STANDALONE
        synchronized (this) {
            if (capitalizationBrkIter == null) {
                // should only happen when deserializing, etc.
                capitalizationBrkIter = BreakIterator.getSentenceInstance(locale);
            }
            return UCharacter.toTitleCase(locale, name, capitalizationBrkIter,
                    UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
        }
    }
    return name;
}
 
示例6
private String keyValueDisplayName(String key, String value, boolean skipAdjust) {
    String keyValueName = null;

    if (key.equals("currency")) {
        keyValueName = currencyDisplayInfo.getName(AsciiUtil.toUpperString(value));
        if (keyValueName == null) {
            keyValueName = value;
        }
    } else {
        if (nameLength == DisplayContext.LENGTH_SHORT) {
            String tmp = langData.get("Types%short", key, value);
            if (tmp != null && !tmp.equals(value)) {
                keyValueName = tmp;
            }
        }
        if (keyValueName == null) {
            keyValueName = langData.get("Types", key, value);
        }
    }

    return skipAdjust? keyValueName: adjustForUsageAndContext(CapitalizationContextUsage.KEYVALUE, keyValueName);
}
 
示例7
@Test
public void TestRelativeDateWithQuantitySrFallback() {
    Object[][] data = {
            {0.0, Direction.NEXT, RelativeUnit.MONTHS, "\u0437\u0430 0 \u043C."},
            {1.2, Direction.NEXT, RelativeUnit.MONTHS, "\u0437\u0430 1,2 \u043C."},
            {21.0, Direction.NEXT, RelativeUnit.MONTHS, "\u0437\u0430 21 \u043C."},
    };
    RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance(
            new ULocale("sr"),
            null,
            RelativeDateTimeFormatter.Style.NARROW,
            DisplayContext.CAPITALIZATION_NONE);
    for (Object[] row : data) {
        String actual = fmt.format(
                ((Double) row[0]).doubleValue(), (Direction) row[1], (RelativeUnit) row[2]);
        assertEquals("Relative date with quantity fallback", row[3], actual);
    }
}
 
示例8
@Test
public void TestGetters() {
    RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance(
            new ULocale("en_US"),
            null,
            RelativeDateTimeFormatter.Style.SHORT,
            DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
    assertEquals("", RelativeDateTimeFormatter.Style.SHORT, fmt.getFormatStyle());
    assertEquals(
            "",
            DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE,
            fmt.getCapitalizationContext());

    // test the no-arguments getInstance();
    RelativeDateTimeFormatter fmt_default = RelativeDateTimeFormatter.getInstance();
    assertEquals("", RelativeDateTimeFormatter.Style.LONG, fmt_default.getFormatStyle());
    assertEquals(
            "",
            DisplayContext.CAPITALIZATION_NONE,
            fmt_default.getCapitalizationContext());
}
 
示例9
@Test
public void TestSidewaysDataLoading() {
    RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance(
            new ULocale("en_GB"),
            null,
            RelativeDateTimeFormatter.Style.NARROW,
            DisplayContext.CAPITALIZATION_NONE);
    String s = fmt.format(3.0, Direction.NEXT, RelativeUnit.MONTHS);
    assertEquals("narrow: in 3 months", "in 3 mo", s);
    String t = fmt.format(1.0, Direction.LAST, RelativeUnit.QUARTERS);
    assertEquals("narrow: 1 qtr ago", "1 qtr ago", t);
    // Check for fallback to SHORT
    String u = fmt.format(3.0, Direction.LAST, RelativeUnit.QUARTERS);
    assertEquals("narrow: 3 qtr ago", "3 qtr ago", u);
    // Do not expect fall back to SHORT
    String v = fmt.format(1.0, Direction.LAST, RelativeUnit.QUARTERS);
    assertEquals("narrow: 1 qtr ago", "1 qtr ago", v);
    String w = fmt.format(6.0, Direction.NEXT, RelativeUnit.QUARTERS);
    assertEquals("narrow: in 6 qtr", "in 6 qtr", w);
}
 
示例10
@Test
public void TestIllformedLocale() {
    ULocale french = ULocale.FRENCH; 
    Collator collator = Collator.getInstance(french); 
    LocaleDisplayNames names = LocaleDisplayNames.getInstance(french,  
            DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU); 
    for (String malformed : Arrays.asList("en-a", "$", "ü--a", "en--US")) {
        try {
            Set<ULocale> supported = Collections.singleton(new ULocale(malformed));
            names.getUiList(supported, false, collator);
            assertNull("Failed to detect bogus locale «" + malformed + "»", supported);
        } catch (IllformedLocaleException e) {
            logln("Successfully detected ill-formed locale «" + malformed + "»:" + e.getMessage());
        } 
    }
}
 
示例11
/**
 * @param displayLocale {@link Locale} to be used to display {@code localeToDisplay}
 * @param localeToDisplay {@link Locale} to be displayed in {@code displayLocale}
 * @param displayContext context parameter to be used to display {@code localeToDisplay} in
 * {@code displayLocale}
 * @return Returns the name of the {@code localeToDisplay} in the user's current locale.
 */
@NonNull
private static String getLocaleDisplayName(
        @Nullable Locale displayLocale, @Nullable Locale localeToDisplay,
        final DisplayContext displayContext) {
    if (localeToDisplay == null) {
        return "";
    }
    final Locale nonNullDisplayLocale =
            displayLocale != null ? displayLocale : Locale.getDefault();
    return LocaleDisplayNames
            .getInstance(nonNullDisplayLocale, displayContext)
            .localeDisplayName(localeToDisplay);
}
 
示例12
private String localeIdName(String localeId) {
    if (nameLength == DisplayContext.LENGTH_SHORT) {
        String locIdName = langData.get("Languages%short", localeId);
        if (locIdName != null && !locIdName.equals(localeId)) {
            return locIdName;
        }
    }
    return langData.get("Languages", localeId);
}
 
示例13
@Override
public String languageDisplayName(String lang) {
    // Special case to eliminate non-languages, which pollute our data.
    if (lang.equals("root") || lang.indexOf('_') != -1) {
        return substituteHandling == DisplayContext.SUBSTITUTE ? lang : null;
    }
    if (nameLength == DisplayContext.LENGTH_SHORT) {
        String langName = langData.get("Languages%short", lang);
        if (langName != null && !langName.equals(lang)) {
            return adjustForUsageAndContext(CapitalizationContextUsage.LANGUAGE, langName);
        }
    }
    return adjustForUsageAndContext(CapitalizationContextUsage.LANGUAGE, langData.get("Languages", lang));
}
 
示例14
@Override
public String scriptDisplayName(String script) {
    String str = langData.get("Scripts%stand-alone", script);
    if (str == null || str.equals(script)) {
        if (nameLength == DisplayContext.LENGTH_SHORT) {
            str = langData.get("Scripts%short", script);
            if (str != null && !str.equals(script)) {
                return adjustForUsageAndContext(CapitalizationContextUsage.SCRIPT, str);
            }
        }
        str = langData.get("Scripts", script);
    }
    return adjustForUsageAndContext(CapitalizationContextUsage.SCRIPT, str);
}
 
示例15
private UiListItem newRow(ULocale modified, DisplayContext capContext) {
    ULocale minimized = ULocale.minimizeSubtags(modified, ULocale.Minimize.FAVOR_SCRIPT);
    String tempName = modified.getDisplayName(locale);
    boolean titlecase = capContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU;
    String nameInDisplayLocale =  titlecase ? UCharacter.toTitleFirst(locale, tempName) : tempName;
    tempName = modified.getDisplayName(modified);
    String nameInSelf = capContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU ? UCharacter.toTitleFirst(modified, tempName) : tempName;
    return new UiListItem(minimized, modified, nameInDisplayLocale, nameInSelf);
}
 
示例16
public LocaleDisplayNames get(ULocale locale, DialectHandling dialectHandling) {
    if (!(dialectHandling == this.dialectHandling && DisplayContext.CAPITALIZATION_NONE == this.capitalization &&
            DisplayContext.LENGTH_FULL == this.nameLength && DisplayContext.SUBSTITUTE == this.substituteHandling &&
            locale.equals(this.locale))) {
        this.locale = locale;
        this.dialectHandling = dialectHandling;
        this.capitalization = DisplayContext.CAPITALIZATION_NONE;
        this.nameLength = DisplayContext.LENGTH_FULL;
        this.substituteHandling = DisplayContext.SUBSTITUTE;
        this.cache = new LocaleDisplayNamesImpl(locale, dialectHandling);
    }
    return cache;
}
 
示例17
public LocaleDisplayNames get(ULocale locale, DisplayContext... contexts) {
    DialectHandling dialectHandlingIn = DialectHandling.STANDARD_NAMES;
    DisplayContext capitalizationIn = DisplayContext.CAPITALIZATION_NONE;
    DisplayContext nameLengthIn = DisplayContext.LENGTH_FULL;
    DisplayContext substituteHandling = DisplayContext.SUBSTITUTE;
    for (DisplayContext contextItem : contexts) {
        switch (contextItem.type()) {
        case DIALECT_HANDLING:
            dialectHandlingIn = (contextItem.value()==DisplayContext.STANDARD_NAMES.value())?
                    DialectHandling.STANDARD_NAMES: DialectHandling.DIALECT_NAMES;
            break;
        case CAPITALIZATION:
            capitalizationIn = contextItem;
            break;
        case DISPLAY_LENGTH:
            nameLengthIn = contextItem;
            break;
        case SUBSTITUTE_HANDLING:
            substituteHandling = contextItem;
            break;
        default:
            break;
        }
    }
    if (!(dialectHandlingIn == this.dialectHandling && capitalizationIn == this.capitalization &&
            nameLengthIn == this.nameLength && substituteHandling == this.substituteHandling &&
            locale.equals(this.locale))) {
        this.locale = locale;
        this.dialectHandling = dialectHandlingIn;
        this.capitalization = capitalizationIn;
        this.nameLength = nameLengthIn;
        this.substituteHandling = substituteHandling;
        this.cache = new LocaleDisplayNamesImpl(locale, contexts);
    }
    return cache;
}
 
示例18
@Override
public void setContext(DisplayContext context) {
    super.setContext(context);
    if (!capitalizationInfoIsSet &&
          (context==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU || context==DisplayContext.CAPITALIZATION_FOR_STANDALONE)) {
        initCapitalizationContextInfo(fLocale);
        capitalizationInfoIsSet = true;
    }
    if (capitalizationBrkIter == null && (context==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
          (context==DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationOfRelativeUnitsForListOrMenu) ||
          (context==DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationOfRelativeUnitsForStandAlone) )) {
        capitalizationBrkIter = BreakIterator.getSentenceInstance(fLocale);
    }
}
 
示例19
@Test
public void TestBadDisplayContext() {
    try {
        RelativeDateTimeFormatter.getInstance(
                new ULocale("en_US"),
                null,
                RelativeDateTimeFormatter.Style.LONG,
                DisplayContext.STANDARD_NAMES);
        fail("Expected IllegalArgumentException");
    } catch (IllegalArgumentException expected) {
    }
}
 
示例20
@Test
public void TestUldnWithGarbage(){
    LocaleDisplayNames ldn = LocaleDisplayNames.getInstance(Locale.US, DisplayContext.DIALECT_NAMES);
    String badLocaleID = "english (United States) [w";
    String expectedResult = "english [united states] [w"; // case changed from input
    String result = ldn.localeDisplayName(badLocaleID);
    if (result.compareTo(expectedResult) != 0) {
        errln("FAIL: LocaleDisplayNames.localeDisplayName(String) for bad locale ID \"" + badLocaleID + "\", expected \"" + expectedResult + "\", got \"" + result + "\"");
    }
    ULocale badLocale = new ULocale(badLocaleID);
    result = ldn.localeDisplayName(badLocale);
    if (result.compareTo(expectedResult) != 0) {
        errln("FAIL: LocaleDisplayNames.localeDisplayName(ULocale) for bad locale ID \"" + badLocaleID + "\", expected \"" + expectedResult + "\", got \"" + result + "\"");
    }
}
 
示例21
/**
 * Returns a display name for this subtype.
 *
 * <p>If {@code subtypeNameResId} is specified (!= 0) text generated from that resource will
 * be returned. The localized string resource of the label should be capitalized for inclusion
 * in UI lists. The string resource may contain at most one {@code %s}. If present, the
 * {@code %s} will be replaced with the display name of the subtype locale in the user's locale.
 *
 * <p>If {@code subtypeNameResId} is not specified (== 0) the framework returns the display name
 * of the subtype locale, as capitalized for use in UI lists, in the user's locale.
 *
 * @param context {@link Context} will be used for getting {@link Locale} and
 * {@link android.content.pm.PackageManager}.
 * @param packageName The package name of the input method.
 * @param appInfo The {@link ApplicationInfo} of the input method.
 * @return a display name for this subtype.
 */
@NonNull
public CharSequence getDisplayName(
        Context context, String packageName, ApplicationInfo appInfo) {
    if (mSubtypeNameResId == 0) {
        return getLocaleDisplayName(getLocaleFromContext(context), getLocaleObject(),
                DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU);
    }

    final CharSequence subtypeName = context.getPackageManager().getText(
            packageName, mSubtypeNameResId, appInfo);
    if (TextUtils.isEmpty(subtypeName)) {
        return "";
    }
    final String subtypeNameString = subtypeName.toString();
    String replacementString;
    if (containsExtraValueKey(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)) {
        replacementString = getExtraValueOf(
                EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME);
    } else {
        final DisplayContext displayContext;
        if (TextUtils.equals(subtypeNameString, "%s")) {
            displayContext = DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU;
        } else if (subtypeNameString.startsWith("%s")) {
            displayContext = DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
        } else {
            displayContext = DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE;
        }
        replacementString = getLocaleDisplayName(getLocaleFromContext(context),
                getLocaleObject(), displayContext);
    }
    if (replacementString == null) {
        replacementString = "";
    }
    try {
        return String.format(subtypeNameString, replacementString);
    } catch (IllegalFormatException e) {
        Slog.w(TAG, "Found illegal format in subtype name("+ subtypeName + "): " + e);
        return "";
    }
}
 
示例22
private void updateMonthYearLabel() {
    final String format = DateFormat.getBestDateTimePattern(mLocale, MONTH_YEAR_FORMAT);
    final SimpleDateFormat formatter = new SimpleDateFormat(format, mLocale);
    formatter.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE);
    mMonthYearLabel = formatter.format(mCalendar.getTime());
}
 
示例23
public static LocaleDisplayNames getInstance(ULocale locale, DisplayContext... contexts) {
    synchronized (cache) {
        return cache.get(locale, contexts);
    }
}
 
示例24
public LocaleDisplayNamesImpl(ULocale locale, DialectHandling dialectHandling) {
    this(locale, (dialectHandling==DialectHandling.STANDARD_NAMES)? DisplayContext.STANDARD_NAMES: DisplayContext.DIALECT_NAMES,
            DisplayContext.CAPITALIZATION_NONE);
}
 
示例25
public LocaleDisplayNamesImpl(ULocale locale, DisplayContext... contexts) {
    DialectHandling dialectHandling = DialectHandling.STANDARD_NAMES;
    DisplayContext capitalization = DisplayContext.CAPITALIZATION_NONE;
    DisplayContext nameLength = DisplayContext.LENGTH_FULL;
    DisplayContext substituteHandling = DisplayContext.SUBSTITUTE;
    for (DisplayContext contextItem : contexts) {
        switch (contextItem.type()) {
        case DIALECT_HANDLING:
            dialectHandling = (contextItem.value()==DisplayContext.STANDARD_NAMES.value())?
                    DialectHandling.STANDARD_NAMES: DialectHandling.DIALECT_NAMES;
            break;
        case CAPITALIZATION:
            capitalization = contextItem;
            break;
        case DISPLAY_LENGTH:
            nameLength = contextItem;
            break;
        case SUBSTITUTE_HANDLING:
            substituteHandling = contextItem;
            break;
        default:
            break;
        }
    }

    this.dialectHandling = dialectHandling;
    this.capitalization = capitalization;
    this.nameLength = nameLength;
    this.substituteHandling = substituteHandling;
    this.langData = LangDataTables.impl.get(locale, substituteHandling == DisplayContext.NO_SUBSTITUTE);
    this.regionData = RegionDataTables.impl.get(locale, substituteHandling == DisplayContext.NO_SUBSTITUTE);
    this.locale = ULocale.ROOT.equals(langData.getLocale()) ? regionData.getLocale() :
        langData.getLocale();

    // Note, by going through DataTable, this uses table lookup rather than straight lookup.
    // That should get us the same data, I think.  This way we don't have to explicitly
    // load the bundle again.  Using direct lookup didn't seem to make an appreciable
    // difference in performance.
    String sep = langData.get("localeDisplayPattern", "separator");
    if (sep == null || "separator".equals(sep)) {
        sep = "{0}, {1}";
    }
    StringBuilder sb = new StringBuilder();
    this.separatorFormat = SimpleFormatterImpl.compileToStringMinMaxArguments(sep, sb, 2, 2);

    String pattern = langData.get("localeDisplayPattern", "pattern");
    if (pattern == null || "pattern".equals(pattern)) {
        pattern = "{0} ({1})";
    }
    this.format = SimpleFormatterImpl.compileToStringMinMaxArguments(pattern, sb, 2, 2);
    if (pattern.contains("(")) {
        formatOpenParen = '(';
        formatCloseParen = ')';
        formatReplaceOpenParen = '[';
        formatReplaceCloseParen = ']';
    } else  {
        formatOpenParen = '(';
        formatCloseParen = ')';
        formatReplaceOpenParen = '[';
        formatReplaceCloseParen = ']';
    }

    String keyTypePattern = langData.get("localeDisplayPattern", "keyTypePattern");
    if (keyTypePattern == null || "keyTypePattern".equals(keyTypePattern)) {
        keyTypePattern = "{0}={1}";
    }
    this.keyTypeFormat = SimpleFormatterImpl.compileToStringMinMaxArguments(
            keyTypePattern, sb, 2, 2);

    // Get values from the contextTransforms data if we need them
    // Also check whether we will need a break iterator (depends on the data)
    boolean needBrkIter = false;
    if (capitalization == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU ||
            capitalization == DisplayContext.CAPITALIZATION_FOR_STANDALONE) {
        capitalizationUsage = new boolean[CapitalizationContextUsage.values().length]; // initialized to all false
        ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale);
        CapitalizationContextSink sink = new CapitalizationContextSink();
        try {
            rb.getAllItemsWithFallback("contextTransforms", sink);
        }
        catch (MissingResourceException e) {
            // Silently ignore.  Not every locale has contextTransforms.
        }
        needBrkIter = sink.hasCapitalizationUsage;
    }
    // Get a sentence break iterator if we will need it
    if (needBrkIter || capitalization == DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
        capitalizationBrkIter = BreakIterator.getSentenceInstance(locale);
    }

    this.currencyDisplayInfo = CurrencyData.provider.getInstance(locale, false);
}
 
示例26
@Override
public StringBuffer format(Calendar cal, StringBuffer toAppendTo,
        FieldPosition fieldPosition) {

    String relativeDayString = null;
    DisplayContext capitalizationContext = getContext(DisplayContext.Type.CAPITALIZATION);

    if (fDateStyle != DateFormat.NONE) {
        // calculate the difference, in days, between 'cal' and now.
        int dayDiff = dayDifference(cal);

        // look up string
        relativeDayString = getStringForDay(dayDiff);
    }

    if (fDateTimeFormat != null) {
        if (relativeDayString != null && fDatePattern != null &&
                (fTimePattern == null || fCombinedFormat == null || combinedFormatHasDateAtStart) ) {
            // capitalize relativeDayString according to context for relative, set formatter no context
            if ( relativeDayString.length() > 0 && UCharacter.isLowerCase(relativeDayString.codePointAt(0)) &&
                 (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
                    (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU && capitalizationOfRelativeUnitsForListOrMenu) ||
                    (capitalizationContext == DisplayContext.CAPITALIZATION_FOR_STANDALONE && capitalizationOfRelativeUnitsForStandAlone) )) {
                if (capitalizationBrkIter == null) {
                    // should only happen when deserializing, etc.
                    capitalizationBrkIter = BreakIterator.getSentenceInstance(fLocale);
                }
                relativeDayString = UCharacter.toTitleCase(fLocale, relativeDayString, capitalizationBrkIter,
                                UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
            }
            fDateTimeFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
        } else {
            // set our context for the formatter
            fDateTimeFormat.setContext(capitalizationContext);
        }
    }

    if (fDateTimeFormat != null && (fDatePattern != null || fTimePattern != null)) {
        // The new way
        if (fDatePattern == null) {
            // must have fTimePattern
            fDateTimeFormat.applyPattern(fTimePattern);
            fDateTimeFormat.format(cal, toAppendTo, fieldPosition);
        } else if (fTimePattern == null) {
            // must have fDatePattern
            if (relativeDayString != null) {
                toAppendTo.append(relativeDayString);
            } else {
                fDateTimeFormat.applyPattern(fDatePattern);
                fDateTimeFormat.format(cal, toAppendTo, fieldPosition);
            }
        } else {
            String datePattern = fDatePattern; // default;
            if (relativeDayString != null) {
                // Need to quote the relativeDayString to make it a legal date pattern
                datePattern = "'" + relativeDayString.replace("'", "''") + "'";
            }
            StringBuffer combinedPattern = new StringBuffer("");
            fCombinedFormat.format(new Object[] {fTimePattern, datePattern}, combinedPattern, new FieldPosition(0));
            fDateTimeFormat.applyPattern(combinedPattern.toString());
            fDateTimeFormat.format(cal, toAppendTo, fieldPosition);
        }
    } else if (fDateFormat != null) {
        // A subset of the old way, for serialization compatibility
        // (just do the date part)
        if (relativeDayString != null) {
            toAppendTo.append(relativeDayString);
        } else {
            fDateFormat.format(cal, toAppendTo, fieldPosition);
        }
    }

    return toAppendTo;
}
 
示例27
@Test
public void TestRelativeDateWithQuantityCaps() {
    Object[][] data = {
            {0.0, Direction.NEXT, RelativeUnit.SECONDS, "In 0 seconds"},
            {0.5, Direction.NEXT, RelativeUnit.SECONDS, "In 0.5 seconds"},

            {1.0, Direction.NEXT, RelativeUnit.SECONDS, "In 1 second"},
            {2.0, Direction.NEXT, RelativeUnit.SECONDS, "In 2 seconds"},
            {0.0, Direction.NEXT, RelativeUnit.MINUTES, "In 0 minutes"},
            {0.5, Direction.NEXT, RelativeUnit.MINUTES, "In 0.5 minutes"},
            {1.0, Direction.NEXT, RelativeUnit.MINUTES, "In 1 minute"},
            {2.0, Direction.NEXT, RelativeUnit.MINUTES, "In 2 minutes"},
            {0.0, Direction.NEXT, RelativeUnit.HOURS, "In 0 hours"},
            {0.5, Direction.NEXT, RelativeUnit.HOURS, "In 0.5 hours"},
            {1.0, Direction.NEXT, RelativeUnit.HOURS, "In 1 hour"},
            {2.0, Direction.NEXT, RelativeUnit.HOURS, "In 2 hours"},
            {0.0, Direction.NEXT, RelativeUnit.DAYS, "In 0 days"},
            {0.5, Direction.NEXT, RelativeUnit.DAYS, "In 0.5 days"},
            {1.0, Direction.NEXT, RelativeUnit.DAYS, "In 1 day"},
            {2.0, Direction.NEXT, RelativeUnit.DAYS, "In 2 days"},
            {0.0, Direction.NEXT, RelativeUnit.WEEKS, "In 0 weeks"},
            {0.5, Direction.NEXT, RelativeUnit.WEEKS, "In 0.5 weeks"},
            {1.0, Direction.NEXT, RelativeUnit.WEEKS, "In 1 week"},
            {2.0, Direction.NEXT, RelativeUnit.WEEKS, "In 2 weeks"},
            {0.0, Direction.NEXT, RelativeUnit.MONTHS, "In 0 months"},
            {0.5, Direction.NEXT, RelativeUnit.MONTHS, "In 0.5 months"},
            {1.0, Direction.NEXT, RelativeUnit.MONTHS, "In 1 month"},
            {2.0, Direction.NEXT, RelativeUnit.MONTHS, "In 2 months"},
            {0.0, Direction.NEXT, RelativeUnit.YEARS, "In 0 years"},
            {0.5, Direction.NEXT, RelativeUnit.YEARS, "In 0.5 years"},
            {1.0, Direction.NEXT, RelativeUnit.YEARS, "In 1 year"},
            {2.0, Direction.NEXT, RelativeUnit.YEARS, "In 2 years"},

            {0.0, Direction.LAST, RelativeUnit.SECONDS, "0 seconds ago"},
            {0.5, Direction.LAST, RelativeUnit.SECONDS, "0.5 seconds ago"},
            {1.0, Direction.LAST, RelativeUnit.SECONDS, "1 second ago"},
            {2.0, Direction.LAST, RelativeUnit.SECONDS, "2 seconds ago"},
            {0.0, Direction.LAST, RelativeUnit.MINUTES, "0 minutes ago"},
            {0.5, Direction.LAST, RelativeUnit.MINUTES, "0.5 minutes ago"},
            {1.0, Direction.LAST, RelativeUnit.MINUTES, "1 minute ago"},
            {2.0, Direction.LAST, RelativeUnit.MINUTES, "2 minutes ago"},
            {0.0, Direction.LAST, RelativeUnit.HOURS, "0 hours ago"},
            {0.5, Direction.LAST, RelativeUnit.HOURS, "0.5 hours ago"},
            {1.0, Direction.LAST, RelativeUnit.HOURS, "1 hour ago"},
            {2.0, Direction.LAST, RelativeUnit.HOURS, "2 hours ago"},
            {0.0, Direction.LAST, RelativeUnit.DAYS, "0 days ago"},
            {0.5, Direction.LAST, RelativeUnit.DAYS, "0.5 days ago"},
            {1.0, Direction.LAST, RelativeUnit.DAYS, "1 day ago"},
            {2.0, Direction.LAST, RelativeUnit.DAYS, "2 days ago"},
            {0.0, Direction.LAST, RelativeUnit.WEEKS, "0 weeks ago"},
            {0.5, Direction.LAST, RelativeUnit.WEEKS, "0.5 weeks ago"},
            {1.0, Direction.LAST, RelativeUnit.WEEKS, "1 week ago"},
            {2.0, Direction.LAST, RelativeUnit.WEEKS, "2 weeks ago"},
            {0.0, Direction.LAST, RelativeUnit.MONTHS, "0 months ago"},
            {0.5, Direction.LAST, RelativeUnit.MONTHS, "0.5 months ago"},
            {1.0, Direction.LAST, RelativeUnit.MONTHS, "1 month ago"},
            {2.0, Direction.LAST, RelativeUnit.MONTHS, "2 months ago"},
            {0.0, Direction.LAST, RelativeUnit.YEARS, "0 years ago"},
            {0.5, Direction.LAST, RelativeUnit.YEARS, "0.5 years ago"},
            {1.0, Direction.LAST, RelativeUnit.YEARS, "1 year ago"},
            {2.0, Direction.LAST, RelativeUnit.YEARS, "2 years ago"},

    };
    RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance(
            new ULocale("en_US"),
            null,
            RelativeDateTimeFormatter.Style.LONG,
            DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
    for (Object[] row : data) {
        String actual = fmt.format(
                ((Double) row[0]).doubleValue(), (Direction) row[1], (RelativeUnit) row[2]);
        assertEquals("Relative date with quantity", row[3], actual);
    }
}
 
示例28
@Test
public void TestRelativeDateWithQuantityShort() {
    Object[][] data = {
            {0.0, Direction.NEXT, RelativeUnit.SECONDS, "in 0 sec."},
            {0.5, Direction.NEXT, RelativeUnit.SECONDS, "in 0.5 sec."},

            {1.0, Direction.NEXT, RelativeUnit.SECONDS, "in 1 sec."},
            {2.0, Direction.NEXT, RelativeUnit.SECONDS, "in 2 sec."},
            {0.0, Direction.NEXT, RelativeUnit.MINUTES, "in 0 min."},
            {0.5, Direction.NEXT, RelativeUnit.MINUTES, "in 0.5 min."},
            {1.0, Direction.NEXT, RelativeUnit.MINUTES, "in 1 min."},
            {2.0, Direction.NEXT, RelativeUnit.MINUTES, "in 2 min."},
            {0.0, Direction.NEXT, RelativeUnit.HOURS, "in 0 hr."},
            {0.5, Direction.NEXT, RelativeUnit.HOURS, "in 0.5 hr."},
            {1.0, Direction.NEXT, RelativeUnit.HOURS, "in 1 hr."},
            {2.0, Direction.NEXT, RelativeUnit.HOURS, "in 2 hr."},
            {0.0, Direction.NEXT, RelativeUnit.DAYS, "in 0 days"},
            {0.5, Direction.NEXT, RelativeUnit.DAYS, "in 0.5 days"},
            {1.0, Direction.NEXT, RelativeUnit.DAYS, "in 1 day"},
            {2.0, Direction.NEXT, RelativeUnit.DAYS, "in 2 days"},
            {0.0, Direction.NEXT, RelativeUnit.WEEKS, "in 0 wk."},
            {0.5, Direction.NEXT, RelativeUnit.WEEKS, "in 0.5 wk."},
            {1.0, Direction.NEXT, RelativeUnit.WEEKS, "in 1 wk."},
            {2.0, Direction.NEXT, RelativeUnit.WEEKS, "in 2 wk."},
            {0.0, Direction.NEXT, RelativeUnit.MONTHS, "in 0 mo."},
            {0.5, Direction.NEXT, RelativeUnit.MONTHS, "in 0.5 mo."},
            {1.0, Direction.NEXT, RelativeUnit.MONTHS, "in 1 mo."},
            {2.0, Direction.NEXT, RelativeUnit.MONTHS, "in 2 mo."},
            {0.0, Direction.NEXT, RelativeUnit.YEARS, "in 0 yr."},
            {0.5, Direction.NEXT, RelativeUnit.YEARS, "in 0.5 yr."},
            {1.0, Direction.NEXT, RelativeUnit.YEARS, "in 1 yr."},
            {2.0, Direction.NEXT, RelativeUnit.YEARS, "in 2 yr."},

            {0.0, Direction.LAST, RelativeUnit.SECONDS, "0 sec. ago"},
            {0.5, Direction.LAST, RelativeUnit.SECONDS, "0.5 sec. ago"},
            {1.0, Direction.LAST, RelativeUnit.SECONDS, "1 sec. ago"},
            {2.0, Direction.LAST, RelativeUnit.SECONDS, "2 sec. ago"},
            {0.0, Direction.LAST, RelativeUnit.MINUTES, "0 min. ago"},
            {0.5, Direction.LAST, RelativeUnit.MINUTES, "0.5 min. ago"},
            {1.0, Direction.LAST, RelativeUnit.MINUTES, "1 min. ago"},
            {2.0, Direction.LAST, RelativeUnit.MINUTES, "2 min. ago"},
            {0.0, Direction.LAST, RelativeUnit.HOURS, "0 hr. ago"},
            {0.5, Direction.LAST, RelativeUnit.HOURS, "0.5 hr. ago"},
            {1.0, Direction.LAST, RelativeUnit.HOURS, "1 hr. ago"},
            {2.0, Direction.LAST, RelativeUnit.HOURS, "2 hr. ago"},
            {0.0, Direction.LAST, RelativeUnit.DAYS, "0 days ago"},
            {0.5, Direction.LAST, RelativeUnit.DAYS, "0.5 days ago"},
            {1.0, Direction.LAST, RelativeUnit.DAYS, "1 day ago"},
            {2.0, Direction.LAST, RelativeUnit.DAYS, "2 days ago"},
            {0.0, Direction.LAST, RelativeUnit.WEEKS, "0 wk. ago"},
            {0.5, Direction.LAST, RelativeUnit.WEEKS, "0.5 wk. ago"},
            {1.0, Direction.LAST, RelativeUnit.WEEKS, "1 wk. ago"},
            {2.0, Direction.LAST, RelativeUnit.WEEKS, "2 wk. ago"},
            {0.0, Direction.LAST, RelativeUnit.MONTHS, "0 mo. ago"},
            {0.5, Direction.LAST, RelativeUnit.MONTHS, "0.5 mo. ago"},
            {1.0, Direction.LAST, RelativeUnit.MONTHS, "1 mo. ago"},
            {2.0, Direction.LAST, RelativeUnit.MONTHS, "2 mo. ago"},
            {0.0, Direction.LAST, RelativeUnit.YEARS, "0 yr. ago"},
            {0.5, Direction.LAST, RelativeUnit.YEARS, "0.5 yr. ago"},
            {1.0, Direction.LAST, RelativeUnit.YEARS, "1 yr. ago"},
            {2.0, Direction.LAST, RelativeUnit.YEARS, "2 yr. ago"},

    };
    RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance(
            new ULocale("en_US"),
            null,
            RelativeDateTimeFormatter.Style.SHORT,
            DisplayContext.CAPITALIZATION_NONE);
    for (Object[] row : data) {
        String actual = fmt.format(
                ((Double) row[0]).doubleValue(), (Direction) row[1], (RelativeUnit) row[2]);
        assertEquals("Relative date with quantity", row[3], actual);
    }
}
 
示例29
@Test
public void TestRelativeDateWithQuantityNarrow() {
    Object[][] data = {
            {0.0, Direction.NEXT, RelativeUnit.SECONDS, "in 0 sec."},
            {0.5, Direction.NEXT, RelativeUnit.SECONDS, "in 0.5 sec."},

            {1.0, Direction.NEXT, RelativeUnit.SECONDS, "in 1 sec."},
            {2.0, Direction.NEXT, RelativeUnit.SECONDS, "in 2 sec."},
            {0.0, Direction.NEXT, RelativeUnit.MINUTES, "in 0 min."},
            {0.5, Direction.NEXT, RelativeUnit.MINUTES, "in 0.5 min."},
            {1.0, Direction.NEXT, RelativeUnit.MINUTES, "in 1 min."},
            {2.0, Direction.NEXT, RelativeUnit.MINUTES, "in 2 min."},
            {0.0, Direction.NEXT, RelativeUnit.HOURS, "in 0 hr."},
            {0.5, Direction.NEXT, RelativeUnit.HOURS, "in 0.5 hr."},
            {1.0, Direction.NEXT, RelativeUnit.HOURS, "in 1 hr."},
            {2.0, Direction.NEXT, RelativeUnit.HOURS, "in 2 hr."},
            {0.0, Direction.NEXT, RelativeUnit.DAYS, "in 0 days"},
            {0.5, Direction.NEXT, RelativeUnit.DAYS, "in 0.5 days"},
            {1.0, Direction.NEXT, RelativeUnit.DAYS, "in 1 day"},
            {2.0, Direction.NEXT, RelativeUnit.DAYS, "in 2 days"},
            {0.0, Direction.NEXT, RelativeUnit.WEEKS, "in 0 wk."},
            {0.5, Direction.NEXT, RelativeUnit.WEEKS, "in 0.5 wk."},
            {1.0, Direction.NEXT, RelativeUnit.WEEKS, "in 1 wk."},
            {2.0, Direction.NEXT, RelativeUnit.WEEKS, "in 2 wk."},
            {0.0, Direction.NEXT, RelativeUnit.MONTHS, "in 0 mo."},
            {0.5, Direction.NEXT, RelativeUnit.MONTHS, "in 0.5 mo."},
            {1.0, Direction.NEXT, RelativeUnit.MONTHS, "in 1 mo."},
            {2.0, Direction.NEXT, RelativeUnit.MONTHS, "in 2 mo."},
            {0.0, Direction.NEXT, RelativeUnit.YEARS, "in 0 yr."},
            {0.5, Direction.NEXT, RelativeUnit.YEARS, "in 0.5 yr."},
            {1.0, Direction.NEXT, RelativeUnit.YEARS, "in 1 yr."},
            {2.0, Direction.NEXT, RelativeUnit.YEARS, "in 2 yr."},

            {0.0, Direction.LAST, RelativeUnit.SECONDS, "0 sec. ago"},
            {0.5, Direction.LAST, RelativeUnit.SECONDS, "0.5 sec. ago"},
            {1.0, Direction.LAST, RelativeUnit.SECONDS, "1 sec. ago"},
            {2.0, Direction.LAST, RelativeUnit.SECONDS, "2 sec. ago"},
            {0.0, Direction.LAST, RelativeUnit.MINUTES, "0 min. ago"},
            {0.5, Direction.LAST, RelativeUnit.MINUTES, "0.5 min. ago"},
            {1.0, Direction.LAST, RelativeUnit.MINUTES, "1 min. ago"},
            {2.0, Direction.LAST, RelativeUnit.MINUTES, "2 min. ago"},
            {0.0, Direction.LAST, RelativeUnit.HOURS, "0 hr. ago"},
            {0.5, Direction.LAST, RelativeUnit.HOURS, "0.5 hr. ago"},
            {1.0, Direction.LAST, RelativeUnit.HOURS, "1 hr. ago"},
            {2.0, Direction.LAST, RelativeUnit.HOURS, "2 hr. ago"},
            {0.0, Direction.LAST, RelativeUnit.DAYS, "0 days ago"},
            {0.5, Direction.LAST, RelativeUnit.DAYS, "0.5 days ago"},
            {1.0, Direction.LAST, RelativeUnit.DAYS, "1 day ago"},
            {2.0, Direction.LAST, RelativeUnit.DAYS, "2 days ago"},
            {0.0, Direction.LAST, RelativeUnit.WEEKS, "0 wk. ago"},
            {0.5, Direction.LAST, RelativeUnit.WEEKS, "0.5 wk. ago"},
            {1.0, Direction.LAST, RelativeUnit.WEEKS, "1 wk. ago"},
            {2.0, Direction.LAST, RelativeUnit.WEEKS, "2 wk. ago"},
            {0.0, Direction.LAST, RelativeUnit.MONTHS, "0 mo. ago"},
            {0.5, Direction.LAST, RelativeUnit.MONTHS, "0.5 mo. ago"},
            {1.0, Direction.LAST, RelativeUnit.MONTHS, "1 mo. ago"},
            {2.0, Direction.LAST, RelativeUnit.MONTHS, "2 mo. ago"},
            {0.0, Direction.LAST, RelativeUnit.YEARS, "0 yr. ago"},
            {0.5, Direction.LAST, RelativeUnit.YEARS, "0.5 yr. ago"},
            {1.0, Direction.LAST, RelativeUnit.YEARS, "1 yr. ago"},
            {2.0, Direction.LAST, RelativeUnit.YEARS, "2 yr. ago"},

    };
    RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance(
            new ULocale("en_US"),
            null,
            RelativeDateTimeFormatter.Style.NARROW,
            DisplayContext.CAPITALIZATION_NONE);
    for (Object[] row : data) {
        String actual = fmt.format(
                ((Double) row[0]).doubleValue(), (Direction) row[1], (RelativeUnit) row[2]);
        assertEquals("Relative date with quantity", row[3], actual);
    }
}
 
示例30
@Test
public void TestRelativeDateWithoutQuantityCaps() {
    Object[][] data = {
            {Direction.NEXT_2, AbsoluteUnit.DAY, null},

            {Direction.NEXT, AbsoluteUnit.DAY, "Tomorrow"},
            {Direction.NEXT, AbsoluteUnit.WEEK, "Next week"},
            {Direction.NEXT, AbsoluteUnit.MONTH, "Next month"},
            {Direction.NEXT, AbsoluteUnit.YEAR, "Next year"},

            {Direction.NEXT, AbsoluteUnit.MONDAY, "Next Monday"},
            {Direction.NEXT, AbsoluteUnit.TUESDAY, "Next Tuesday"},
            {Direction.NEXT, AbsoluteUnit.WEDNESDAY, "Next Wednesday"},
            {Direction.NEXT, AbsoluteUnit.THURSDAY, "Next Thursday"},
            {Direction.NEXT, AbsoluteUnit.FRIDAY, "Next Friday"},
            {Direction.NEXT, AbsoluteUnit.SATURDAY, "Next Saturday"},
            {Direction.NEXT, AbsoluteUnit.SUNDAY, "Next Sunday"},

            {Direction.LAST_2, AbsoluteUnit.DAY, null},

            {Direction.LAST, AbsoluteUnit.DAY, "Yesterday"},
            {Direction.LAST, AbsoluteUnit.WEEK, "Last week"},
            {Direction.LAST, AbsoluteUnit.MONTH, "Last month"},
            {Direction.LAST, AbsoluteUnit.YEAR, "Last year"},
            {Direction.LAST, AbsoluteUnit.MONDAY, "Last Monday"},
            {Direction.LAST, AbsoluteUnit.TUESDAY, "Last Tuesday"},
            {Direction.LAST, AbsoluteUnit.WEDNESDAY, "Last Wednesday"},
            {Direction.LAST, AbsoluteUnit.THURSDAY, "Last Thursday"},
            {Direction.LAST, AbsoluteUnit.FRIDAY, "Last Friday"},
            {Direction.LAST, AbsoluteUnit.SATURDAY, "Last Saturday"},
            {Direction.LAST, AbsoluteUnit.SUNDAY, "Last Sunday"},

            {Direction.THIS, AbsoluteUnit.DAY, "Today"},
            {Direction.THIS, AbsoluteUnit.WEEK, "This week"},
            {Direction.THIS, AbsoluteUnit.MONTH, "This month"},
            {Direction.THIS, AbsoluteUnit.YEAR, "This year"},
            {Direction.THIS, AbsoluteUnit.MONDAY, "This Monday"},
            {Direction.THIS, AbsoluteUnit.TUESDAY, "This Tuesday"},
            {Direction.THIS, AbsoluteUnit.WEDNESDAY, "This Wednesday"},
            {Direction.THIS, AbsoluteUnit.THURSDAY, "This Thursday"},
            {Direction.THIS, AbsoluteUnit.FRIDAY, "This Friday"},
            {Direction.THIS, AbsoluteUnit.SATURDAY, "This Saturday"},
            {Direction.THIS, AbsoluteUnit.SUNDAY, "This Sunday"},

            {Direction.PLAIN, AbsoluteUnit.DAY, "Day"},
            {Direction.PLAIN, AbsoluteUnit.WEEK, "Week"},
            {Direction.PLAIN, AbsoluteUnit.MONTH, "Month"},
            {Direction.PLAIN, AbsoluteUnit.YEAR, "Year"},
            {Direction.PLAIN, AbsoluteUnit.MONDAY, "Monday"},
            {Direction.PLAIN, AbsoluteUnit.TUESDAY, "Tuesday"},
            {Direction.PLAIN, AbsoluteUnit.WEDNESDAY, "Wednesday"},
            {Direction.PLAIN, AbsoluteUnit.THURSDAY, "Thursday"},
            {Direction.PLAIN, AbsoluteUnit.FRIDAY, "Friday"},
            {Direction.PLAIN, AbsoluteUnit.SATURDAY, "Saturday"},
            {Direction.PLAIN, AbsoluteUnit.SUNDAY, "Sunday"},

            {Direction.PLAIN, AbsoluteUnit.NOW, "Now"},

    };
    RelativeDateTimeFormatter fmt = RelativeDateTimeFormatter.getInstance(
            new ULocale("en_US"),
            null,
            RelativeDateTimeFormatter.Style.LONG,
            DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
    for (Object[] row : data) {
        String actual = fmt.format((Direction) row[0], (AbsoluteUnit) row[1]);
        assertEquals("Relative date without quantity caps", row[2], actual);
    }
}