summaryrefslogtreecommitdiff
path: root/core/java/android/text/format/DateUtilsBridge.java
blob: 92ec9cf6d7362722ff94555816d426acb9eec07d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/*
 * 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);
    }
}