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 libcore.icu; 18 19 import android.icu.impl.JavaTimeZone; 20 import android.icu.util.Calendar; 21 import android.icu.util.GregorianCalendar; 22 import android.icu.util.ULocale; 23 24 /** 25 * Common methods and constants for the various ICU formatters used to support 26 * android.text.format.DateUtils. 27 */ 28 public 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 */ icuTimeZone(java.util.TimeZone tz)53 public static android.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 createIcuCalendar(android.icu.util.TimeZone icuTimeZone, ULocale icuLocale, long timeInMillis)59 public static Calendar createIcuCalendar(android.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 toSkeleton(Calendar calendar, int flags)66 public static String toSkeleton(Calendar calendar, int flags) { 67 return toSkeleton(calendar, calendar, flags); 68 } 69 toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags)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 dayDistance(Calendar c1, Calendar c2)150 public static int dayDistance(Calendar c1, Calendar c2) { 151 return c2.get(Calendar.JULIAN_DAY) - c1.get(Calendar.JULIAN_DAY); 152 } 153 onTheHour(Calendar c)154 private static boolean onTheHour(Calendar c) { 155 return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0; 156 } 157 fallOnDifferentDates(Calendar c1, Calendar c2)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 fallInSameMonth(Calendar c1, Calendar c2)164 private static boolean fallInSameMonth(Calendar c1, Calendar c2) { 165 return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH); 166 } 167 fallInSameYear(Calendar c1, Calendar c2)168 private static boolean fallInSameYear(Calendar c1, Calendar c2) { 169 return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR); 170 } 171 isThisYear(Calendar c)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