Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 02d0c76b authored by Victor Chang's avatar Victor Chang
Browse files

Allow the skeleton of "jmma" in DateFormat.getBestDateTimePattern()

'a' and 'j' specify the intent to use 12-hour format,
and the locale default 12-hour format, and they shouldn't be used
in the same skeleton, because they contradict to each other.

A skeleton, e.g. "jmma", is mapped into "ahmma" internally
by #mapSkeletonMetacharacters, and thus the internal skeleton
has duplicated fields "a".

This is a regression when we moved the implementation of
android.text.format.DateFormat#getBestPattern() from
udatpg_getBestPattern in ICU4C to ICU4j DateTimePatternGenerator
in the CL https://r.android.com/1355735.
The expected pattern generated from skeleton of "jmma" is
"h:mm a".

Bug: 170233598
Test: DateFormatTest
Change-Id: I13929f4b5bc66aaa90e0cef666cf52d7c32122ac
parent 2046a9dc
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -17,10 +17,14 @@
package android.text.format;

import android.annotation.NonNull;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.icu.text.DateFormatSymbols;
import android.icu.text.DateTimePatternGenerator;
import android.os.Build;
import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
@@ -158,6 +162,16 @@ public class DateFormat {
    private static Locale sIs24HourLocale;
    private static boolean sIs24Hour;

    /**
     * {@link #getBestDateTimePattern(Locale, String)} does not allow non-consecutive repeated
     * symbol in the skeleton. For example, please use a skeleton of {@code "jmm"} or
     * {@code "hmma"} instead of {@code "ahmma"} or {@code "jmma"}, because the field 'j' could
     * mean using 12-hour in some locales and, in this case, is duplicated as the 'a' field.
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
    static final long DISALLOW_DUPLICATE_FIELD_IN_SKELETON = 170233598L;

    /**
     * Returns true if times should be formatted as 24 hour times, false if times should be
     * formatted as 12 hour (AM/PM) times. Based on the user's chosen locale and other preferences.
@@ -251,7 +265,9 @@ public class DateFormat {
     */
    public static String getBestDateTimePattern(Locale locale, String skeleton) {
        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(locale);
        return dtpg.getBestPattern(skeleton);
        boolean allowDuplicateFields = !CompatChanges.isChangeEnabled(
                DISALLOW_DUPLICATE_FIELD_IN_SKELETON);
        return dtpg.getBestPattern(skeleton, allowDuplicateFields);
    }

    /**
+34 −0
Original line number Diff line number Diff line
@@ -21,13 +21,19 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import android.compat.testing.PlatformCompatChangeRule;
import android.icu.text.DateFormatSymbols;
import android.platform.test.annotations.Presubmit;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;

import java.util.Arrays;
@@ -38,6 +44,9 @@ import java.util.Locale;
@RunWith(AndroidJUnit4.class)
public class DateFormatTest {

    @Rule
    public TestRule compatChangeRule = new PlatformCompatChangeRule();

    @Test
    public void testHasDesignator() {
        assertTrue(DateFormat.hasDesignator("hh:mm:ss", DateFormat.MINUTE));
@@ -135,4 +144,29 @@ public class DateFormatTest {
    private static String best(Locale l, String skeleton) {
        return DateFormat.getBestDateTimePattern(l, skeleton);
    }

    @Test
    @EnableCompatChanges({DateFormat.DISALLOW_DUPLICATE_FIELD_IN_SKELETON})
    public void testGetBestDateTimePattern_disableDuplicateField() {
        assertIllegalArgumentException(Locale.US, "jmma");
        assertIllegalArgumentException(Locale.US, "ahmma");
    }

    @Test
    @DisableCompatChanges({DateFormat.DISALLOW_DUPLICATE_FIELD_IN_SKELETON})
    public void testGetBestDateTimePattern_enableDuplicateField() {
        // en-US uses 12-hour format by default.
        assertEquals("h:mm a", DateFormat.getBestDateTimePattern(Locale.US, "jmma"));
        assertEquals("h:mm a", DateFormat.getBestDateTimePattern(Locale.US, "ahmma"));
    }

    private static void assertIllegalArgumentException(Locale l, String skeleton) {
        try {
            DateFormat.getBestDateTimePattern(l, skeleton);
            fail("getBestDateTimePattern() does not fail with Locale: " + l
                    + " skeleton: " + skeleton);
        } catch (IllegalArgumentException expected) {
            // ignored
        }
    }
}