Loading core/java/android/text/format/DateFormat.java +49 −3 Original line number Diff line number Diff line Loading @@ -19,12 +19,12 @@ package android.text.format; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.icu.text.DateTimePatternGenerator; import android.provider.Settings; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.SpannedString; import libcore.icu.ICU; import libcore.icu.LocaleData; import java.text.SimpleDateFormat; Loading Loading @@ -251,7 +251,8 @@ public class DateFormat { * @return a string pattern suitable for use with {@link java.text.SimpleDateFormat}. */ public static String getBestDateTimePattern(Locale locale, String skeleton) { return ICU.getBestDateTimePattern(skeleton, locale); DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(locale); return dtpg.getBestPattern(skeleton); } /** Loading Loading @@ -333,7 +334,52 @@ public class DateFormat { * order returned here. */ public static char[] getDateFormatOrder(Context context) { return ICU.getDateFormatOrder(getDateFormatString(context)); return getDateFormatOrder(getDateFormatString(context)); } /** * @hide Used by internal framework class {@link android.widget.DatePickerSpinnerDelegate}. */ public static char[] getDateFormatOrder(String pattern) { char[] result = new char[3]; int resultIndex = 0; boolean sawDay = false; boolean sawMonth = false; boolean sawYear = false; for (int i = 0; i < pattern.length(); ++i) { char ch = pattern.charAt(i); if (ch == 'd' || ch == 'L' || ch == 'M' || ch == 'y') { if (ch == 'd' && !sawDay) { result[resultIndex++] = 'd'; sawDay = true; } else if ((ch == 'L' || ch == 'M') && !sawMonth) { result[resultIndex++] = 'M'; sawMonth = true; } else if ((ch == 'y') && !sawYear) { result[resultIndex++] = 'y'; sawYear = true; } } else if (ch == 'G') { // Ignore the era specifier, if present. } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { throw new IllegalArgumentException("Bad pattern character '" + ch + "' in " + pattern); } else if (ch == '\'') { if (i < pattern.length() - 1 && pattern.charAt(i + 1) == '\'') { ++i; } else { i = pattern.indexOf('\'', i + 1); if (i == -1) { throw new IllegalArgumentException("Bad quoting in " + pattern); } ++i; } } else { // Ignore spaces and punctuation. } } return result; } private static String getDateFormatString(Context context) { Loading core/java/android/widget/DatePickerSpinnerDelegate.java +1 −3 Original line number Diff line number Diff line Loading @@ -34,8 +34,6 @@ import android.view.inputmethod.InputMethodManager; import android.widget.DatePicker.AbstractDatePickerDelegate; import android.widget.NumberPicker.OnValueChangeListener; import libcore.icu.ICU; import java.text.DateFormatSymbols; import java.text.ParseException; import java.text.SimpleDateFormat; Loading Loading @@ -459,7 +457,7 @@ class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate { // We use numeric spinners for year and day, but textual months. Ask icu4c what // order the user's locale uses for that combination. http://b/7207103. String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd"); char[] order = ICU.getDateFormatOrder(pattern); char[] order = DateFormat.getDateFormatOrder(pattern); final int spinnerCount = order.length; for (int i = 0; i < spinnerCount; i++) { switch (order[i]) { Loading core/tests/coretests/src/android/text/format/DateFormatTest.java +70 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package android.text.format; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.platform.test.annotations.Presubmit; Loading @@ -27,6 +29,7 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; import java.util.Locale; @Presubmit Loading Loading @@ -55,4 +58,71 @@ public class DateFormatTest { assertFalse(DateFormat.is24HourLocale(Locale.US)); assertTrue(DateFormat.is24HourLocale(Locale.GERMANY)); } @Test public void testGetDateFormatOrder() { // lv and fa use differing orders depending on whether you're using numeric or // textual months. Locale lv = new Locale("lv"); assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder( best(lv, "yyyy-M-dd")))); assertEquals("[y, d, M]", Arrays.toString(DateFormat.getDateFormatOrder( best(lv, "yyyy-MMM-dd")))); assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder( best(lv, "MMM-dd")))); Locale fa = new Locale("fa"); assertEquals("[y, M, d]", Arrays.toString(DateFormat.getDateFormatOrder( best(fa, "yyyy-M-dd")))); assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder( best(fa, "yyyy-MMM-dd")))); assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder( best(fa, "MMM-dd")))); // English differs on each side of the Atlantic. Locale enUS = Locale.US; assertEquals("[M, d, y]", Arrays.toString(DateFormat.getDateFormatOrder( best(enUS, "yyyy-M-dd")))); assertEquals("[M, d, y]", Arrays.toString(DateFormat.getDateFormatOrder( best(enUS, "yyyy-MMM-dd")))); assertEquals("[M, d, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder( best(enUS, "MMM-dd")))); Locale enGB = Locale.UK; assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder( best(enGB, "yyyy-M-dd")))); assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder( best(enGB, "yyyy-MMM-dd")))); assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder( best(enGB, "MMM-dd")))); assertEquals("[y, M, d]", Arrays.toString(DateFormat.getDateFormatOrder( "yyyy - 'why' '' 'ddd' MMM-dd"))); try { DateFormat.getDateFormatOrder("the quick brown fox jumped over the lazy dog"); fail(); } catch (IllegalArgumentException expected) { } try { DateFormat.getDateFormatOrder("'"); fail(); } catch (IllegalArgumentException expected) { } try { DateFormat.getDateFormatOrder("yyyy'"); fail(); } catch (IllegalArgumentException expected) { } try { DateFormat.getDateFormatOrder("yyyy'MMM"); fail(); } catch (IllegalArgumentException expected) { } } private static String best(Locale l, String skeleton) { return DateFormat.getBestDateTimePattern(l, skeleton); } } Loading
core/java/android/text/format/DateFormat.java +49 −3 Original line number Diff line number Diff line Loading @@ -19,12 +19,12 @@ package android.text.format; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.icu.text.DateTimePatternGenerator; import android.provider.Settings; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.SpannedString; import libcore.icu.ICU; import libcore.icu.LocaleData; import java.text.SimpleDateFormat; Loading Loading @@ -251,7 +251,8 @@ public class DateFormat { * @return a string pattern suitable for use with {@link java.text.SimpleDateFormat}. */ public static String getBestDateTimePattern(Locale locale, String skeleton) { return ICU.getBestDateTimePattern(skeleton, locale); DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(locale); return dtpg.getBestPattern(skeleton); } /** Loading Loading @@ -333,7 +334,52 @@ public class DateFormat { * order returned here. */ public static char[] getDateFormatOrder(Context context) { return ICU.getDateFormatOrder(getDateFormatString(context)); return getDateFormatOrder(getDateFormatString(context)); } /** * @hide Used by internal framework class {@link android.widget.DatePickerSpinnerDelegate}. */ public static char[] getDateFormatOrder(String pattern) { char[] result = new char[3]; int resultIndex = 0; boolean sawDay = false; boolean sawMonth = false; boolean sawYear = false; for (int i = 0; i < pattern.length(); ++i) { char ch = pattern.charAt(i); if (ch == 'd' || ch == 'L' || ch == 'M' || ch == 'y') { if (ch == 'd' && !sawDay) { result[resultIndex++] = 'd'; sawDay = true; } else if ((ch == 'L' || ch == 'M') && !sawMonth) { result[resultIndex++] = 'M'; sawMonth = true; } else if ((ch == 'y') && !sawYear) { result[resultIndex++] = 'y'; sawYear = true; } } else if (ch == 'G') { // Ignore the era specifier, if present. } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { throw new IllegalArgumentException("Bad pattern character '" + ch + "' in " + pattern); } else if (ch == '\'') { if (i < pattern.length() - 1 && pattern.charAt(i + 1) == '\'') { ++i; } else { i = pattern.indexOf('\'', i + 1); if (i == -1) { throw new IllegalArgumentException("Bad quoting in " + pattern); } ++i; } } else { // Ignore spaces and punctuation. } } return result; } private static String getDateFormatString(Context context) { Loading
core/java/android/widget/DatePickerSpinnerDelegate.java +1 −3 Original line number Diff line number Diff line Loading @@ -34,8 +34,6 @@ import android.view.inputmethod.InputMethodManager; import android.widget.DatePicker.AbstractDatePickerDelegate; import android.widget.NumberPicker.OnValueChangeListener; import libcore.icu.ICU; import java.text.DateFormatSymbols; import java.text.ParseException; import java.text.SimpleDateFormat; Loading Loading @@ -459,7 +457,7 @@ class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate { // We use numeric spinners for year and day, but textual months. Ask icu4c what // order the user's locale uses for that combination. http://b/7207103. String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd"); char[] order = ICU.getDateFormatOrder(pattern); char[] order = DateFormat.getDateFormatOrder(pattern); final int spinnerCount = order.length; for (int i = 0; i < spinnerCount; i++) { switch (order[i]) { Loading
core/tests/coretests/src/android/text/format/DateFormatTest.java +70 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,10 @@ package android.text.format; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.platform.test.annotations.Presubmit; Loading @@ -27,6 +29,7 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; import java.util.Locale; @Presubmit Loading Loading @@ -55,4 +58,71 @@ public class DateFormatTest { assertFalse(DateFormat.is24HourLocale(Locale.US)); assertTrue(DateFormat.is24HourLocale(Locale.GERMANY)); } @Test public void testGetDateFormatOrder() { // lv and fa use differing orders depending on whether you're using numeric or // textual months. Locale lv = new Locale("lv"); assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder( best(lv, "yyyy-M-dd")))); assertEquals("[y, d, M]", Arrays.toString(DateFormat.getDateFormatOrder( best(lv, "yyyy-MMM-dd")))); assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder( best(lv, "MMM-dd")))); Locale fa = new Locale("fa"); assertEquals("[y, M, d]", Arrays.toString(DateFormat.getDateFormatOrder( best(fa, "yyyy-M-dd")))); assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder( best(fa, "yyyy-MMM-dd")))); assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder( best(fa, "MMM-dd")))); // English differs on each side of the Atlantic. Locale enUS = Locale.US; assertEquals("[M, d, y]", Arrays.toString(DateFormat.getDateFormatOrder( best(enUS, "yyyy-M-dd")))); assertEquals("[M, d, y]", Arrays.toString(DateFormat.getDateFormatOrder( best(enUS, "yyyy-MMM-dd")))); assertEquals("[M, d, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder( best(enUS, "MMM-dd")))); Locale enGB = Locale.UK; assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder( best(enGB, "yyyy-M-dd")))); assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder( best(enGB, "yyyy-MMM-dd")))); assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder( best(enGB, "MMM-dd")))); assertEquals("[y, M, d]", Arrays.toString(DateFormat.getDateFormatOrder( "yyyy - 'why' '' 'ddd' MMM-dd"))); try { DateFormat.getDateFormatOrder("the quick brown fox jumped over the lazy dog"); fail(); } catch (IllegalArgumentException expected) { } try { DateFormat.getDateFormatOrder("'"); fail(); } catch (IllegalArgumentException expected) { } try { DateFormat.getDateFormatOrder("yyyy'"); fail(); } catch (IllegalArgumentException expected) { } try { DateFormat.getDateFormatOrder("yyyy'MMM"); fail(); } catch (IllegalArgumentException expected) { } } private static String best(Locale l, String skeleton) { return DateFormat.getBestDateTimePattern(l, skeleton); } }