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