Home | History | Annotate | Download | only in format
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.text.format;
     18 
     19 import android.content.Context;
     20 import android.os.UserHandle;
     21 import android.provider.Settings;
     22 import android.text.SpannableStringBuilder;
     23 import android.text.Spanned;
     24 import android.text.SpannedString;
     25 
     26 import com.android.internal.R;
     27 
     28 import java.util.Calendar;
     29 import java.util.Date;
     30 import java.util.GregorianCalendar;
     31 import java.util.Locale;
     32 import java.util.TimeZone;
     33 import java.text.SimpleDateFormat;
     34 
     35 import libcore.icu.ICU;
     36 import libcore.icu.LocaleData;
     37 
     38 /**
     39  * Utility class for producing strings with formatted date/time.
     40  *
     41  * <p>Most callers should avoid supplying their own format strings to this
     42  * class' {@code format} methods and rely on the correctly localized ones
     43  * supplied by the system. This class' factory methods return
     44  * appropriately-localized {@link java.text.DateFormat} instances, suitable
     45  * for both formatting and parsing dates. For the canonical documentation
     46  * of format strings, see {@link java.text.SimpleDateFormat}.
     47  *
     48  * <p>In cases where the system does not provide a suitable pattern,
     49  * this class offers the {@link #getBestDateTimePattern} method.
     50  *
     51  * <p>The {@code format} methods in this class implement a subset of Unicode
     52  * <a href="http://www.unicode.org/reports/tr35/#Date_Format_Patterns">UTS #35</a> patterns.
     53  * The subset currently supported by this class includes the following format characters:
     54  * {@code acdEHhLKkLMmsyz}. Up to API level 17, only {@code adEhkMmszy} were supported.
     55  * Note that this class incorrectly implements {@code k} as if it were {@code H} for backwards
     56  * compatibility.
     57  *
     58  * <p>See {@link java.text.SimpleDateFormat} for more documentation
     59  * about patterns, or if you need a more complete or correct implementation.
     60  * Note that the non-{@code format} methods in this class are implemented by
     61  * {@code SimpleDateFormat}.
     62  */
     63 public class DateFormat {
     64     /**
     65      * @deprecated Use a literal {@code '} instead.
     66      * @removed
     67      */
     68     @Deprecated
     69     public  static final char    QUOTE                  =    '\'';
     70 
     71     /**
     72      * @deprecated Use a literal {@code 'a'} instead.
     73      * @removed
     74      */
     75     @Deprecated
     76     public  static final char    AM_PM                  =    'a';
     77 
     78     /**
     79      * @deprecated Use a literal {@code 'a'} instead; 'A' was always equivalent to 'a'.
     80      * @removed
     81      */
     82     @Deprecated
     83     public  static final char    CAPITAL_AM_PM          =    'A';
     84 
     85     /**
     86      * @deprecated Use a literal {@code 'd'} instead.
     87      * @removed
     88      */
     89     @Deprecated
     90     public  static final char    DATE                   =    'd';
     91 
     92     /**
     93      * @deprecated Use a literal {@code 'E'} instead.
     94      * @removed
     95      */
     96     @Deprecated
     97     public  static final char    DAY                    =    'E';
     98 
     99     /**
    100      * @deprecated Use a literal {@code 'h'} instead.
    101      * @removed
    102      */
    103     @Deprecated
    104     public  static final char    HOUR                   =    'h';
    105 
    106     /**
    107      * @deprecated Use a literal {@code 'H'} (for compatibility with {@link SimpleDateFormat}
    108      * and Unicode) or {@code 'k'} (for compatibility with Android releases up to and including
    109      * Jelly Bean MR-1) instead. Note that the two are incompatible.
    110      *
    111      * @removed
    112      */
    113     @Deprecated
    114     public  static final char    HOUR_OF_DAY            =    'k';
    115 
    116     /**
    117      * @deprecated Use a literal {@code 'm'} instead.
    118      * @removed
    119      */
    120     @Deprecated
    121     public  static final char    MINUTE                 =    'm';
    122 
    123     /**
    124      * @deprecated Use a literal {@code 'M'} instead.
    125      * @removed
    126      */
    127     @Deprecated
    128     public  static final char    MONTH                  =    'M';
    129 
    130     /**
    131      * @deprecated Use a literal {@code 'L'} instead.
    132      * @removed
    133      */
    134     @Deprecated
    135     public  static final char    STANDALONE_MONTH       =    'L';
    136 
    137     /**
    138      * @deprecated Use a literal {@code 's'} instead.
    139      * @removed
    140      */
    141     @Deprecated
    142     public  static final char    SECONDS                =    's';
    143 
    144     /**
    145      * @deprecated Use a literal {@code 'z'} instead.
    146      * @removed
    147      */
    148     @Deprecated
    149     public  static final char    TIME_ZONE              =    'z';
    150 
    151     /**
    152      * @deprecated Use a literal {@code 'y'} instead.
    153      * @removed
    154      */
    155     @Deprecated
    156     public  static final char    YEAR                   =    'y';
    157 
    158 
    159     private static final Object sLocaleLock = new Object();
    160     private static Locale sIs24HourLocale;
    161     private static boolean sIs24Hour;
    162 
    163 
    164     /**
    165      * Returns true if user preference is set to 24-hour format.
    166      * @param context the context to use for the content resolver
    167      * @return true if 24 hour time format is selected, false otherwise.
    168      */
    169     public static boolean is24HourFormat(Context context) {
    170         return is24HourFormat(context, UserHandle.myUserId());
    171     }
    172 
    173     /**
    174      * Returns true if user preference with the given user handle is set to 24-hour format.
    175      * @param context the context to use for the content resolver
    176      * @param userHandle the user handle of the user to query.
    177      * @return true if 24 hour time format is selected, false otherwise.
    178      *
    179      * @hide
    180      */
    181     public static boolean is24HourFormat(Context context, int userHandle) {
    182         String value = Settings.System.getStringForUser(context.getContentResolver(),
    183                 Settings.System.TIME_12_24, userHandle);
    184 
    185         if (value == null) {
    186             Locale locale = context.getResources().getConfiguration().locale;
    187 
    188             synchronized (sLocaleLock) {
    189                 if (sIs24HourLocale != null && sIs24HourLocale.equals(locale)) {
    190                     return sIs24Hour;
    191                 }
    192             }
    193 
    194             java.text.DateFormat natural =
    195                     java.text.DateFormat.getTimeInstance(java.text.DateFormat.LONG, locale);
    196 
    197             if (natural instanceof SimpleDateFormat) {
    198                 SimpleDateFormat sdf = (SimpleDateFormat) natural;
    199                 String pattern = sdf.toPattern();
    200 
    201                 if (pattern.indexOf('H') >= 0) {
    202                     value = "24";
    203                 } else {
    204                     value = "12";
    205                 }
    206             } else {
    207                 value = "12";
    208             }
    209 
    210             synchronized (sLocaleLock) {
    211                 sIs24HourLocale = locale;
    212                 sIs24Hour = value.equals("24");
    213             }
    214 
    215             return sIs24Hour;
    216         }
    217 
    218         return value.equals("24");
    219     }
    220 
    221     /**
    222      * Returns the best possible localized form of the given skeleton for the given
    223      * locale. A skeleton is similar to, and uses the same format characters as, a Unicode
    224      * <a href="http://www.unicode.org/reports/tr35/#Date_Format_Patterns">UTS #35</a>
    225      * pattern.
    226      *
    227      * <p>One difference is that order is irrelevant. For example, "MMMMd" will return
    228      * "MMMM d" in the {@code en_US} locale, but "d. MMMM" in the {@code de_CH} locale.
    229      *
    230      * <p>Note also in that second example that the necessary punctuation for German was
    231      * added. For the same input in {@code es_ES}, we'd have even more extra text:
    232      * "d 'de' MMMM".
    233      *
    234      * <p>This method will automatically correct for grammatical necessity. Given the
    235      * same "MMMMd" input, this method will return "d LLLL" in the {@code fa_IR} locale,
    236      * where stand-alone months are necessary. Lengths are preserved where meaningful,
    237      * so "Md" would give a different result to "MMMd", say, except in a locale such as
    238      * {@code ja_JP} where there is only one length of month.
    239      *
    240      * <p>This method will only return patterns that are in CLDR, and is useful whenever
    241      * you know what elements you want in your format string but don't want to make your
    242      * code specific to any one locale.
    243      *
    244      * @param locale the locale into which the skeleton should be localized
    245      * @param skeleton a skeleton as described above
    246      * @return a string pattern suitable for use with {@link java.text.SimpleDateFormat}.
    247      */
    248     public static String getBestDateTimePattern(Locale locale, String skeleton) {
    249         return ICU.getBestDateTimePattern(skeleton, locale);
    250     }
    251 
    252     /**
    253      * Returns a {@link java.text.DateFormat} object that can format the time according
    254      * to the current locale and the user's 12-/24-hour clock preference.
    255      * @param context the application context
    256      * @return the {@link java.text.DateFormat} object that properly formats the time.
    257      */
    258     public static java.text.DateFormat getTimeFormat(Context context) {
    259         return new java.text.SimpleDateFormat(getTimeFormatString(context));
    260     }
    261 
    262     /**
    263      * Returns a String pattern that can be used to format the time according
    264      * to the current locale and the user's 12-/24-hour clock preference.
    265      * @param context the application context
    266      * @hide
    267      */
    268     public static String getTimeFormatString(Context context) {
    269         return getTimeFormatString(context, UserHandle.myUserId());
    270     }
    271 
    272     /**
    273      * Returns a String pattern that can be used to format the time according
    274      * to the current locale and the user's 12-/24-hour clock preference.
    275      * @param context the application context
    276      * @param userHandle the user handle of the user to query the format for
    277      * @hide
    278      */
    279     public static String getTimeFormatString(Context context, int userHandle) {
    280         LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
    281         return is24HourFormat(context, userHandle) ? d.timeFormat24 : d.timeFormat12;
    282     }
    283 
    284     /**
    285      * Returns a {@link java.text.DateFormat} object that can format the date
    286      * in short form (such as 12/31/1999) according
    287      * to the current locale and the user's date-order preference.
    288      * @param context the application context
    289      * @return the {@link java.text.DateFormat} object that properly formats the date.
    290      */
    291     public static java.text.DateFormat getDateFormat(Context context) {
    292         String value = Settings.System.getString(context.getContentResolver(),
    293                 Settings.System.DATE_FORMAT);
    294 
    295         return getDateFormatForSetting(context, value);
    296     }
    297 
    298     /**
    299      * Returns a {@link java.text.DateFormat} object to format the date
    300      * as if the date format setting were set to <code>value</code>,
    301      * including null to use the locale's default format.
    302      * @param context the application context
    303      * @param value the date format setting string to interpret for
    304      *              the current locale
    305      * @hide
    306      */
    307     public static java.text.DateFormat getDateFormatForSetting(Context context,
    308                                                                String value) {
    309         String format = getDateFormatStringForSetting(context, value);
    310         return new java.text.SimpleDateFormat(format);
    311     }
    312 
    313     private static String getDateFormatStringForSetting(Context context, String value) {
    314         if (value != null) {
    315             int month = value.indexOf('M');
    316             int day = value.indexOf('d');
    317             int year = value.indexOf('y');
    318 
    319             if (month >= 0 && day >= 0 && year >= 0) {
    320                 String template = context.getString(R.string.numeric_date_template);
    321                 if (year < month && year < day) {
    322                     if (month < day) {
    323                         value = String.format(template, "yyyy", "MM", "dd");
    324                     } else {
    325                         value = String.format(template, "yyyy", "dd", "MM");
    326                     }
    327                 } else if (month < day) {
    328                     if (day < year) {
    329                         value = String.format(template, "MM", "dd", "yyyy");
    330                     } else { // unlikely
    331                         value = String.format(template, "MM", "yyyy", "dd");
    332                     }
    333                 } else { // day < month
    334                     if (month < year) {
    335                         value = String.format(template, "dd", "MM", "yyyy");
    336                     } else { // unlikely
    337                         value = String.format(template, "dd", "yyyy", "MM");
    338                     }
    339                 }
    340 
    341                 return value;
    342             }
    343         }
    344 
    345         // The setting is not set; use the locale's default.
    346         LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
    347         return d.shortDateFormat4;
    348     }
    349 
    350     /**
    351      * Returns a {@link java.text.DateFormat} object that can format the date
    352      * in long form (such as {@code Monday, January 3, 2000}) for the current locale.
    353      * @param context the application context
    354      * @return the {@link java.text.DateFormat} object that formats the date in long form.
    355      */
    356     public static java.text.DateFormat getLongDateFormat(Context context) {
    357         return java.text.DateFormat.getDateInstance(java.text.DateFormat.LONG);
    358     }
    359 
    360     /**
    361      * Returns a {@link java.text.DateFormat} object that can format the date
    362      * in medium form (such as {@code Jan 3, 2000}) for the current locale.
    363      * @param context the application context
    364      * @return the {@link java.text.DateFormat} object that formats the date in long form.
    365      */
    366     public static java.text.DateFormat getMediumDateFormat(Context context) {
    367         return java.text.DateFormat.getDateInstance(java.text.DateFormat.MEDIUM);
    368     }
    369 
    370     /**
    371      * Gets the current date format stored as a char array. Returns a 3 element
    372      * array containing the day ({@code 'd'}), month ({@code 'M'}), and year ({@code 'y'}))
    373      * in the order specified by the user's format preference.  Note that this order is
    374      * <i>only</i> appropriate for all-numeric dates; spelled-out (MEDIUM and LONG)
    375      * dates will generally contain other punctuation, spaces, or words,
    376      * not just the day, month, and year, and not necessarily in the same
    377      * order returned here.
    378      */
    379     public static char[] getDateFormatOrder(Context context) {
    380         return ICU.getDateFormatOrder(getDateFormatString(context));
    381     }
    382 
    383     private static String getDateFormatString(Context context) {
    384         String value = Settings.System.getString(context.getContentResolver(),
    385                 Settings.System.DATE_FORMAT);
    386 
    387         return getDateFormatStringForSetting(context, value);
    388     }
    389 
    390     /**
    391      * Given a format string and a time in milliseconds since Jan 1, 1970 GMT, returns a
    392      * CharSequence containing the requested date.
    393      * @param inFormat the format string, as described in {@link android.text.format.DateFormat}
    394      * @param inTimeInMillis in milliseconds since Jan 1, 1970 GMT
    395      * @return a {@link CharSequence} containing the requested text
    396      */
    397     public static CharSequence format(CharSequence inFormat, long inTimeInMillis) {
    398         return format(inFormat, new Date(inTimeInMillis));
    399     }
    400 
    401     /**
    402      * Given a format string and a {@link java.util.Date} object, returns a CharSequence containing
    403      * the requested date.
    404      * @param inFormat the format string, as described in {@link android.text.format.DateFormat}
    405      * @param inDate the date to format
    406      * @return a {@link CharSequence} containing the requested text
    407      */
    408     public static CharSequence format(CharSequence inFormat, Date inDate) {
    409         Calendar c = new GregorianCalendar();
    410         c.setTime(inDate);
    411         return format(inFormat, c);
    412     }
    413 
    414     /**
    415      * Indicates whether the specified format string contains seconds.
    416      *
    417      * Always returns false if the input format is null.
    418      *
    419      * @param inFormat the format string, as described in {@link android.text.format.DateFormat}
    420      *
    421      * @return true if the format string contains {@link #SECONDS}, false otherwise
    422      *
    423      * @hide
    424      */
    425     public static boolean hasSeconds(CharSequence inFormat) {
    426         return hasDesignator(inFormat, SECONDS);
    427     }
    428 
    429     /**
    430      * Test if a format string contains the given designator. Always returns
    431      * {@code false} if the input format is {@code null}.
    432      *
    433      * @hide
    434      */
    435     public static boolean hasDesignator(CharSequence inFormat, char designator) {
    436         if (inFormat == null) return false;
    437 
    438         final int length = inFormat.length();
    439 
    440         int c;
    441         int count;
    442 
    443         for (int i = 0; i < length; i += count) {
    444             count = 1;
    445             c = inFormat.charAt(i);
    446 
    447             if (c == QUOTE) {
    448                 count = skipQuotedText(inFormat, i, length);
    449             } else if (c == designator) {
    450                 return true;
    451             }
    452         }
    453 
    454         return false;
    455     }
    456 
    457     private static int skipQuotedText(CharSequence s, int i, int len) {
    458         if (i + 1 < len && s.charAt(i + 1) == QUOTE) {
    459             return 2;
    460         }
    461 
    462         int count = 1;
    463         // skip leading quote
    464         i++;
    465 
    466         while (i < len) {
    467             char c = s.charAt(i);
    468 
    469             if (c == QUOTE) {
    470                 count++;
    471                 //  QUOTEQUOTE -> QUOTE
    472                 if (i + 1 < len && s.charAt(i + 1) == QUOTE) {
    473                     i++;
    474                 } else {
    475                     break;
    476                 }
    477             } else {
    478                 i++;
    479                 count++;
    480             }
    481         }
    482 
    483         return count;
    484     }
    485 
    486     /**
    487      * Given a format string and a {@link java.util.Calendar} object, returns a CharSequence
    488      * containing the requested date.
    489      * @param inFormat the format string, as described in {@link android.text.format.DateFormat}
    490      * @param inDate the date to format
    491      * @return a {@link CharSequence} containing the requested text
    492      */
    493     public static CharSequence format(CharSequence inFormat, Calendar inDate) {
    494         SpannableStringBuilder s = new SpannableStringBuilder(inFormat);
    495         int count;
    496 
    497         LocaleData localeData = LocaleData.get(Locale.getDefault());
    498 
    499         int len = inFormat.length();
    500 
    501         for (int i = 0; i < len; i += count) {
    502             count = 1;
    503             int c = s.charAt(i);
    504 
    505             if (c == QUOTE) {
    506                 count = appendQuotedText(s, i, len);
    507                 len = s.length();
    508                 continue;
    509             }
    510 
    511             while ((i + count < len) && (s.charAt(i + count) == c)) {
    512                 count++;
    513             }
    514 
    515             String replacement;
    516             switch (c) {
    517                 case 'A':
    518                 case 'a':
    519                     replacement = localeData.amPm[inDate.get(Calendar.AM_PM) - Calendar.AM];
    520                     break;
    521                 case 'd':
    522                     replacement = zeroPad(inDate.get(Calendar.DATE), count);
    523                     break;
    524                 case 'c':
    525                 case 'E':
    526                     replacement = getDayOfWeekString(localeData,
    527                                                      inDate.get(Calendar.DAY_OF_WEEK), count, c);
    528                     break;
    529                 case 'K': // hour in am/pm (0-11)
    530                 case 'h': // hour in am/pm (1-12)
    531                     {
    532                         int hour = inDate.get(Calendar.HOUR);
    533                         if (c == 'h' && hour == 0) {
    534                             hour = 12;
    535                         }
    536                         replacement = zeroPad(hour, count);
    537                     }
    538                     break;
    539                 case 'H': // hour in day (0-23)
    540                 case 'k': // hour in day (1-24) [but see note below]
    541                     {
    542                         int hour = inDate.get(Calendar.HOUR_OF_DAY);
    543                         // Historically on Android 'k' was interpreted as 'H', which wasn't
    544                         // implemented, so pretty much all callers that want to format 24-hour
    545                         // times are abusing 'k'. http://b/8359981.
    546                         if (false && c == 'k' && hour == 0) {
    547                             hour = 24;
    548                         }
    549                         replacement = zeroPad(hour, count);
    550                     }
    551                     break;
    552                 case 'L':
    553                 case 'M':
    554                     replacement = getMonthString(localeData,
    555                                                  inDate.get(Calendar.MONTH), count, c);
    556                     break;
    557                 case 'm':
    558                     replacement = zeroPad(inDate.get(Calendar.MINUTE), count);
    559                     break;
    560                 case 's':
    561                     replacement = zeroPad(inDate.get(Calendar.SECOND), count);
    562                     break;
    563                 case 'y':
    564                     replacement = getYearString(inDate.get(Calendar.YEAR), count);
    565                     break;
    566                 case 'z':
    567                     replacement = getTimeZoneString(inDate, count);
    568                     break;
    569                 default:
    570                     replacement = null;
    571                     break;
    572             }
    573 
    574             if (replacement != null) {
    575                 s.replace(i, i + count, replacement);
    576                 count = replacement.length(); // CARE: count is used in the for loop above
    577                 len = s.length();
    578             }
    579         }
    580 
    581         if (inFormat instanceof Spanned) {
    582             return new SpannedString(s);
    583         } else {
    584             return s.toString();
    585         }
    586     }
    587 
    588     private static String getDayOfWeekString(LocaleData ld, int day, int count, int kind) {
    589         boolean standalone = (kind == 'c');
    590         if (count == 5) {
    591             return standalone ? ld.tinyStandAloneWeekdayNames[day] : ld.tinyWeekdayNames[day];
    592         } else if (count == 4) {
    593             return standalone ? ld.longStandAloneWeekdayNames[day] : ld.longWeekdayNames[day];
    594         } else {
    595             return standalone ? ld.shortStandAloneWeekdayNames[day] : ld.shortWeekdayNames[day];
    596         }
    597     }
    598 
    599     private static String getMonthString(LocaleData ld, int month, int count, int kind) {
    600         boolean standalone = (kind == 'L');
    601         if (count == 5) {
    602             return standalone ? ld.tinyStandAloneMonthNames[month] : ld.tinyMonthNames[month];
    603         } else if (count == 4) {
    604             return standalone ? ld.longStandAloneMonthNames[month] : ld.longMonthNames[month];
    605         } else if (count == 3) {
    606             return standalone ? ld.shortStandAloneMonthNames[month] : ld.shortMonthNames[month];
    607         } else {
    608             // Calendar.JANUARY == 0, so add 1 to month.
    609             return zeroPad(month+1, count);
    610         }
    611     }
    612 
    613     private static String getTimeZoneString(Calendar inDate, int count) {
    614         TimeZone tz = inDate.getTimeZone();
    615         if (count < 2) { // FIXME: shouldn't this be <= 2 ?
    616             return formatZoneOffset(inDate.get(Calendar.DST_OFFSET) +
    617                                     inDate.get(Calendar.ZONE_OFFSET),
    618                                     count);
    619         } else {
    620             boolean dst = inDate.get(Calendar.DST_OFFSET) != 0;
    621             return tz.getDisplayName(dst, TimeZone.SHORT);
    622         }
    623     }
    624 
    625     private static String formatZoneOffset(int offset, int count) {
    626         offset /= 1000; // milliseconds to seconds
    627         StringBuilder tb = new StringBuilder();
    628 
    629         if (offset < 0) {
    630             tb.insert(0, "-");
    631             offset = -offset;
    632         } else {
    633             tb.insert(0, "+");
    634         }
    635 
    636         int hours = offset / 3600;
    637         int minutes = (offset % 3600) / 60;
    638 
    639         tb.append(zeroPad(hours, 2));
    640         tb.append(zeroPad(minutes, 2));
    641         return tb.toString();
    642     }
    643 
    644     private static String getYearString(int year, int count) {
    645         return (count <= 2) ? zeroPad(year % 100, 2)
    646                             : String.format(Locale.getDefault(), "%d", year);
    647     }
    648 
    649     private static int appendQuotedText(SpannableStringBuilder s, int i, int len) {
    650         if (i + 1 < len && s.charAt(i + 1) == QUOTE) {
    651             s.delete(i, i + 1);
    652             return 1;
    653         }
    654 
    655         int count = 0;
    656 
    657         // delete leading quote
    658         s.delete(i, i + 1);
    659         len--;
    660 
    661         while (i < len) {
    662             char c = s.charAt(i);
    663 
    664             if (c == QUOTE) {
    665                 //  QUOTEQUOTE -> QUOTE
    666                 if (i + 1 < len && s.charAt(i + 1) == QUOTE) {
    667 
    668                     s.delete(i, i + 1);
    669                     len--;
    670                     count++;
    671                     i++;
    672                 } else {
    673                     //  Closing QUOTE ends quoted text copying
    674                     s.delete(i, i + 1);
    675                     break;
    676                 }
    677             } else {
    678                 i++;
    679                 count++;
    680             }
    681         }
    682 
    683         return count;
    684     }
    685 
    686     private static String zeroPad(int inValue, int inMinDigits) {
    687         return String.format(Locale.getDefault(), "%0" + inMinDigits + "d", inValue);
    688     }
    689 }
    690