DateUtilsBridge.java revision a40d2447b6f516116135ee7f126579771ba98a2c
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
17package libcore.icu;
18
19import com.ibm.icu.impl.JavaTimeZone;
20import com.ibm.icu.util.Calendar;
21import com.ibm.icu.util.GregorianCalendar;
22import com.ibm.icu.util.ULocale;
23
24/**
25 * Common methods and constants for the various ICU formatters used to support
26 * android.text.format.DateUtils.
27 */
28public final class DateUtilsBridge {
29  // These are all public API in DateUtils. There are others, but they're either for use with
30  // other methods (like FORMAT_ABBREV_RELATIVE), don't internationalize (like FORMAT_CAP_AMPM),
31  // or have never been implemented anyway.
32  public static final int FORMAT_SHOW_TIME       = 0x00001;
33  public static final int FORMAT_SHOW_WEEKDAY    = 0x00002;
34  public static final int FORMAT_SHOW_YEAR       = 0x00004;
35  public static final int FORMAT_NO_YEAR         = 0x00008;
36  public static final int FORMAT_SHOW_DATE       = 0x00010;
37  public static final int FORMAT_NO_MONTH_DAY    = 0x00020;
38  public static final int FORMAT_12HOUR          = 0x00040;
39  public static final int FORMAT_24HOUR          = 0x00080;
40  public static final int FORMAT_UTC             = 0x02000;
41  public static final int FORMAT_ABBREV_TIME     = 0x04000;
42  public static final int FORMAT_ABBREV_WEEKDAY  = 0x08000;
43  public static final int FORMAT_ABBREV_MONTH    = 0x10000;
44  public static final int FORMAT_NUMERIC_DATE    = 0x20000;
45  public static final int FORMAT_ABBREV_RELATIVE = 0x40000;
46  public static final int FORMAT_ABBREV_ALL      = 0x80000;
47
48  /**
49   * Creates an immutable ICU timezone backed by the specified libcore timezone data. At the time of
50   * writing the libcore implementation is faster but restricted to 1902 - 2038.
51   * Callers must not modify the {@code tz} after calling this method.
52   */
53  public static com.ibm.icu.util.TimeZone icuTimeZone(java.util.TimeZone tz) {
54    JavaTimeZone javaTimeZone = new JavaTimeZone(tz, null);
55    javaTimeZone.freeze(); // Optimization - allows the timezone to be copied cheaply.
56    return javaTimeZone;
57  }
58
59  public static Calendar createIcuCalendar(com.ibm.icu.util.TimeZone icuTimeZone, ULocale icuLocale,
60      long timeInMillis) {
61    Calendar calendar = new GregorianCalendar(icuTimeZone, icuLocale);
62    calendar.setTimeInMillis(timeInMillis);
63    return calendar;
64  }
65
66  public static String toSkeleton(Calendar calendar, int flags) {
67    return toSkeleton(calendar, calendar, flags);
68  }
69
70  public static String toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags) {
71    if ((flags & FORMAT_ABBREV_ALL) != 0) {
72      flags |= FORMAT_ABBREV_MONTH | FORMAT_ABBREV_TIME | FORMAT_ABBREV_WEEKDAY;
73    }
74
75    String monthPart = "MMMM";
76    if ((flags & FORMAT_NUMERIC_DATE) != 0) {
77      monthPart = "M";
78    } else if ((flags & FORMAT_ABBREV_MONTH) != 0) {
79      monthPart = "MMM";
80    }
81
82    String weekPart = "EEEE";
83    if ((flags & FORMAT_ABBREV_WEEKDAY) != 0) {
84      weekPart = "EEE";
85    }
86
87    String timePart = "j"; // "j" means choose 12 or 24 hour based on current locale.
88    if ((flags & FORMAT_24HOUR) != 0) {
89      timePart = "H";
90    } else if ((flags & FORMAT_12HOUR) != 0) {
91      timePart = "h";
92    }
93
94    // If we've not been asked to abbreviate times, or we're using the 24-hour clock (where it
95    // never makes sense to leave out the minutes), include minutes. This gets us times like
96    // "4 PM" while avoiding times like "16" (for "16:00").
97    if ((flags & FORMAT_ABBREV_TIME) == 0 || (flags & FORMAT_24HOUR) != 0) {
98      timePart += "m";
99    } else {
100      // Otherwise, we're abbreviating a 12-hour time, and should only show the minutes
101      // if they're not both "00".
102      if (!(onTheHour(startCalendar) && onTheHour(endCalendar))) {
103        timePart = timePart + "m";
104      }
105    }
106
107    if (fallOnDifferentDates(startCalendar, endCalendar)) {
108      flags |= FORMAT_SHOW_DATE;
109    }
110
111    if (fallInSameMonth(startCalendar, endCalendar) && (flags & FORMAT_NO_MONTH_DAY) != 0) {
112      flags &= (~FORMAT_SHOW_WEEKDAY);
113      flags &= (~FORMAT_SHOW_TIME);
114    }
115
116    if ((flags & (FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_SHOW_WEEKDAY)) == 0) {
117      flags |= FORMAT_SHOW_DATE;
118    }
119
120    // If we've been asked to show the date, work out whether we think we should show the year.
121    if ((flags & FORMAT_SHOW_DATE) != 0) {
122      if ((flags & FORMAT_SHOW_YEAR) != 0) {
123        // The caller explicitly wants us to show the year.
124      } else if ((flags & FORMAT_NO_YEAR) != 0) {
125        // The caller explicitly doesn't want us to show the year, even if we otherwise would.
126      } else if (!fallInSameYear(startCalendar, endCalendar) || !isThisYear(startCalendar)) {
127        flags |= FORMAT_SHOW_YEAR;
128      }
129    }
130
131    StringBuilder builder = new StringBuilder();
132    if ((flags & (FORMAT_SHOW_DATE | FORMAT_NO_MONTH_DAY)) != 0) {
133      if ((flags & FORMAT_SHOW_YEAR) != 0) {
134        builder.append("y");
135      }
136      builder.append(monthPart);
137      if ((flags & FORMAT_NO_MONTH_DAY) == 0) {
138        builder.append("d");
139      }
140    }
141    if ((flags & FORMAT_SHOW_WEEKDAY) != 0) {
142      builder.append(weekPart);
143    }
144    if ((flags & FORMAT_SHOW_TIME) != 0) {
145      builder.append(timePart);
146    }
147    return builder.toString();
148  }
149
150  public static int dayDistance(Calendar c1, Calendar c2) {
151    return c2.get(Calendar.JULIAN_DAY) - c1.get(Calendar.JULIAN_DAY);
152  }
153
154  private static boolean onTheHour(Calendar c) {
155    return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0;
156  }
157
158  private static boolean fallOnDifferentDates(Calendar c1, Calendar c2) {
159    return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) ||
160        c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) ||
161        c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH);
162  }
163
164  private static boolean fallInSameMonth(Calendar c1, Calendar c2) {
165    return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH);
166  }
167
168  private static boolean fallInSameYear(Calendar c1, Calendar c2) {
169    return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR);
170  }
171
172  private static boolean isThisYear(Calendar c) {
173    Calendar now = (Calendar) c.clone();
174    now.setTimeInMillis(System.currentTimeMillis());
175    return c.get(Calendar.YEAR) == now.get(Calendar.YEAR);
176  }
177}
178