/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.text.format; import static android.text.format.DateUtils.FORMAT_12HOUR; import static android.text.format.DateUtils.FORMAT_24HOUR; import static android.text.format.DateUtils.FORMAT_ABBREV_ALL; import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; import static android.text.format.DateUtils.FORMAT_ABBREV_TIME; import static android.text.format.DateUtils.FORMAT_ABBREV_WEEKDAY; import static android.text.format.DateUtils.FORMAT_NO_MONTH_DAY; import static android.text.format.DateUtils.FORMAT_NO_YEAR; import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE; import static android.text.format.DateUtils.FORMAT_SHOW_DATE; import static android.text.format.DateUtils.FORMAT_SHOW_TIME; import static android.text.format.DateUtils.FORMAT_SHOW_WEEKDAY; import static android.text.format.DateUtils.FORMAT_SHOW_YEAR; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import android.icu.util.Calendar; import android.icu.util.GregorianCalendar; import android.icu.util.TimeZone; import android.icu.util.ULocale; import com.android.internal.annotations.VisibleForTesting; /** * Common methods and constants for the various ICU formatters used to support {@link * android.text.format.DateUtils}. * * @hide */ @VisibleForTesting(visibility = PACKAGE) public final class DateUtilsBridge { /** * Creates an immutable ICU timezone backed by the specified libcore timezone data. At the time * of writing the libcore implementation is faster but restricted to 1902 - 2038. Callers must * not modify the {@code tz} after calling this method. */ public static TimeZone icuTimeZone(java.util.TimeZone tz) { TimeZone icuTimeZone = TimeZone.getTimeZone(tz.getID()); icuTimeZone.freeze(); // Optimization - allows the timezone to be copied cheaply. return icuTimeZone; } /** * Create a GregorianCalendar based on the arguments */ public static Calendar createIcuCalendar(TimeZone icuTimeZone, ULocale icuLocale, long timeInMillis) { Calendar calendar = new GregorianCalendar(icuTimeZone, icuLocale); calendar.setTimeInMillis(timeInMillis); return calendar; } public static String toSkeleton(Calendar calendar, int flags) { return toSkeleton(calendar, calendar, flags); } public static String toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags) { if ((flags & FORMAT_ABBREV_ALL) != 0) { flags |= FORMAT_ABBREV_MONTH | FORMAT_ABBREV_TIME | FORMAT_ABBREV_WEEKDAY; } String monthPart = "MMMM"; if ((flags & FORMAT_NUMERIC_DATE) != 0) { monthPart = "M"; } else if ((flags & FORMAT_ABBREV_MONTH) != 0) { monthPart = "MMM"; } String weekPart = "EEEE"; if ((flags & FORMAT_ABBREV_WEEKDAY) != 0) { weekPart = "EEE"; } String timePart = "j"; // "j" means choose 12 or 24 hour based on current locale. if ((flags & FORMAT_24HOUR) != 0) { timePart = "H"; } else if ((flags & FORMAT_12HOUR) != 0) { timePart = "h"; } // If we've not been asked to abbreviate times, or we're using the 24-hour clock (where it // never makes sense to leave out the minutes), include minutes. This gets us times like // "4 PM" while avoiding times like "16" (for "16:00"). if ((flags & FORMAT_ABBREV_TIME) == 0 || (flags & FORMAT_24HOUR) != 0) { timePart += "m"; } else { // Otherwise, we're abbreviating a 12-hour time, and should only show the minutes // if they're not both "00". if (!(onTheHour(startCalendar) && onTheHour(endCalendar))) { timePart = timePart + "m"; } } if (fallOnDifferentDates(startCalendar, endCalendar)) { flags |= FORMAT_SHOW_DATE; } if (fallInSameMonth(startCalendar, endCalendar) && (flags & FORMAT_NO_MONTH_DAY) != 0) { flags &= (~FORMAT_SHOW_WEEKDAY); flags &= (~FORMAT_SHOW_TIME); } if ((flags & (FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_SHOW_WEEKDAY)) == 0) { flags |= FORMAT_SHOW_DATE; } // If we've been asked to show the date, work out whether we think we should show the year. if ((flags & FORMAT_SHOW_DATE) != 0) { if ((flags & FORMAT_SHOW_YEAR) != 0) { // The caller explicitly wants us to show the year. } else if ((flags & FORMAT_NO_YEAR) != 0) { // The caller explicitly doesn't want us to show the year, even if we otherwise // would. } else if (!fallInSameYear(startCalendar, endCalendar) || !isThisYear(startCalendar)) { flags |= FORMAT_SHOW_YEAR; } } StringBuilder builder = new StringBuilder(); if ((flags & (FORMAT_SHOW_DATE | FORMAT_NO_MONTH_DAY)) != 0) { if ((flags & FORMAT_SHOW_YEAR) != 0) { builder.append("y"); } builder.append(monthPart); if ((flags & FORMAT_NO_MONTH_DAY) == 0) { builder.append("d"); } } if ((flags & FORMAT_SHOW_WEEKDAY) != 0) { builder.append(weekPart); } if ((flags & FORMAT_SHOW_TIME) != 0) { builder.append(timePart); } return builder.toString(); } public static int dayDistance(Calendar c1, Calendar c2) { return c2.get(Calendar.JULIAN_DAY) - c1.get(Calendar.JULIAN_DAY); } /** * Returns whether the argument will be displayed as if it were midnight, using any of the * skeletons provided by {@link #toSkeleton}. */ public static boolean isDisplayMidnightUsingSkeleton(Calendar c) { // All the skeletons returned by toSkeleton have minute precision (they may abbreviate // 4:00 PM to 4 PM but will still show the following minute as 4:01 PM). return c.get(Calendar.HOUR_OF_DAY) == 0 && c.get(Calendar.MINUTE) == 0; } private static boolean onTheHour(Calendar c) { return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0; } private static boolean fallOnDifferentDates(Calendar c1, Calendar c2) { return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) || c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) || c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH); } private static boolean fallInSameMonth(Calendar c1, Calendar c2) { return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH); } private static boolean fallInSameYear(Calendar c1, Calendar c2) { return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR); } private static boolean isThisYear(Calendar c) { Calendar now = (Calendar) c.clone(); now.setTimeInMillis(System.currentTimeMillis()); return c.get(Calendar.YEAR) == now.get(Calendar.YEAR); } }