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.content.res.Configuration;
     21 import android.content.res.Resources;
     22 import android.icu.text.MeasureFormat;
     23 import android.icu.text.MeasureFormat.FormatWidth;
     24 import android.icu.util.Measure;
     25 import android.icu.util.MeasureUnit;
     26 
     27 import com.android.internal.R;
     28 
     29 import libcore.icu.DateIntervalFormat;
     30 import libcore.icu.LocaleData;
     31 import libcore.icu.RelativeDateTimeFormatter;
     32 
     33 import java.io.IOException;
     34 import java.util.Calendar;
     35 import java.util.Date;
     36 import java.util.Formatter;
     37 import java.util.GregorianCalendar;
     38 import java.util.Locale;
     39 import java.util.TimeZone;
     40 
     41 /**
     42  * This class contains various date-related utilities for creating text for things like
     43  * elapsed time and date ranges, strings for days of the week and months, and AM/PM text etc.
     44  */
     45 public class DateUtils
     46 {
     47     private static final Object sLock = new Object();
     48     private static Configuration sLastConfig;
     49     private static String sElapsedFormatMMSS;
     50     private static String sElapsedFormatHMMSS;
     51 
     52     public static final long SECOND_IN_MILLIS = 1000;
     53     public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
     54     public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
     55     public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
     56     public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
     57     /**
     58      * This constant is actually the length of 364 days, not of a year!
     59      */
     60     public static final long YEAR_IN_MILLIS = WEEK_IN_MILLIS * 52;
     61 
     62     // The following FORMAT_* symbols are used for specifying the format of
     63     // dates and times in the formatDateRange method.
     64     public static final int FORMAT_SHOW_TIME = 0x00001;
     65     public static final int FORMAT_SHOW_WEEKDAY = 0x00002;
     66     public static final int FORMAT_SHOW_YEAR = 0x00004;
     67     public static final int FORMAT_NO_YEAR = 0x00008;
     68     public static final int FORMAT_SHOW_DATE = 0x00010;
     69     public static final int FORMAT_NO_MONTH_DAY = 0x00020;
     70     @Deprecated
     71     public static final int FORMAT_12HOUR = 0x00040;
     72     @Deprecated
     73     public static final int FORMAT_24HOUR = 0x00080;
     74     @Deprecated
     75     public static final int FORMAT_CAP_AMPM = 0x00100;
     76     public static final int FORMAT_NO_NOON = 0x00200;
     77     @Deprecated
     78     public static final int FORMAT_CAP_NOON = 0x00400;
     79     public static final int FORMAT_NO_MIDNIGHT = 0x00800;
     80     @Deprecated
     81     public static final int FORMAT_CAP_MIDNIGHT = 0x01000;
     82     /**
     83      * @deprecated Use
     84      * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}
     85      * and pass in {@link Time#TIMEZONE_UTC Time.TIMEZONE_UTC} for the timeZone instead.
     86      */
     87     @Deprecated
     88     public static final int FORMAT_UTC = 0x02000;
     89     public static final int FORMAT_ABBREV_TIME = 0x04000;
     90     public static final int FORMAT_ABBREV_WEEKDAY = 0x08000;
     91     public static final int FORMAT_ABBREV_MONTH = 0x10000;
     92     public static final int FORMAT_NUMERIC_DATE = 0x20000;
     93     public static final int FORMAT_ABBREV_RELATIVE = 0x40000;
     94     public static final int FORMAT_ABBREV_ALL = 0x80000;
     95     @Deprecated
     96     public static final int FORMAT_CAP_NOON_MIDNIGHT = (FORMAT_CAP_NOON | FORMAT_CAP_MIDNIGHT);
     97     @Deprecated
     98     public static final int FORMAT_NO_NOON_MIDNIGHT = (FORMAT_NO_NOON | FORMAT_NO_MIDNIGHT);
     99 
    100     // Date and time format strings that are constant and don't need to be
    101     // translated.
    102     /**
    103      * This is not actually the preferred 24-hour date format in all locales.
    104      * @deprecated Use {@link java.text.SimpleDateFormat} instead.
    105      */
    106     @Deprecated
    107     public static final String HOUR_MINUTE_24 = "%H:%M";
    108     public static final String MONTH_FORMAT = "%B";
    109     /**
    110      * This is not actually a useful month name in all locales.
    111      * @deprecated Use {@link java.text.SimpleDateFormat} instead.
    112      */
    113     @Deprecated
    114     public static final String ABBREV_MONTH_FORMAT = "%b";
    115     public static final String NUMERIC_MONTH_FORMAT = "%m";
    116     public static final String MONTH_DAY_FORMAT = "%-d";
    117     public static final String YEAR_FORMAT = "%Y";
    118     public static final String YEAR_FORMAT_TWO_DIGITS = "%g";
    119     public static final String WEEKDAY_FORMAT = "%A";
    120     public static final String ABBREV_WEEKDAY_FORMAT = "%a";
    121 
    122     /** @deprecated Do not use. */
    123     @Deprecated
    124     public static final int[] sameYearTable = null;
    125 
    126     /** @deprecated Do not use. */
    127     @Deprecated
    128     public static final int[] sameMonthTable = null;
    129 
    130     /**
    131      * Request the full spelled-out name. For use with the 'abbrev' parameter of
    132      * {@link #getDayOfWeekString} and {@link #getMonthString}.
    133      *
    134      * @more <p>
    135      *       e.g. "Sunday" or "January"
    136      * @deprecated Use {@link java.text.SimpleDateFormat} instead.
    137      */
    138     @Deprecated
    139     public static final int LENGTH_LONG = 10;
    140 
    141     /**
    142      * Request an abbreviated version of the name. For use with the 'abbrev'
    143      * parameter of {@link #getDayOfWeekString} and {@link #getMonthString}.
    144      *
    145      * @more <p>
    146      *       e.g. "Sun" or "Jan"
    147      * @deprecated Use {@link java.text.SimpleDateFormat} instead.
    148      */
    149     @Deprecated
    150     public static final int LENGTH_MEDIUM = 20;
    151 
    152     /**
    153      * Request a shorter abbreviated version of the name.
    154      * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}.
    155      * @more
    156      * <p>e.g. "Su" or "Jan"
    157      * <p>In most languages, the results returned for LENGTH_SHORT will be the same as
    158      * the results returned for {@link #LENGTH_MEDIUM}.
    159      * @deprecated Use {@link java.text.SimpleDateFormat} instead.
    160      */
    161     @Deprecated
    162     public static final int LENGTH_SHORT = 30;
    163 
    164     /**
    165      * Request an even shorter abbreviated version of the name.
    166      * Do not use this.  Currently this will always return the same result
    167      * as {@link #LENGTH_SHORT}.
    168      * @deprecated Use {@link java.text.SimpleDateFormat} instead.
    169      */
    170     @Deprecated
    171     public static final int LENGTH_SHORTER = 40;
    172 
    173     /**
    174      * Request an even shorter abbreviated version of the name.
    175      * For use with the 'abbrev' parameter of {@link #getDayOfWeekString} and {@link #getMonthString}.
    176      * @more
    177      * <p>e.g. "S", "T", "T" or "J"
    178      * <p>In some languages, the results returned for LENGTH_SHORTEST will be the same as
    179      * the results returned for {@link #LENGTH_SHORT}.
    180      * @deprecated Use {@link java.text.SimpleDateFormat} instead.
    181      */
    182     @Deprecated
    183     public static final int LENGTH_SHORTEST = 50;
    184 
    185     /**
    186      * Return a string for the day of the week.
    187      * @param dayOfWeek One of {@link Calendar#SUNDAY Calendar.SUNDAY},
    188      *               {@link Calendar#MONDAY Calendar.MONDAY}, etc.
    189      * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_SHORT},
    190      *               {@link #LENGTH_MEDIUM}, or {@link #LENGTH_SHORTEST}.
    191      *               Note that in most languages, {@link #LENGTH_SHORT}
    192      *               will return the same as {@link #LENGTH_MEDIUM}.
    193      *               Undefined lengths will return {@link #LENGTH_MEDIUM}
    194      *               but may return something different in the future.
    195      * @throws IndexOutOfBoundsException if the dayOfWeek is out of bounds.
    196      * @deprecated Use {@link java.text.SimpleDateFormat} instead.
    197      */
    198     @Deprecated
    199     public static String getDayOfWeekString(int dayOfWeek, int abbrev) {
    200         LocaleData d = LocaleData.get(Locale.getDefault());
    201         String[] names;
    202         switch (abbrev) {
    203             case LENGTH_LONG:       names = d.longWeekdayNames;  break;
    204             case LENGTH_MEDIUM:     names = d.shortWeekdayNames; break;
    205             case LENGTH_SHORT:      names = d.shortWeekdayNames; break; // TODO
    206             case LENGTH_SHORTER:    names = d.shortWeekdayNames; break; // TODO
    207             case LENGTH_SHORTEST:   names = d.tinyWeekdayNames;  break;
    208             default:                names = d.shortWeekdayNames; break;
    209         }
    210         return names[dayOfWeek];
    211     }
    212 
    213     /**
    214      * Return a localized string for AM or PM.
    215      * @param ampm Either {@link Calendar#AM Calendar.AM} or {@link Calendar#PM Calendar.PM}.
    216      * @throws IndexOutOfBoundsException if the ampm is out of bounds.
    217      * @return Localized version of "AM" or "PM".
    218      * @deprecated Use {@link java.text.SimpleDateFormat} instead.
    219      */
    220     @Deprecated
    221     public static String getAMPMString(int ampm) {
    222         return LocaleData.get(Locale.getDefault()).amPm[ampm - Calendar.AM];
    223     }
    224 
    225     /**
    226      * Return a localized string for the month of the year.
    227      * @param month One of {@link Calendar#JANUARY Calendar.JANUARY},
    228      *               {@link Calendar#FEBRUARY Calendar.FEBRUARY}, etc.
    229      * @param abbrev One of {@link #LENGTH_LONG}, {@link #LENGTH_MEDIUM},
    230      *               or {@link #LENGTH_SHORTEST}.
    231      *               Undefined lengths will return {@link #LENGTH_MEDIUM}
    232      *               but may return something different in the future.
    233      * @return Localized month of the year.
    234      * @deprecated Use {@link java.text.SimpleDateFormat} instead.
    235      */
    236     @Deprecated
    237     public static String getMonthString(int month, int abbrev) {
    238         LocaleData d = LocaleData.get(Locale.getDefault());
    239         String[] names;
    240         switch (abbrev) {
    241             case LENGTH_LONG:       names = d.longMonthNames;  break;
    242             case LENGTH_MEDIUM:     names = d.shortMonthNames; break;
    243             case LENGTH_SHORT:      names = d.shortMonthNames; break;
    244             case LENGTH_SHORTER:    names = d.shortMonthNames; break;
    245             case LENGTH_SHORTEST:   names = d.tinyMonthNames;  break;
    246             default:                names = d.shortMonthNames; break;
    247         }
    248         return names[month];
    249     }
    250 
    251     /**
    252      * Returns a string describing the elapsed time since startTime.
    253      * <p>
    254      * The minimum timespan to report is set to {@link #MINUTE_IN_MILLIS}.
    255      * @param startTime some time in the past.
    256      * @return a String object containing the elapsed time.
    257      * @see #getRelativeTimeSpanString(long, long, long)
    258      */
    259     public static CharSequence getRelativeTimeSpanString(long startTime) {
    260         return getRelativeTimeSpanString(startTime, System.currentTimeMillis(), MINUTE_IN_MILLIS);
    261     }
    262 
    263     /**
    264      * Returns a string describing 'time' as a time relative to 'now'.
    265      * <p>
    266      * Time spans in the past are formatted like "42 minutes ago".
    267      * Time spans in the future are formatted like "In 42 minutes".
    268      *
    269      * @param time the time to describe, in milliseconds
    270      * @param now the current time in milliseconds
    271      * @param minResolution the minimum timespan to report. For example, a time 3 seconds in the
    272      *     past will be reported as "0 minutes ago" if this is set to MINUTE_IN_MILLIS. Pass one of
    273      *     0, MINUTE_IN_MILLIS, HOUR_IN_MILLIS, DAY_IN_MILLIS, WEEK_IN_MILLIS
    274      */
    275     public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution) {
    276         int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_ABBREV_MONTH;
    277         return getRelativeTimeSpanString(time, now, minResolution, flags);
    278     }
    279 
    280     /**
    281      * Returns a string describing 'time' as a time relative to 'now'.
    282      * <p>
    283      * Time spans in the past are formatted like "42 minutes ago". Time spans in
    284      * the future are formatted like "In 42 minutes".
    285      * <p>
    286      * Can use {@link #FORMAT_ABBREV_RELATIVE} flag to use abbreviated relative
    287      * times, like "42 mins ago".
    288      *
    289      * @param time the time to describe, in milliseconds
    290      * @param now the current time in milliseconds
    291      * @param minResolution the minimum timespan to report. For example, a time
    292      *            3 seconds in the past will be reported as "0 minutes ago" if
    293      *            this is set to MINUTE_IN_MILLIS. Pass one of 0,
    294      *            MINUTE_IN_MILLIS, HOUR_IN_MILLIS, DAY_IN_MILLIS,
    295      *            WEEK_IN_MILLIS
    296      * @param flags a bit mask of formatting options, such as
    297      *            {@link #FORMAT_NUMERIC_DATE} or
    298      *            {@link #FORMAT_ABBREV_RELATIVE}
    299      */
    300     public static CharSequence getRelativeTimeSpanString(long time, long now, long minResolution,
    301             int flags) {
    302         return RelativeDateTimeFormatter.getRelativeTimeSpanString(Locale.getDefault(),
    303                 TimeZone.getDefault(), time, now, minResolution, flags);
    304     }
    305 
    306     /**
    307      * Return string describing the elapsed time since startTime formatted like
    308      * "[relative time/date], [time]".
    309      * <p>
    310      * Example output strings for the US date format.
    311      * <ul>
    312      * <li>3 min. ago, 10:15 AM</li>
    313      * <li>Yesterday, 12:20 PM</li>
    314      * <li>Dec 12, 4:12 AM</li>
    315      * <li>11/14/2007, 8:20 AM</li>
    316      * </ul>
    317      *
    318      * @param time some time in the past.
    319      * @param minResolution the minimum elapsed time (in milliseconds) to report
    320      *            when showing relative times. For example, a time 3 seconds in
    321      *            the past will be reported as "0 minutes ago" if this is set to
    322      *            {@link #MINUTE_IN_MILLIS}.
    323      * @param transitionResolution the elapsed time (in milliseconds) at which
    324      *            to stop reporting relative measurements. Elapsed times greater
    325      *            than this resolution will default to normal date formatting.
    326      *            For example, will transition from "7 days ago" to "Dec 12"
    327      *            when using {@link #WEEK_IN_MILLIS}.
    328      */
    329     public static CharSequence getRelativeDateTimeString(Context c, long time, long minResolution,
    330             long transitionResolution, int flags) {
    331         // Same reason as in formatDateRange() to explicitly indicate 12- or 24-hour format.
    332         if ((flags & (FORMAT_SHOW_TIME | FORMAT_12HOUR | FORMAT_24HOUR)) == FORMAT_SHOW_TIME) {
    333             flags |= DateFormat.is24HourFormat(c) ? FORMAT_24HOUR : FORMAT_12HOUR;
    334         }
    335 
    336         return RelativeDateTimeFormatter.getRelativeDateTimeString(Locale.getDefault(),
    337                 TimeZone.getDefault(), time, System.currentTimeMillis(), minResolution,
    338                 transitionResolution, flags);
    339     }
    340 
    341     private static void initFormatStrings() {
    342         synchronized (sLock) {
    343             initFormatStringsLocked();
    344         }
    345     }
    346 
    347     private static void initFormatStringsLocked() {
    348         Resources r = Resources.getSystem();
    349         Configuration cfg = r.getConfiguration();
    350         if (sLastConfig == null || !sLastConfig.equals(cfg)) {
    351             sLastConfig = cfg;
    352             sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss);
    353             sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss);
    354         }
    355     }
    356 
    357     /**
    358      * Returns the given duration in a human-friendly format. For example,
    359      * "4 minutes" or "1 second". Returns only the largest meaningful unit of time,
    360      * from seconds up to hours.
    361      *
    362      * @hide
    363      */
    364     public static CharSequence formatDuration(long millis) {
    365         return formatDuration(millis, LENGTH_LONG);
    366     }
    367 
    368     /**
    369      * Returns the given duration in a human-friendly format. For example,
    370      * "4 minutes" or "1 second". Returns only the largest meaningful unit of time,
    371      * from seconds up to hours.
    372      * <p>
    373      * You can use abbrev to specify a preference for abbreviations (but note that some
    374      * locales may not have abbreviations). Use LENGTH_LONG for the full spelling (e.g. "2 hours"),
    375      * LENGTH_SHORT for the abbreviated spelling if available (e.g. "2 hr"), and LENGTH_SHORTEST for
    376      * the briefest form available (e.g. "2h").
    377      * @hide
    378      */
    379     public static CharSequence formatDuration(long millis, int abbrev) {
    380         final FormatWidth width;
    381         switch (abbrev) {
    382             case LENGTH_LONG:
    383                 width = FormatWidth.WIDE;
    384                 break;
    385             case LENGTH_SHORT:
    386             case LENGTH_SHORTER:
    387             case LENGTH_MEDIUM:
    388                 width = FormatWidth.SHORT;
    389                 break;
    390             case LENGTH_SHORTEST:
    391                 width = FormatWidth.NARROW;
    392                 break;
    393             default:
    394                 width = FormatWidth.WIDE;
    395         }
    396         final MeasureFormat formatter = MeasureFormat.getInstance(Locale.getDefault(), width);
    397         if (millis >= HOUR_IN_MILLIS) {
    398             final int hours = (int) ((millis + 1800000) / HOUR_IN_MILLIS);
    399             return formatter.format(new Measure(hours, MeasureUnit.HOUR));
    400         } else if (millis >= MINUTE_IN_MILLIS) {
    401             final int minutes = (int) ((millis + 30000) / MINUTE_IN_MILLIS);
    402             return formatter.format(new Measure(minutes, MeasureUnit.MINUTE));
    403         } else {
    404             final int seconds = (int) ((millis + 500) / SECOND_IN_MILLIS);
    405             return formatter.format(new Measure(seconds, MeasureUnit.SECOND));
    406         }
    407     }
    408 
    409     /**
    410      * Formats an elapsed time in the form "MM:SS" or "H:MM:SS"
    411      * for display on the call-in-progress screen.
    412      * @param elapsedSeconds the elapsed time in seconds.
    413      */
    414     public static String formatElapsedTime(long elapsedSeconds) {
    415         return formatElapsedTime(null, elapsedSeconds);
    416     }
    417 
    418     /**
    419      * Formats an elapsed time in a format like "MM:SS" or "H:MM:SS" (using a form
    420      * suited to the current locale), similar to that used on the call-in-progress
    421      * screen.
    422      *
    423      * @param recycle {@link StringBuilder} to recycle, or null to use a temporary one.
    424      * @param elapsedSeconds the elapsed time in seconds.
    425      */
    426     public static String formatElapsedTime(StringBuilder recycle, long elapsedSeconds) {
    427         // Break the elapsed seconds into hours, minutes, and seconds.
    428         long hours = 0;
    429         long minutes = 0;
    430         long seconds = 0;
    431         if (elapsedSeconds >= 3600) {
    432             hours = elapsedSeconds / 3600;
    433             elapsedSeconds -= hours * 3600;
    434         }
    435         if (elapsedSeconds >= 60) {
    436             minutes = elapsedSeconds / 60;
    437             elapsedSeconds -= minutes * 60;
    438         }
    439         seconds = elapsedSeconds;
    440 
    441         // Create a StringBuilder if we weren't given one to recycle.
    442         // TODO: if we cared, we could have a thread-local temporary StringBuilder.
    443         StringBuilder sb = recycle;
    444         if (sb == null) {
    445             sb = new StringBuilder(8);
    446         } else {
    447             sb.setLength(0);
    448         }
    449 
    450         // Format the broken-down time in a locale-appropriate way.
    451         // TODO: use icu4c when http://unicode.org/cldr/trac/ticket/3407 is fixed.
    452         Formatter f = new Formatter(sb, Locale.getDefault());
    453         initFormatStrings();
    454         if (hours > 0) {
    455             return f.format(sElapsedFormatHMMSS, hours, minutes, seconds).toString();
    456         } else {
    457             return f.format(sElapsedFormatMMSS, minutes, seconds).toString();
    458         }
    459     }
    460 
    461     /**
    462      * Format a date / time such that if the then is on the same day as now, it shows
    463      * just the time and if it's a different day, it shows just the date.
    464      *
    465      * <p>The parameters dateFormat and timeFormat should each be one of
    466      * {@link java.text.DateFormat#DEFAULT},
    467      * {@link java.text.DateFormat#FULL},
    468      * {@link java.text.DateFormat#LONG},
    469      * {@link java.text.DateFormat#MEDIUM}
    470      * or
    471      * {@link java.text.DateFormat#SHORT}
    472      *
    473      * @param then the date to format
    474      * @param now the base time
    475      * @param dateStyle how to format the date portion.
    476      * @param timeStyle how to format the time portion.
    477      */
    478     public static final CharSequence formatSameDayTime(long then, long now,
    479             int dateStyle, int timeStyle) {
    480         Calendar thenCal = new GregorianCalendar();
    481         thenCal.setTimeInMillis(then);
    482         Date thenDate = thenCal.getTime();
    483         Calendar nowCal = new GregorianCalendar();
    484         nowCal.setTimeInMillis(now);
    485 
    486         java.text.DateFormat f;
    487 
    488         if (thenCal.get(Calendar.YEAR) == nowCal.get(Calendar.YEAR)
    489                 && thenCal.get(Calendar.MONTH) == nowCal.get(Calendar.MONTH)
    490                 && thenCal.get(Calendar.DAY_OF_MONTH) == nowCal.get(Calendar.DAY_OF_MONTH)) {
    491             f = java.text.DateFormat.getTimeInstance(timeStyle);
    492         } else {
    493             f = java.text.DateFormat.getDateInstance(dateStyle);
    494         }
    495         return f.format(thenDate);
    496     }
    497 
    498     /**
    499      * @return true if the supplied when is today else false
    500      */
    501     public static boolean isToday(long when) {
    502         Time time = new Time();
    503         time.set(when);
    504 
    505         int thenYear = time.year;
    506         int thenMonth = time.month;
    507         int thenMonthDay = time.monthDay;
    508 
    509         time.set(System.currentTimeMillis());
    510         return (thenYear == time.year)
    511                 && (thenMonth == time.month)
    512                 && (thenMonthDay == time.monthDay);
    513     }
    514 
    515     /**
    516      * Formats a date or a time range according to the local conventions.
    517      * <p>
    518      * Note that this is a convenience method. Using it involves creating an
    519      * internal {@link java.util.Formatter} instance on-the-fly, which is
    520      * somewhat costly in terms of memory and time. This is probably acceptable
    521      * if you use the method only rarely, but if you rely on it for formatting a
    522      * large number of dates, consider creating and reusing your own
    523      * {@link java.util.Formatter} instance and use the version of
    524      * {@link #formatDateRange(Context, long, long, int) formatDateRange}
    525      * that takes a {@link java.util.Formatter}.
    526      *
    527      * @param context the context is required only if the time is shown
    528      * @param startMillis the start time in UTC milliseconds
    529      * @param endMillis the end time in UTC milliseconds
    530      * @param flags a bit mask of options See
    531      * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}
    532      * @return a string containing the formatted date/time range.
    533      */
    534     public static String formatDateRange(Context context, long startMillis,
    535             long endMillis, int flags) {
    536         Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault());
    537         return formatDateRange(context, f, startMillis, endMillis, flags).toString();
    538     }
    539 
    540     /**
    541      * Formats a date or a time range according to the local conventions.
    542      * <p>
    543      * Note that this is a convenience method for formatting the date or
    544      * time range in the local time zone. If you want to specify the time
    545      * zone please use
    546      * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}.
    547      *
    548      * @param context the context is required only if the time is shown
    549      * @param formatter the Formatter used for formatting the date range.
    550      * Note: be sure to call setLength(0) on StringBuilder passed to
    551      * the Formatter constructor unless you want the results to accumulate.
    552      * @param startMillis the start time in UTC milliseconds
    553      * @param endMillis the end time in UTC milliseconds
    554      * @param flags a bit mask of options See
    555      * {@link #formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}
    556      * @return a string containing the formatted date/time range.
    557      */
    558     public static Formatter formatDateRange(Context context, Formatter formatter, long startMillis,
    559             long endMillis, int flags) {
    560         return formatDateRange(context, formatter, startMillis, endMillis, flags, null);
    561     }
    562 
    563     /**
    564      * Formats a date or a time range according to the local conventions.
    565      *
    566      * <p>
    567      * Example output strings (date formats in these examples are shown using
    568      * the US date format convention but that may change depending on the
    569      * local settings):
    570      * <ul>
    571      *   <li>10:15am</li>
    572      *   <li>3:00pm - 4:00pm</li>
    573      *   <li>3pm - 4pm</li>
    574      *   <li>3PM - 4PM</li>
    575      *   <li>08:00 - 17:00</li>
    576      *   <li>Oct 9</li>
    577      *   <li>Tue, Oct 9</li>
    578      *   <li>October 9, 2007</li>
    579      *   <li>Oct 9 - 10</li>
    580      *   <li>Oct 9 - 10, 2007</li>
    581      *   <li>Oct 28 - Nov 3, 2007</li>
    582      *   <li>Dec 31, 2007 - Jan 1, 2008</li>
    583      *   <li>Oct 9, 8:00am - Oct 10, 5:00pm</li>
    584      *   <li>12/31/2007 - 01/01/2008</li>
    585      * </ul>
    586      *
    587      * <p>
    588      * The flags argument is a bitmask of options from the following list:
    589      *
    590      * <ul>
    591      *   <li>FORMAT_SHOW_TIME</li>
    592      *   <li>FORMAT_SHOW_WEEKDAY</li>
    593      *   <li>FORMAT_SHOW_YEAR</li>
    594      *   <li>FORMAT_SHOW_DATE</li>
    595      *   <li>FORMAT_NO_MONTH_DAY</li>
    596      *   <li>FORMAT_12HOUR</li>
    597      *   <li>FORMAT_24HOUR</li>
    598      *   <li>FORMAT_CAP_AMPM</li>
    599      *   <li>FORMAT_NO_NOON</li>
    600      *   <li>FORMAT_CAP_NOON</li>
    601      *   <li>FORMAT_NO_MIDNIGHT</li>
    602      *   <li>FORMAT_CAP_MIDNIGHT</li>
    603      *   <li>FORMAT_UTC</li>
    604      *   <li>FORMAT_ABBREV_TIME</li>
    605      *   <li>FORMAT_ABBREV_WEEKDAY</li>
    606      *   <li>FORMAT_ABBREV_MONTH</li>
    607      *   <li>FORMAT_ABBREV_ALL</li>
    608      *   <li>FORMAT_NUMERIC_DATE</li>
    609      * </ul>
    610      *
    611      * <p>
    612      * If FORMAT_SHOW_TIME is set, the time is shown as part of the date range.
    613      * If the start and end time are the same, then just the start time is
    614      * shown.
    615      *
    616      * <p>
    617      * If FORMAT_SHOW_WEEKDAY is set, then the weekday is shown.
    618      *
    619      * <p>
    620      * If FORMAT_SHOW_YEAR is set, then the year is always shown.
    621      * If FORMAT_SHOW_YEAR is not set, then the year
    622      * is shown only if it is different from the current year, or if the start
    623      * and end dates fall on different years.
    624      *
    625      * <p>
    626      * Normally the date is shown unless the start and end day are the same.
    627      * If FORMAT_SHOW_DATE is set, then the date is always shown, even for
    628      * same day ranges.
    629      *
    630      * <p>
    631      * If FORMAT_NO_MONTH_DAY is set, then if the date is shown, just the
    632      * month name will be shown, not the day of the month.  For example,
    633      * "January, 2008" instead of "January 6 - 12, 2008".
    634      *
    635      * <p>
    636      * If FORMAT_CAP_AMPM is set and 12-hour time is used, then the "AM"
    637      * and "PM" are capitalized.  You should not use this flag
    638      * because in some locales these terms cannot be capitalized, and in
    639      * many others it doesn't make sense to do so even though it is possible.
    640      *
    641      * <p>
    642      * If FORMAT_NO_NOON is set and 12-hour time is used, then "12pm" is
    643      * shown instead of "noon".
    644      *
    645      * <p>
    646      * If FORMAT_CAP_NOON is set and 12-hour time is used, then "Noon" is
    647      * shown instead of "noon".  You should probably not use this flag
    648      * because in many locales it will not make sense to capitalize
    649      * the term.
    650      *
    651      * <p>
    652      * If FORMAT_NO_MIDNIGHT is set and 12-hour time is used, then "12am" is
    653      * shown instead of "midnight".
    654      *
    655      * <p>
    656      * If FORMAT_CAP_MIDNIGHT is set and 12-hour time is used, then "Midnight"
    657      * is shown instead of "midnight".  You should probably not use this
    658      * flag because in many locales it will not make sense to capitalize
    659      * the term.
    660      *
    661      * <p>
    662      * If FORMAT_12HOUR is set and the time is shown, then the time is
    663      * shown in the 12-hour time format. You should not normally set this.
    664      * Instead, let the time format be chosen automatically according to the
    665      * system settings. If both FORMAT_12HOUR and FORMAT_24HOUR are set, then
    666      * FORMAT_24HOUR takes precedence.
    667      *
    668      * <p>
    669      * If FORMAT_24HOUR is set and the time is shown, then the time is
    670      * shown in the 24-hour time format. You should not normally set this.
    671      * Instead, let the time format be chosen automatically according to the
    672      * system settings. If both FORMAT_12HOUR and FORMAT_24HOUR are set, then
    673      * FORMAT_24HOUR takes precedence.
    674      *
    675      * <p>
    676      * If FORMAT_UTC is set, then the UTC time zone is used for the start
    677      * and end milliseconds unless a time zone is specified. If a time zone
    678      * is specified it will be used regardless of the FORMAT_UTC flag.
    679      *
    680      * <p>
    681      * If FORMAT_ABBREV_TIME is set and 12-hour time format is used, then the
    682      * start and end times (if shown) are abbreviated by not showing the minutes
    683      * if they are zero.  For example, instead of "3:00pm" the time would be
    684      * abbreviated to "3pm".
    685      *
    686      * <p>
    687      * If FORMAT_ABBREV_WEEKDAY is set, then the weekday (if shown) is
    688      * abbreviated to a 3-letter string.
    689      *
    690      * <p>
    691      * If FORMAT_ABBREV_MONTH is set, then the month (if shown) is abbreviated
    692      * to a 3-letter string.
    693      *
    694      * <p>
    695      * If FORMAT_ABBREV_ALL is set, then the weekday and the month (if shown)
    696      * are abbreviated to 3-letter strings.
    697      *
    698      * <p>
    699      * If FORMAT_NUMERIC_DATE is set, then the date is shown in numeric format
    700      * instead of using the name of the month.  For example, "12/31/2008"
    701      * instead of "December 31, 2008".
    702      *
    703      * <p>
    704      * If the end date ends at 12:00am at the beginning of a day, it is
    705      * formatted as the end of the previous day in two scenarios:
    706      * <ul>
    707      *   <li>For single day events. This results in "8pm - midnight" instead of
    708      *       "Nov 10, 8pm - Nov 11, 12am".</li>
    709      *   <li>When the time is not displayed. This results in "Nov 10 - 11" for
    710      *       an event with a start date of Nov 10 and an end date of Nov 12 at
    711      *       00:00.</li>
    712      * </ul>
    713      *
    714      * @param context the context is required only if the time is shown
    715      * @param formatter the Formatter used for formatting the date range.
    716      * Note: be sure to call setLength(0) on StringBuilder passed to
    717      * the Formatter constructor unless you want the results to accumulate.
    718      * @param startMillis the start time in UTC milliseconds
    719      * @param endMillis the end time in UTC milliseconds
    720      * @param flags a bit mask of options
    721      * @param timeZone the time zone to compute the string in. Use null for local
    722      * or if the FORMAT_UTC flag is being used.
    723      *
    724      * @return the formatter with the formatted date/time range appended to the string buffer.
    725      */
    726     public static Formatter formatDateRange(Context context, Formatter formatter, long startMillis,
    727                                             long endMillis, int flags, String timeZone) {
    728         // If we're being asked to format a time without being explicitly told whether to use
    729         // the 12- or 24-hour clock, icu4c will fall back to the locale's preferred 12/24 format,
    730         // but we want to fall back to the user's preference.
    731         if ((flags & (FORMAT_SHOW_TIME | FORMAT_12HOUR | FORMAT_24HOUR)) == FORMAT_SHOW_TIME) {
    732             flags |= DateFormat.is24HourFormat(context) ? FORMAT_24HOUR : FORMAT_12HOUR;
    733         }
    734 
    735         String range = DateIntervalFormat.formatDateRange(startMillis, endMillis, flags, timeZone);
    736         try {
    737             formatter.out().append(range);
    738         } catch (IOException impossible) {
    739             throw new AssertionError(impossible);
    740         }
    741         return formatter;
    742     }
    743 
    744     /**
    745      * Formats a date or a time according to the local conventions. There are
    746      * lots of options that allow the caller to control, for example, if the
    747      * time is shown, if the day of the week is shown, if the month name is
    748      * abbreviated, if noon is shown instead of 12pm, and so on. For the
    749      * complete list of options, see the documentation for
    750      * {@link #formatDateRange}.
    751      * <p>
    752      * Example output strings (date formats in these examples are shown using
    753      * the US date format convention but that may change depending on the
    754      * local settings):
    755      * <ul>
    756      *   <li>10:15am</li>
    757      *   <li>3:00pm</li>
    758      *   <li>3pm</li>
    759      *   <li>3PM</li>
    760      *   <li>08:00</li>
    761      *   <li>17:00</li>
    762      *   <li>noon</li>
    763      *   <li>Noon</li>
    764      *   <li>midnight</li>
    765      *   <li>Midnight</li>
    766      *   <li>Oct 31</li>
    767      *   <li>Oct 31, 2007</li>
    768      *   <li>October 31, 2007</li>
    769      *   <li>10am, Oct 31</li>
    770      *   <li>17:00, Oct 31</li>
    771      *   <li>Wed</li>
    772      *   <li>Wednesday</li>
    773      *   <li>10am, Wed, Oct 31</li>
    774      *   <li>Wed, Oct 31</li>
    775      *   <li>Wednesday, Oct 31</li>
    776      *   <li>Wed, Oct 31, 2007</li>
    777      *   <li>Wed, October 31</li>
    778      *   <li>10/31/2007</li>
    779      * </ul>
    780      *
    781      * @param context the context is required only if the time is shown
    782      * @param millis a point in time in UTC milliseconds
    783      * @param flags a bit mask of formatting options
    784      * @return a string containing the formatted date/time.
    785      */
    786     public static String formatDateTime(Context context, long millis, int flags) {
    787         return formatDateRange(context, millis, millis, flags);
    788     }
    789 
    790     /**
    791      * @return a relative time string to display the time expressed by millis.  Times
    792      * are counted starting at midnight, which means that assuming that the current
    793      * time is March 31st, 0:30:
    794      * <ul>
    795      *   <li>"millis=0:10 today" will be displayed as "0:10"</li>
    796      *   <li>"millis=11:30pm the day before" will be displayed as "Mar 30"</li>
    797      * </ul>
    798      * If the given millis is in a different year, then the full date is
    799      * returned in numeric format (e.g., "10/12/2008").
    800      *
    801      * @param withPreposition If true, the string returned will include the correct
    802      * preposition ("at 9:20am", "on 10/12/2008" or "on May 29").
    803      */
    804     public static CharSequence getRelativeTimeSpanString(Context c, long millis,
    805             boolean withPreposition) {
    806 
    807         String result;
    808         long now = System.currentTimeMillis();
    809         long span = Math.abs(now - millis);
    810 
    811         synchronized (DateUtils.class) {
    812             if (sNowTime == null) {
    813                 sNowTime = new Time();
    814             }
    815 
    816             if (sThenTime == null) {
    817                 sThenTime = new Time();
    818             }
    819 
    820             sNowTime.set(now);
    821             sThenTime.set(millis);
    822 
    823             int prepositionId;
    824             if (span < DAY_IN_MILLIS && sNowTime.weekDay == sThenTime.weekDay) {
    825                 // Same day
    826                 int flags = FORMAT_SHOW_TIME;
    827                 result = formatDateRange(c, millis, millis, flags);
    828                 prepositionId = R.string.preposition_for_time;
    829             } else if (sNowTime.year != sThenTime.year) {
    830                 // Different years
    831                 int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
    832                 result = formatDateRange(c, millis, millis, flags);
    833 
    834                 // This is a date (like "10/31/2008" so use the date preposition)
    835                 prepositionId = R.string.preposition_for_date;
    836             } else {
    837                 // Default
    838                 int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH;
    839                 result = formatDateRange(c, millis, millis, flags);
    840                 prepositionId = R.string.preposition_for_date;
    841             }
    842             if (withPreposition) {
    843                 Resources res = c.getResources();
    844                 result = res.getString(prepositionId, result);
    845             }
    846         }
    847         return result;
    848     }
    849 
    850     /**
    851      * Convenience function to return relative time string without preposition.
    852      * @param c context for resources
    853      * @param millis time in milliseconds
    854      * @return {@link CharSequence} containing relative time.
    855      * @see #getRelativeTimeSpanString(Context, long, boolean)
    856      */
    857     public static CharSequence getRelativeTimeSpanString(Context c, long millis) {
    858         return getRelativeTimeSpanString(c, millis, false /* no preposition */);
    859     }
    860 
    861     private static Time sNowTime;
    862     private static Time sThenTime;
    863 }
    864