Make a copy of DateIntervalFormat from libcore - part 2
Bug: 160606356 Test: atest FrameworksCoreTests:android.text.format Change-Id: I4c4253c707211f2a5ee0a11da107ab1dd6c4fab8
This commit is contained in:
@@ -16,103 +16,111 @@
|
||||
|
||||
package android.text.format;
|
||||
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import static android.text.format.DateUtilsBridge.FORMAT_UTC;
|
||||
|
||||
import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
|
||||
|
||||
import android.icu.util.Calendar;
|
||||
import android.icu.util.ULocale;
|
||||
import android.util.LruCache;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.text.FieldPosition;
|
||||
import java.util.TimeZone;
|
||||
import libcore.util.BasicLruCache;
|
||||
|
||||
import static libcore.icu.DateUtilsBridge.FORMAT_UTC;
|
||||
|
||||
/**
|
||||
* Exposes icu4j's DateIntervalFormat.
|
||||
* A wrapper of {@link android.icu.text.DateIntervalFormat} used by {@link DateUtilsBridge}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@libcore.api.CorePlatformApi
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public final class DateIntervalFormat {
|
||||
|
||||
private static final BasicLruCache<String, android.icu.text.DateIntervalFormat> CACHED_FORMATTERS
|
||||
= new BasicLruCache<>(8);
|
||||
private static final LruCache<String, android.icu.text.DateIntervalFormat> CACHED_FORMATTERS =
|
||||
new LruCache<>(8);
|
||||
|
||||
private DateIntervalFormat() {
|
||||
}
|
||||
|
||||
// This is public DateUtils API in frameworks/base.
|
||||
@UnsupportedAppUsage
|
||||
@libcore.api.CorePlatformApi
|
||||
public static String formatDateRange(long startMs, long endMs, int flags, String olsonId) {
|
||||
if ((flags & FORMAT_UTC) != 0) {
|
||||
olsonId = "UTC";
|
||||
}
|
||||
// We create a java.util.TimeZone here to use libcore's data and libcore's olson ID / pseudo-tz
|
||||
// logic.
|
||||
TimeZone tz = (olsonId != null) ? TimeZone.getTimeZone(olsonId) : TimeZone.getDefault();
|
||||
android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
|
||||
ULocale icuLocale = ULocale.getDefault();
|
||||
return formatDateRange(icuLocale, icuTimeZone, startMs, endMs, flags);
|
||||
}
|
||||
|
||||
// This is our slightly more sensible internal API. (A truly sane replacement would take a
|
||||
// skeleton instead of int flags.)
|
||||
public static String formatDateRange(ULocale icuLocale, android.icu.util.TimeZone icuTimeZone,
|
||||
long startMs, long endMs, int flags) {
|
||||
Calendar startCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, startMs);
|
||||
Calendar endCalendar;
|
||||
if (startMs == endMs) {
|
||||
endCalendar = startCalendar;
|
||||
} else {
|
||||
endCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, endMs);
|
||||
private DateIntervalFormat() {
|
||||
}
|
||||
|
||||
// Special handling when the range ends at midnight:
|
||||
// - If we're not showing times, and the range is non-empty, we fudge the end date so we don't
|
||||
// count the day that's about to start.
|
||||
// - If we are showing times, and the range ends at exactly 00:00 of the day following its start
|
||||
// (which can be thought of as 24:00 the same day), we fudge the end date so we don't show the
|
||||
// dates --- unless the start is anything displayed as 00:00, in which case we include both
|
||||
// dates to disambiguate.
|
||||
// This is not the behavior of icu4j's DateIntervalFormat, but it's the required behavior
|
||||
// of Android's DateUtils.formatDateRange.
|
||||
if (isExactlyMidnight(endCalendar)) {
|
||||
boolean showTime =
|
||||
(flags & DateUtilsBridge.FORMAT_SHOW_TIME) == DateUtilsBridge.FORMAT_SHOW_TIME;
|
||||
boolean endsDayAfterStart = DateUtilsBridge.dayDistance(startCalendar, endCalendar) == 1;
|
||||
if ((!showTime && startMs != endMs)
|
||||
|| (endsDayAfterStart
|
||||
&& !DateUtilsBridge.isDisplayMidnightUsingSkeleton(startCalendar))) {
|
||||
endCalendar.add(Calendar.DAY_OF_MONTH, -1);
|
||||
}
|
||||
/**
|
||||
* Format a date range.
|
||||
*/
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public static String formatDateRange(long startMs, long endMs, int flags, String olsonId) {
|
||||
if ((flags & FORMAT_UTC) != 0) {
|
||||
olsonId = "UTC";
|
||||
}
|
||||
// We create a java.util.TimeZone here to use libcore's data and libcore's olson ID /
|
||||
// pseudo-tz logic.
|
||||
TimeZone tz = (olsonId != null) ? TimeZone.getTimeZone(olsonId) : TimeZone.getDefault();
|
||||
android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
|
||||
ULocale icuLocale = ULocale.getDefault();
|
||||
return formatDateRange(icuLocale, icuTimeZone, startMs, endMs, flags);
|
||||
}
|
||||
|
||||
String skeleton = DateUtilsBridge.toSkeleton(startCalendar, endCalendar, flags);
|
||||
synchronized (CACHED_FORMATTERS) {
|
||||
android.icu.text.DateIntervalFormat formatter =
|
||||
getFormatter(skeleton, icuLocale, icuTimeZone);
|
||||
return formatter.format(startCalendar, endCalendar, new StringBuffer(),
|
||||
new FieldPosition(0)).toString();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Format a date range. This is our slightly more sensible internal API.
|
||||
* A truly sane replacement would take a skeleton instead of int flags.
|
||||
*/
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public static String formatDateRange(ULocale icuLocale, android.icu.util.TimeZone icuTimeZone,
|
||||
long startMs, long endMs, int flags) {
|
||||
Calendar startCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, startMs);
|
||||
Calendar endCalendar;
|
||||
if (startMs == endMs) {
|
||||
endCalendar = startCalendar;
|
||||
} else {
|
||||
endCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, endMs);
|
||||
}
|
||||
|
||||
private static android.icu.text.DateIntervalFormat getFormatter(String skeleton, ULocale locale,
|
||||
android.icu.util.TimeZone icuTimeZone) {
|
||||
String key = skeleton + "\t" + locale + "\t" + icuTimeZone;
|
||||
android.icu.text.DateIntervalFormat formatter = CACHED_FORMATTERS.get(key);
|
||||
if (formatter != null) {
|
||||
return formatter;
|
||||
}
|
||||
formatter = android.icu.text.DateIntervalFormat.getInstance(skeleton, locale);
|
||||
formatter.setTimeZone(icuTimeZone);
|
||||
CACHED_FORMATTERS.put(key, formatter);
|
||||
return formatter;
|
||||
}
|
||||
// Special handling when the range ends at midnight:
|
||||
// - If we're not showing times, and the range is non-empty, we fudge the end date so we
|
||||
// don't count the day that's about to start.
|
||||
// - If we are showing times, and the range ends at exactly 00:00 of the day following
|
||||
// its start (which can be thought of as 24:00 the same day), we fudge the end date so we
|
||||
// don't show the dates --- unless the start is anything displayed as 00:00, in which case
|
||||
// we include both dates to disambiguate.
|
||||
// This is not the behavior of icu4j's DateIntervalFormat, but it's the required behavior
|
||||
// of Android's DateUtils.formatDateRange.
|
||||
if (isExactlyMidnight(endCalendar)) {
|
||||
boolean showTime =
|
||||
(flags & DateUtilsBridge.FORMAT_SHOW_TIME) == DateUtilsBridge.FORMAT_SHOW_TIME;
|
||||
boolean endsDayAfterStart = DateUtilsBridge.dayDistance(startCalendar, endCalendar)
|
||||
== 1;
|
||||
if ((!showTime && startMs != endMs)
|
||||
|| (endsDayAfterStart
|
||||
&& !DateUtilsBridge.isDisplayMidnightUsingSkeleton(startCalendar))) {
|
||||
endCalendar.add(Calendar.DAY_OF_MONTH, -1);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isExactlyMidnight(Calendar c) {
|
||||
return c.get(Calendar.HOUR_OF_DAY) == 0 &&
|
||||
c.get(Calendar.MINUTE) == 0 &&
|
||||
c.get(Calendar.SECOND) == 0 &&
|
||||
c.get(Calendar.MILLISECOND) == 0;
|
||||
}
|
||||
String skeleton = DateUtilsBridge.toSkeleton(startCalendar, endCalendar, flags);
|
||||
synchronized (CACHED_FORMATTERS) {
|
||||
android.icu.text.DateIntervalFormat formatter =
|
||||
getFormatter(skeleton, icuLocale, icuTimeZone);
|
||||
return formatter.format(startCalendar, endCalendar, new StringBuffer(),
|
||||
new FieldPosition(0)).toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static android.icu.text.DateIntervalFormat getFormatter(String skeleton, ULocale locale,
|
||||
android.icu.util.TimeZone icuTimeZone) {
|
||||
String key = skeleton + "\t" + locale + "\t" + icuTimeZone;
|
||||
android.icu.text.DateIntervalFormat formatter = CACHED_FORMATTERS.get(key);
|
||||
if (formatter != null) {
|
||||
return formatter;
|
||||
}
|
||||
formatter = android.icu.text.DateIntervalFormat.getInstance(skeleton, locale);
|
||||
formatter.setTimeZone(icuTimeZone);
|
||||
CACHED_FORMATTERS.put(key, formatter);
|
||||
return formatter;
|
||||
}
|
||||
|
||||
private static boolean isExactlyMidnight(Calendar c) {
|
||||
return c.get(Calendar.HOUR_OF_DAY) == 0
|
||||
&& c.get(Calendar.MINUTE) == 0
|
||||
&& c.get(Calendar.SECOND) == 0
|
||||
&& c.get(Calendar.MILLISECOND) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import android.icu.util.MeasureUnit;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import libcore.icu.DateIntervalFormat;
|
||||
import libcore.icu.LocaleData;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user