Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 59d12aa8 authored by vichang's avatar vichang Committed by Gerrit Code Review
Browse files

Merge changes from topic "DateUtilsBridge"

* changes:
  Move/Copy some libcore.icu classes to android.text.format
  Copy some libcore.icu classes to android.text.format
parents 6ad5decb 5e348b2a
Loading
Loading
Loading
Loading
+60 −0
Original line number Diff line number Diff line
/*
 * 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 android.icu.text.DateFormat;
import android.icu.text.DateTimePatternGenerator;
import android.icu.text.DisplayContext;
import android.icu.text.SimpleDateFormat;
import android.icu.util.Calendar;
import android.icu.util.ULocale;
import android.util.LruCache;

/**
 * A formatter that outputs a single date/time.
 *
 * @hide
 */
class DateTimeFormat {
    private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();

    static class FormatterCache extends LruCache<String, DateFormat> {
        FormatterCache() {
            super(8);
        }
    }

    private DateTimeFormat() {
    }

    public static String format(ULocale icuLocale, Calendar time, int flags,
            DisplayContext displayContext) {
        String skeleton = DateUtilsBridge.toSkeleton(time, flags);
        String key = skeleton + "\t" + icuLocale + "\t" + time.getTimeZone();
        synchronized (CACHED_FORMATTERS) {
            DateFormat formatter = CACHED_FORMATTERS.get(key);
            if (formatter == null) {
                DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(
                        icuLocale);
                formatter = new SimpleDateFormat(generator.getBestPattern(skeleton), icuLocale);
                CACHED_FORMATTERS.put(key, formatter);
            }
            formatter.setContext(displayContext);
            return formatter.format(time);
        }
    }
}
+0 −1
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import com.android.internal.R;

import libcore.icu.DateIntervalFormat;
import libcore.icu.LocaleData;
import libcore.icu.RelativeDateTimeFormatter;

import java.io.IOException;
import java.util.Calendar;
+198 −0
Original line number Diff line number Diff line
/*
 * 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 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 {
    // These are all public API in DateUtils. There are others, but they're either for use with
    // other methods (like FORMAT_ABBREV_RELATIVE), don't internationalize (like FORMAT_CAP_AMPM),
    // or have never been implemented anyway.
    public static final int FORMAT_SHOW_TIME = 0x00001;
    public static final int FORMAT_SHOW_WEEKDAY = 0x00002;
    public static final int FORMAT_SHOW_YEAR = 0x00004;
    public static final int FORMAT_NO_YEAR = 0x00008;
    public static final int FORMAT_SHOW_DATE = 0x00010;
    public static final int FORMAT_NO_MONTH_DAY = 0x00020;
    public static final int FORMAT_12HOUR = 0x00040;
    public static final int FORMAT_24HOUR = 0x00080;
    public static final int FORMAT_UTC = 0x02000;
    public static final int FORMAT_ABBREV_TIME = 0x04000;
    public static final int FORMAT_ABBREV_WEEKDAY = 0x08000;
    public static final int FORMAT_ABBREV_MONTH = 0x10000;
    public static final int FORMAT_NUMERIC_DATE = 0x20000;
    public static final int FORMAT_ABBREV_RELATIVE = 0x40000;
    public static final int FORMAT_ABBREV_ALL = 0x80000;

    /**
     * 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);
    }
}
+359 −0

File added.

Preview size limit exceeded, changes collapsed.

+762 −0

File added.

Preview size limit exceeded, changes collapsed.