• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 static android.text.format.DateUtils.FORMAT_12HOUR;
20 import static android.text.format.DateUtils.FORMAT_24HOUR;
21 import static android.text.format.DateUtils.FORMAT_ABBREV_ALL;
22 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
23 import static android.text.format.DateUtils.FORMAT_ABBREV_TIME;
24 import static android.text.format.DateUtils.FORMAT_ABBREV_WEEKDAY;
25 import static android.text.format.DateUtils.FORMAT_NO_MONTH_DAY;
26 import static android.text.format.DateUtils.FORMAT_NO_YEAR;
27 import static android.text.format.DateUtils.FORMAT_NUMERIC_DATE;
28 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
29 import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
30 import static android.text.format.DateUtils.FORMAT_SHOW_WEEKDAY;
31 import static android.text.format.DateUtils.FORMAT_SHOW_YEAR;
32 
33 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
34 
35 import android.icu.util.Calendar;
36 import android.icu.util.GregorianCalendar;
37 import android.icu.util.TimeZone;
38 import android.icu.util.ULocale;
39 
40 import com.android.internal.annotations.VisibleForTesting;
41 
42 /**
43  * Common methods and constants for the various ICU formatters used to support {@link
44  * android.text.format.DateUtils}.
45  *
46  * @hide
47  */
48 @VisibleForTesting(visibility = PACKAGE)
49 @android.ravenwood.annotation.RavenwoodKeepWholeClass
50 public final class DateUtilsBridge {
51 
52     /**
53      * Creates an immutable ICU timezone backed by the specified libcore timezone data. At the time
54      * of writing the libcore implementation is faster but restricted to 1902 - 2038. Callers must
55      * not modify the {@code tz} after calling this method.
56      */
icuTimeZone(java.util.TimeZone tz)57     public static TimeZone icuTimeZone(java.util.TimeZone tz) {
58         TimeZone icuTimeZone = TimeZone.getTimeZone(tz.getID());
59         icuTimeZone.freeze(); // Optimization - allows the timezone to be copied cheaply.
60         return icuTimeZone;
61     }
62 
63     /**
64      * Create a GregorianCalendar based on the arguments
65      */
createIcuCalendar(TimeZone icuTimeZone, ULocale icuLocale, long timeInMillis)66     public static Calendar createIcuCalendar(TimeZone icuTimeZone, ULocale icuLocale,
67             long timeInMillis) {
68         Calendar calendar = new GregorianCalendar(icuTimeZone, icuLocale);
69         calendar.setTimeInMillis(timeInMillis);
70         return calendar;
71     }
72 
toSkeleton(Calendar calendar, int flags)73     public static String toSkeleton(Calendar calendar, int flags) {
74         return toSkeleton(calendar, calendar, flags);
75     }
76 
toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags)77     public static String toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags) {
78         if ((flags & FORMAT_ABBREV_ALL) != 0) {
79             flags |= FORMAT_ABBREV_MONTH | FORMAT_ABBREV_TIME | FORMAT_ABBREV_WEEKDAY;
80         }
81 
82         String monthPart = "MMMM";
83         if ((flags & FORMAT_NUMERIC_DATE) != 0) {
84             monthPart = "M";
85         } else if ((flags & FORMAT_ABBREV_MONTH) != 0) {
86             monthPart = "MMM";
87         }
88 
89         String weekPart = "EEEE";
90         if ((flags & FORMAT_ABBREV_WEEKDAY) != 0) {
91             weekPart = "EEE";
92         }
93 
94         String timePart = "j"; // "j" means choose 12 or 24 hour based on current locale.
95         if ((flags & FORMAT_24HOUR) != 0) {
96             timePart = "H";
97         } else if ((flags & FORMAT_12HOUR) != 0) {
98             timePart = "h";
99         }
100 
101         // If we've not been asked to abbreviate times, or we're using the 24-hour clock (where it
102         // never makes sense to leave out the minutes), include minutes. This gets us times like
103         // "4 PM" while avoiding times like "16" (for "16:00").
104         if ((flags & FORMAT_ABBREV_TIME) == 0 || (flags & FORMAT_24HOUR) != 0) {
105             timePart += "m";
106         } else {
107             // Otherwise, we're abbreviating a 12-hour time, and should only show the minutes
108             // if they're not both "00".
109             if (!(onTheHour(startCalendar) && onTheHour(endCalendar))) {
110                 timePart = timePart + "m";
111             }
112         }
113 
114         if (fallOnDifferentDates(startCalendar, endCalendar)) {
115             flags |= FORMAT_SHOW_DATE;
116         }
117 
118         if (fallInSameMonth(startCalendar, endCalendar) && (flags & FORMAT_NO_MONTH_DAY) != 0) {
119             flags &= (~FORMAT_SHOW_WEEKDAY);
120             flags &= (~FORMAT_SHOW_TIME);
121         }
122 
123         if ((flags & (FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_SHOW_WEEKDAY)) == 0) {
124             flags |= FORMAT_SHOW_DATE;
125         }
126 
127         // If we've been asked to show the date, work out whether we think we should show the year.
128         if ((flags & FORMAT_SHOW_DATE) != 0) {
129             if ((flags & FORMAT_SHOW_YEAR) != 0) {
130                 // The caller explicitly wants us to show the year.
131             } else if ((flags & FORMAT_NO_YEAR) != 0) {
132                 // The caller explicitly doesn't want us to show the year, even if we otherwise
133                 // would.
134             } else if (!fallInSameYear(startCalendar, endCalendar) || !isThisYear(startCalendar)) {
135                 flags |= FORMAT_SHOW_YEAR;
136             }
137         }
138 
139         StringBuilder builder = new StringBuilder();
140         if ((flags & (FORMAT_SHOW_DATE | FORMAT_NO_MONTH_DAY)) != 0) {
141             if ((flags & FORMAT_SHOW_YEAR) != 0) {
142                 builder.append("y");
143             }
144             builder.append(monthPart);
145             if ((flags & FORMAT_NO_MONTH_DAY) == 0) {
146                 builder.append("d");
147             }
148         }
149         if ((flags & FORMAT_SHOW_WEEKDAY) != 0) {
150             builder.append(weekPart);
151         }
152         if ((flags & FORMAT_SHOW_TIME) != 0) {
153             builder.append(timePart);
154         }
155         return builder.toString();
156     }
157 
dayDistance(Calendar c1, Calendar c2)158     public static int dayDistance(Calendar c1, Calendar c2) {
159         return c2.get(Calendar.JULIAN_DAY) - c1.get(Calendar.JULIAN_DAY);
160     }
161 
162     /**
163      * Returns whether the argument will be displayed as if it were midnight, using any of the
164      * skeletons provided by {@link #toSkeleton}.
165      */
isDisplayMidnightUsingSkeleton(Calendar c)166     public static boolean isDisplayMidnightUsingSkeleton(Calendar c) {
167         // All the skeletons returned by toSkeleton have minute precision (they may abbreviate
168         // 4:00 PM to 4 PM but will still show the following minute as 4:01 PM).
169         return c.get(Calendar.HOUR_OF_DAY) == 0 && c.get(Calendar.MINUTE) == 0;
170     }
171 
onTheHour(Calendar c)172     private static boolean onTheHour(Calendar c) {
173         return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0;
174     }
175 
fallOnDifferentDates(Calendar c1, Calendar c2)176     private static boolean fallOnDifferentDates(Calendar c1, Calendar c2) {
177         return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR)
178                 || c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH)
179                 || c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH);
180     }
181 
fallInSameMonth(Calendar c1, Calendar c2)182     private static boolean fallInSameMonth(Calendar c1, Calendar c2) {
183         return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH);
184     }
185 
fallInSameYear(Calendar c1, Calendar c2)186     private static boolean fallInSameYear(Calendar c1, Calendar c2) {
187         return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR);
188     }
189 
isThisYear(Calendar c)190     private static boolean isThisYear(Calendar c) {
191         Calendar now = (Calendar) c.clone();
192         now.setTimeInMillis(System.currentTimeMillis());
193         return c.get(Calendar.YEAR) == now.get(Calendar.YEAR);
194     }
195 }
196