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