am 2cb314eb: am e0cd3c19: Merge "Improve DateFormat.format."

* commit '2cb314ebc7bb6495a94f577dfb57f9e6c56033ef':
  Improve DateFormat.format.
diff --git a/api/current.txt b/api/current.txt
index 5794453..385d9ac 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22518,19 +22518,19 @@
     method public static java.text.DateFormat getMediumDateFormat(android.content.Context);
     method public static java.text.DateFormat getTimeFormat(android.content.Context);
     method public static boolean is24HourFormat(android.content.Context);
-    field public static final char AM_PM = 97; // 0x0061 'a'
-    field public static final char CAPITAL_AM_PM = 65; // 0x0041 'A'
-    field public static final char DATE = 100; // 0x0064 'd'
-    field public static final char DAY = 69; // 0x0045 'E'
-    field public static final char HOUR = 104; // 0x0068 'h'
-    field public static final char HOUR_OF_DAY = 107; // 0x006b 'k'
-    field public static final char MINUTE = 109; // 0x006d 'm'
-    field public static final char MONTH = 77; // 0x004d 'M'
-    field public static final char QUOTE = 39; // 0x0027 '\''
-    field public static final char SECONDS = 115; // 0x0073 's'
-    field public static final char STANDALONE_MONTH = 76; // 0x004c 'L'
-    field public static final char TIME_ZONE = 122; // 0x007a 'z'
-    field public static final char YEAR = 121; // 0x0079 'y'
+    field public static final deprecated char AM_PM = 97; // 0x0061 'a'
+    field public static final deprecated char CAPITAL_AM_PM = 65; // 0x0041 'A'
+    field public static final deprecated char DATE = 100; // 0x0064 'd'
+    field public static final deprecated char DAY = 69; // 0x0045 'E'
+    field public static final deprecated char HOUR = 104; // 0x0068 'h'
+    field public static final deprecated char HOUR_OF_DAY = 107; // 0x006b 'k'
+    field public static final deprecated char MINUTE = 109; // 0x006d 'm'
+    field public static final deprecated char MONTH = 77; // 0x004d 'M'
+    field public static final deprecated char QUOTE = 39; // 0x0027 '\''
+    field public static final deprecated char SECONDS = 115; // 0x0073 's'
+    field public static final deprecated char STANDALONE_MONTH = 76; // 0x004c 'L'
+    field public static final deprecated char TIME_ZONE = 122; // 0x007a 'z'
+    field public static final deprecated char YEAR = 121; // 0x0079 'y'
   }
 
   public class DateUtils {
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 7e230ac..6d61a5c 100644
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -34,178 +34,72 @@
 import libcore.icu.LocaleData;
 
 /**
-    Utility class for producing strings with formatted date/time.
-
-    <p>
-    Most callers should avoid supplying their own format strings to this
-    class' {@code format} methods and rely on the correctly localized ones
-    supplied by the system. This class' factory methods return
-    appropriately-localized {@link java.text.DateFormat} instances, suitable
-    for both formatting and parsing dates. For the canonical documentation
-    of format strings, see {@link java.text.SimpleDateFormat}.
-    </p>
-    <p>
-    The format methods in this class takes as inputs a format string and a representation of a date/time.
-    The format string controls how the output is generated.
-    This class only supports a subset of the full Unicode specification.
-    Use {@link java.text.SimpleDateFormat} if you need more.
-    Formatting characters may be repeated in order to get more detailed representations
-    of that field.  For instance, the format character &apos;M&apos; is used to
-    represent the month.  Depending on how many times that character is repeated
-    you get a different representation.
-    </p>
-    <p>
-    For the month of September:<br/>
-    M -&gt; 9<br/>
-    MM -&gt; 09<br/>
-    MMM -&gt; Sep<br/>
-    MMMM -&gt; September
-    </p>
-    <p>
-    The effects of the duplication vary depending on the nature of the field.
-    See the notes on the individual field formatters for details.  For purely numeric
-    fields such as <code>HOUR</code> adding more copies of the designator will
-    zero-pad the value to that number of characters.
-    </p>
-    <p>
-    For 7 minutes past the hour:<br/>
-    m -&gt; 7<br/>
-    mm -&gt; 07<br/>
-    mmm -&gt; 007<br/>
-    mmmm -&gt; 0007
-    </p>
-    <p>
-    Examples for April 6, 1970 at 3:23am:<br/>
-    &quot;MM/dd/yy h:mmaa&quot; -&gt; &quot;04/06/70 3:23am&quot<br/>
-    &quot;MMM dd, yyyy h:mmaa&quot; -&gt; &quot;Apr 6, 1970 3:23am&quot<br/>
-    &quot;MMMM dd, yyyy h:mmaa&quot; -&gt; &quot;April 6, 1970 3:23am&quot<br/>
-    &quot;E, MMMM dd, yyyy h:mmaa&quot; -&gt; &quot;Mon, April 6, 1970 3:23am&<br/>
-    &quot;EEEE, MMMM dd, yyyy h:mmaa&quot; -&gt; &quot;Monday, April 6, 1970 3:23am&quot;<br/>
-    &quot;&apos;Noteworthy day: &apos;M/d/yy&quot; -&gt; &quot;Noteworthy day: 4/6/70&quot;
+ * Utility class for producing strings with formatted date/time.
+ *
+ * <p>Most callers should avoid supplying their own format strings to this
+ * class' {@code format} methods and rely on the correctly localized ones
+ * supplied by the system. This class' factory methods return
+ * appropriately-localized {@link java.text.DateFormat} instances, suitable
+ * for both formatting and parsing dates. For the canonical documentation
+ * of format strings, see {@link java.text.SimpleDateFormat}.
+ *
+ * <p>The format methods in this class implement a subset of Unicode
+ * <a href="http://www.unicode.org/reports/tr35/#Date_Format_Patterns">UTS #35</a> patterns.
+ * The subset supported by this class includes the following format characters:
+ * {@code acdEhkLMmsyz}. See {@link java.text.SimpleDateFormat} for more documentation
+ * about patterns, or if you need a more compete implementation.
  */
-
 public class DateFormat {
-    /**
-        Text in the format string that should be copied verbatim rather that
-        interpreted as formatting codes must be surrounded by the <code>QUOTE</code>
-        character.  If you need to embed a literal <code>QUOTE</code> character in
-        the output text then use two in a row.
-     */
+    /** @deprecated Use a literal {@code '} instead. */
+    @Deprecated
     public  static final char    QUOTE                  =    '\'';
-    
-    /**
-        This designator indicates whether the <code>HOUR</code> field is before
-        or after noon.  The output is lower-case.
-     
-        Examples:
-        a -> a or p
-        aa -> am or pm
-     */
+
+    /** @deprecated Use a literal {@code 'a'} instead. */
+    @Deprecated
     public  static final char    AM_PM                  =    'a';
 
-    /**
-        This designator indicates whether the <code>HOUR</code> field is before
-        or after noon.  The output is capitalized.
-     
-        Examples:
-        A -> A or P
-        AA -> AM or PM
-     */
+    /** @deprecated Use a literal {@code 'a'} instead; 'A' was always equivalent to 'a'. */
+    @Deprecated
     public  static final char    CAPITAL_AM_PM          =    'A';
 
-    /**
-        This designator indicates the day of the month.
-         
-        Examples for the 9th of the month:
-        d -> 9
-        dd -> 09
-     */
+    /** @deprecated Use a literal {@code 'd'} instead. */
+    @Deprecated
     public  static final char    DATE                   =    'd';
 
-    /**
-        This designator indicates the name of the day of the week.
-     
-        Examples for Sunday:
-        E -> Sun
-        EEEE -> Sunday
-     */
+    /** @deprecated Use a literal {@code 'E'} instead. */
+    @Deprecated
     public  static final char    DAY                    =    'E';
 
-    /**
-        This designator indicates the hour of the day in 12 hour format.
-     
-        Examples for 3pm:
-        h -> 3
-        hh -> 03
-     */
+    /** @deprecated Use a literal {@code 'h'} instead. */
+    @Deprecated
     public  static final char    HOUR                   =    'h';
 
-    /**
-        This designator indicates the hour of the day in 24 hour format.
-     
-        Example for 3pm:
-        k -> 15
-
-        Examples for midnight:
-        k -> 0
-        kk -> 00
-     */
+    /** @deprecated Use a literal {@code 'k'} instead. */
+    @Deprecated
     public  static final char    HOUR_OF_DAY            =    'k';
 
-    /**
-        This designator indicates the minute of the hour.
-     
-        Examples for 7 minutes past the hour:
-        m -> 7
-        mm -> 07
-     */
+    /** @deprecated Use a literal {@code 'm'} instead. */
+    @Deprecated
     public  static final char    MINUTE                 =    'm';
 
-    /**
-        This designator indicates the month of the year. See also
-        {@link #STANDALONE_MONTH}.
-     
-        Examples for September:
-        M -> 9
-        MM -> 09
-        MMM -> Sep
-        MMMM -> September
-     */
+    /** @deprecated Use a literal {@code 'M'} instead. */
+    @Deprecated
     public  static final char    MONTH                  =    'M';
 
-    /**
-        This designator indicates the standalone month of the year,
-        necessary in some format strings in some languages. For
-        example, Russian distinguishes between the "June" in
-        "June" and that in "June 2010".
-     */
+    /** @deprecated Use a literal {@code 'L'} instead. */
+    @Deprecated
     public  static final char    STANDALONE_MONTH       =    'L';
 
-    /**
-        This designator indicates the seconds of the minute.
-     
-        Examples for 7 seconds past the minute:
-        s -> 7
-        ss -> 07
-     */
+    /** @deprecated Use a literal {@code 's'} instead. */
+    @Deprecated
     public  static final char    SECONDS                =    's';
 
-    /**
-        This designator indicates the offset of the timezone from GMT.
-     
-        Example for US/Pacific timezone:
-        z -> -0800
-        zz -> PST
-     */
+    /** @deprecated Use a literal {@code 'z'} instead. */
+    @Deprecated
     public  static final char    TIME_ZONE              =    'z';
 
-    /**
-        This designator indicates the year.
-     
-        Examples for 2006
-        y -> 06
-        yyyy -> 2006
-     */
+    /** @deprecated Use a literal {@code 'y'} instead. */
+    @Deprecated
     public  static final char    YEAR                   =    'y';
 
 
@@ -233,8 +127,7 @@
             }
 
             java.text.DateFormat natural =
-                java.text.DateFormat.getTimeInstance(
-                    java.text.DateFormat.LONG, locale);
+                java.text.DateFormat.getTimeInstance(java.text.DateFormat.LONG, locale);
 
             if (natural instanceof SimpleDateFormat) {
                 SimpleDateFormat sdf = (SimpleDateFormat) natural;
@@ -273,7 +166,7 @@
     }
 
     /**
-     * Returns a {@link java.text.DateFormat} object that can format the date 
+     * Returns a {@link java.text.DateFormat} object that can format the date
      * in short form (such as 12/31/1999) according
      * to the current locale and the user's date-order preference.
      * @param context the application context
@@ -342,10 +235,10 @@
         value = context.getString(R.string.numeric_date_format);
         return value;
     }
-    
+
     /**
      * Returns a {@link java.text.DateFormat} object that can format the date
-     * in long form (such as December 31, 1999) for the current locale.
+     * in long form (such as {@code Monday, January 3, 2000}) for the current locale.
      * @param context the application context
      * @return the {@link java.text.DateFormat} object that formats the date in long form.
      */
@@ -355,7 +248,7 @@
 
     /**
      * Returns a {@link java.text.DateFormat} object that can format the date
-     * in medium form (such as Dec. 31, 1999) for the current locale.
+     * in medium form (such as {@code Jan 3, 2000}) for the current locale.
      * @param context the application context
      * @return the {@link java.text.DateFormat} object that formats the date in long form.
      */
@@ -365,13 +258,13 @@
 
     /**
      * Gets the current date format stored as a char array. The array will contain
-     * 3 elements ({@link #DATE}, {@link #MONTH}, and {@link #YEAR}) in the order    
+     * 3 elements ({@link #DATE}, {@link #MONTH}, and {@link #YEAR}) in the order
      * specified by the user's format preference.  Note that this order is
      * only appropriate for all-numeric dates; spelled-out (MEDIUM and LONG)
      * dates will generally contain other punctuation, spaces, or words,
      * not just the day, month, and year, and not necessarily in the same
      * order returned here.
-     */    
+     */
     public static char[] getDateFormatOrder(Context context) {
         char[] order = new char[] {DATE, MONTH, YEAR};
         String value = getDateFormatString(context);
@@ -401,7 +294,7 @@
         }
         return order;
     }
-    
+
     private static String getDateFormatString(Context context) {
         String value = Settings.System.getString(context.getContentResolver(),
                 Settings.System.DATE_FORMAT);
@@ -410,7 +303,7 @@
     }
 
     /**
-     * Given a format string and a time in milliseconds since Jan 1, 1970 GMT, returns a 
+     * Given a format string and a time in milliseconds since Jan 1, 1970 GMT, returns a
      * CharSequence containing the requested date.
      * @param inFormat the format string, as described in {@link android.text.format.DateFormat}
      * @param inTimeInMillis in milliseconds since Jan 1, 1970 GMT
@@ -428,22 +321,20 @@
      * @return a {@link CharSequence} containing the requested text
      */
     public static CharSequence format(CharSequence inFormat, Date inDate) {
-        Calendar    c = new GregorianCalendar();
-        
+        Calendar c = new GregorianCalendar();
         c.setTime(inDate);
-        
         return format(inFormat, c);
     }
 
     /**
      * Indicates whether the specified format string contains seconds.
-     * 
+     *
      * Always returns false if the input format is null.
-     * 
+     *
      * @param inFormat the format string, as described in {@link android.text.format.DateFormat}
-     *                 
+     *
      * @return true if the format string contains {@link #SECONDS}, false otherwise
-     * 
+     *
      * @hide
      */
     public static boolean hasSeconds(CharSequence inFormat) {
@@ -508,24 +399,23 @@
     }
 
     /**
-     * Given a format string and a {@link java.util.Calendar} object, returns a CharSequence 
+     * Given a format string and a {@link java.util.Calendar} object, returns a CharSequence
      * containing the requested date.
      * @param inFormat the format string, as described in {@link android.text.format.DateFormat}
      * @param inDate the date to format
      * @return a {@link CharSequence} containing the requested text
      */
     public static CharSequence format(CharSequence inFormat, Calendar inDate) {
-        SpannableStringBuilder      s = new SpannableStringBuilder(inFormat);
-        int             c;
-        int             count;
+        SpannableStringBuilder s = new SpannableStringBuilder(inFormat);
+        int count;
+
+        LocaleData localeData = LocaleData.get(Locale.getDefault());
 
         int len = inFormat.length();
 
         for (int i = 0; i < len; i += count) {
-            int temp;
-
             count = 1;
-            c = s.charAt(i);
+            int c = s.charAt(i);
 
             if (c == QUOTE) {
                 count = appendQuotedText(s, i, len);
@@ -538,102 +428,89 @@
             }
 
             String replacement;
-
             switch (c) {
-                case AM_PM:
-                    replacement = DateUtils.getAMPMString(inDate.get(Calendar.AM_PM));
+                case 'A':
+                case 'a':
+                    replacement = localeData.amPm[inDate.get(Calendar.AM_PM) - Calendar.AM];
                     break;
-                                        
-                case CAPITAL_AM_PM:
-                    //FIXME: this is the same as AM_PM? no capital?
-                    replacement = DateUtils.getAMPMString(inDate.get(Calendar.AM_PM));
-                    break;
-                
-                case DATE:
+                case 'd':
                     replacement = zeroPad(inDate.get(Calendar.DATE), count);
                     break;
-                    
-                case DAY:
-                    temp = inDate.get(Calendar.DAY_OF_WEEK);
-                    replacement = DateUtils.getDayOfWeekString(temp,
-                                                               count < 4 ? 
-                                                               DateUtils.LENGTH_MEDIUM : 
-                                                               DateUtils.LENGTH_LONG);
+                case 'c':
+                case 'E':
+                    replacement = getDayOfWeekString(localeData,
+                                                     inDate.get(Calendar.DAY_OF_WEEK), count, c);
                     break;
-                    
-                case HOUR:
-                    temp = inDate.get(Calendar.HOUR);
-
-                    if (0 == temp)
-                        temp = 12;
-                    
-                    replacement = zeroPad(temp, count);
+                case 'h':
+                    int hour = inDate.get(Calendar.HOUR);
+                    replacement = zeroPad(hour == 0 ? 24 : hour, count);
                     break;
-                    
-                case HOUR_OF_DAY:
+                case 'k':
                     replacement = zeroPad(inDate.get(Calendar.HOUR_OF_DAY), count);
                     break;
-                    
-                case MINUTE:
+                case 'L':
+                case 'M':
+                    replacement = getMonthString(localeData,
+                                                 inDate.get(Calendar.MONTH), count, c);
+                    break;
+                case 'm':
                     replacement = zeroPad(inDate.get(Calendar.MINUTE), count);
                     break;
-                    
-                case MONTH:
-                case STANDALONE_MONTH:
-                    replacement = getMonthString(inDate, count, c);
-                    break;
-
-                case SECONDS:
+                case 's':
                     replacement = zeroPad(inDate.get(Calendar.SECOND), count);
                     break;
-                    
-                case TIME_ZONE:
+                case 'y':
+                    replacement = getYearString(inDate.get(Calendar.YEAR), count);
+                    break;
+                case 'z':
                     replacement = getTimeZoneString(inDate, count);
                     break;
-                    
-                case YEAR:
-                    replacement = getYearString(inDate, count);
-                    break;
-
                 default:
                     replacement = null;
                     break;
             }
-            
+
             if (replacement != null) {
                 s.replace(i, i + count, replacement);
                 count = replacement.length(); // CARE: count is used in the for loop above
                 len = s.length();
             }
         }
-        
-        if (inFormat instanceof Spanned)
+
+        if (inFormat instanceof Spanned) {
             return new SpannedString(s);
-        else
+        } else {
             return s.toString();
+        }
     }
-    
-    private static String getMonthString(Calendar inDate, int count, int kind) {
-        boolean standalone = (kind == STANDALONE_MONTH);
-        int month = inDate.get(Calendar.MONTH);
-        
-        if (count >= 4) {
-            return standalone
-                ? DateUtils.getStandaloneMonthString(month, DateUtils.LENGTH_LONG)
-                : DateUtils.getMonthString(month, DateUtils.LENGTH_LONG);
+
+    private static String getDayOfWeekString(LocaleData ld, int day, int count, int kind) {
+        boolean standalone = (kind == 'c');
+        if (count == 5) {
+            return standalone ? ld.tinyStandAloneWeekdayNames[day] : ld.tinyWeekdayNames[day];
+        } else if (count == 4) {
+            return standalone ? ld.longStandAloneWeekdayNames[day] : ld.longWeekdayNames[day];
+        } else {
+            return standalone ? ld.shortStandAloneWeekdayNames[day] : ld.shortWeekdayNames[day];
+        }
+    }
+
+    private static String getMonthString(LocaleData ld, int month, int count, int kind) {
+        boolean standalone = (kind == 'L');
+        if (count == 5) {
+            return standalone ? ld.tinyStandAloneMonthNames[month] : ld.tinyMonthNames[month];
+        } else if (count == 4) {
+            return standalone ? ld.longStandAloneMonthNames[month] : ld.longMonthNames[month];
         } else if (count == 3) {
-            return standalone
-                ? DateUtils.getStandaloneMonthString(month, DateUtils.LENGTH_MEDIUM)
-                : DateUtils.getMonthString(month, DateUtils.LENGTH_MEDIUM);
+            return standalone ? ld.shortStandAloneMonthNames[month] : ld.shortMonthNames[month];
         } else {
             // Calendar.JANUARY == 0, so add 1 to month.
             return zeroPad(month+1, count);
         }
     }
-        
+
     private static String getTimeZoneString(Calendar inDate, int count) {
         TimeZone tz = inDate.getTimeZone();
-        
         if (count < 2) { // FIXME: shouldn't this be <= 2 ?
             return formatZoneOffset(inDate.get(Calendar.DST_OFFSET) +
                                     inDate.get(Calendar.ZONE_OFFSET),
@@ -662,13 +539,12 @@
         tb.append(zeroPad(minutes, 2));
         return tb.toString();
     }
-    
-    private static String getYearString(Calendar inDate, int count) {
-        int year = inDate.get(Calendar.YEAR);
+
+    private static String getYearString(int year, int count) {
         return (count <= 2) ? zeroPad(year % 100, 2)
                             : String.format(Locale.getDefault(), "%d", year);
     }
-   
+
     private static int appendQuotedText(SpannableStringBuilder s, int i, int len) {
         if (i + 1 < len && s.charAt(i + 1) == QUOTE) {
             s.delete(i, i + 1);
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 5a20ceb..08813df 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -274,10 +274,6 @@
      */
     @Deprecated
     public static String getMonthString(int month, int abbrev) {
-        // Note that here we use d.shortMonthNames for MEDIUM, SHORT and SHORTER.
-        // This is a shortcut to not spam the translators with too many variations
-        // of the same string.  If we find that in a language the distinction
-        // is necessary, we can can add more without changing this API.
         LocaleData d = LocaleData.get(Locale.getDefault());
         String[] names;
         switch (abbrev) {
@@ -308,19 +304,14 @@
      */
     @Deprecated
     public static String getStandaloneMonthString(int month, int abbrev) {
-        // Note that here we use d.shortMonthNames for MEDIUM, SHORT and SHORTER.
-        // This is a shortcut to not spam the translators with too many variations
-        // of the same string.  If we find that in a language the distinction
-        // is necessary, we can can add more without changing this API.
         LocaleData d = LocaleData.get(Locale.getDefault());
         String[] names;
         switch (abbrev) {
-            case LENGTH_LONG:       names = d.longStandAloneMonthNames;
-                                                            break;
+            case LENGTH_LONG:       names = d.longStandAloneMonthNames; break;
             case LENGTH_MEDIUM:     names = d.shortMonthNames; break;
             case LENGTH_SHORT:      names = d.shortMonthNames; break;
             case LENGTH_SHORTER:    names = d.shortMonthNames; break;
-            case LENGTH_SHORTEST:   names = d.tinyMonthNames;  break;
+            case LENGTH_SHORTEST:   names = d.tinyStandAloneMonthNames; break;
             default:                names = d.shortMonthNames; break;
         }
         return names[month];