• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
getDayOfWeekString(int dayOfWeek, int abbrev)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
getAMPMString(int ampm)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
getMonthString(int month, int abbrev)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      */
getRelativeTimeSpanString(long startTime)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      */
getRelativeTimeSpanString(long time, long now, long minResolution)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      */
getRelativeTimeSpanString(long time, long now, long minResolution, int flags)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      */
getRelativeDateTimeString(Context c, long time, long minResolution, long transitionResolution, int flags)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 
initFormatStrings()341     private static void initFormatStrings() {
342         synchronized (sLock) {
343             initFormatStringsLocked();
344         }
345     }
346 
initFormatStringsLocked()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      */
formatDuration(long millis)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      */
formatDuration(long millis, int abbrev)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      */
formatElapsedTime(long elapsedSeconds)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      */
formatElapsedTime(StringBuilder recycle, long elapsedSeconds)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      */
formatSameDayTime(long then, long now, int dateStyle, int timeStyle)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      */
isToday(long when)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      */
formatDateRange(Context context, long startMillis, long endMillis, int flags)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      */
formatDateRange(Context context, Formatter formatter, long startMillis, long endMillis, int flags)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      */
formatDateRange(Context context, Formatter formatter, long startMillis, long endMillis, int flags, String timeZone)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      */
formatDateTime(Context context, long millis, int flags)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      */
getRelativeTimeSpanString(Context c, long millis, boolean withPreposition)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      */
getRelativeTimeSpanString(Context c, long millis)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