DateIntervalFormat.java revision e8f98b22cbd859c5c2cad9866335128a95adcd81
1/* 2 * Copyright (C) 2013 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 java.util.Calendar; 20import java.util.Locale; 21import java.util.TimeZone; 22 23/** 24 * Exposes icu4c's DateIntervalFormat. 25 */ 26public final class DateIntervalFormat { 27 28 // These are all public API in DateUtils. There are others, but they're either for use with 29 // other methods (like FORMAT_ABBREV_RELATIVE), don't internationalize (like FORMAT_CAP_AMPM), 30 // or have never been implemented anyway (like FORMAT_NO_YEAR). 31 public static final int FORMAT_SHOW_TIME = 0x00001; 32 public static final int FORMAT_SHOW_WEEKDAY = 0x00002; 33 public static final int FORMAT_SHOW_YEAR = 0x00004; 34 public static final int FORMAT_SHOW_DATE = 0x00010; 35 public static final int FORMAT_NO_MONTH_DAY = 0x00020; 36 public static final int FORMAT_12HOUR = 0x00040; 37 public static final int FORMAT_24HOUR = 0x00080; 38 public static final int FORMAT_UTC = 0x02000; 39 public static final int FORMAT_ABBREV_TIME = 0x04000; 40 public static final int FORMAT_ABBREV_WEEKDAY = 0x08000; 41 public static final int FORMAT_ABBREV_MONTH = 0x10000; 42 public static final int FORMAT_NUMERIC_DATE = 0x20000; 43 public static final int FORMAT_ABBREV_ALL = 0x80000; 44 45 // TODO: check whether icu4c's DateIntervalFormat is expensive enough to warrant a native peer. 46 private DateIntervalFormat() { 47 } 48 49 // This is public DateUtils API in frameworks/base. 50 public static String formatDateRange(long startMs, long endMs, int flags, String olsonId) { 51 if ((flags & FORMAT_UTC) != 0) { 52 olsonId = "UTC"; 53 } 54 TimeZone tz = (olsonId != null) ? TimeZone.getTimeZone(olsonId) : TimeZone.getDefault(); 55 return formatDateRange(Locale.getDefault(), tz, startMs, endMs, flags); 56 } 57 58 // This is our slightly more sensible internal API. (A truly sane replacement would take a 59 // skeleton instead of int flags.) 60 public static String formatDateRange(Locale locale, TimeZone tz, long startMs, long endMs, int flags) { 61 String skeleton = toSkeleton(tz, startMs, endMs, flags); 62 return formatDateInterval(skeleton, locale.toString(), tz.getID(), startMs, endMs); 63 } 64 65 private static String toSkeleton(TimeZone tz, long startMs, long endMs, int flags) { 66 Calendar startCalendar = Calendar.getInstance(tz); 67 startCalendar.setTimeInMillis(startMs); 68 69 Calendar endCalendar; 70 if (startMs == endMs) { 71 endCalendar = startCalendar; 72 } else { 73 endCalendar = Calendar.getInstance(tz); 74 endCalendar.setTimeInMillis(endMs); 75 } 76 77 if ((flags & FORMAT_ABBREV_ALL) != 0) { 78 flags |= FORMAT_ABBREV_MONTH | FORMAT_ABBREV_TIME | FORMAT_ABBREV_WEEKDAY; 79 } 80 81 String monthPart = "MMMM"; 82 if ((flags & FORMAT_NUMERIC_DATE) != 0) { 83 monthPart = "M"; 84 } else if ((flags & FORMAT_ABBREV_MONTH) != 0) { 85 monthPart = "MMM"; 86 } 87 88 String weekPart = "EEEE"; 89 if ((flags & FORMAT_ABBREV_WEEKDAY) != 0) { 90 weekPart = "EEE"; 91 } 92 93 String timePart = "j"; // "j" means choose 12 or 24 hour based on current locale. 94 if ((flags & FORMAT_24HOUR) != 0) { 95 timePart = "H"; 96 } else if ((flags & FORMAT_12HOUR) != 0) { 97 timePart = "h"; 98 } 99 100 // If we've not been asked to abbreviate times, or we're using the 24-hour clock (where it 101 // never makes sense to leave out the minutes), include minutes. This gets us times like 102 // "4 PM" while avoiding times like "16" (for "16:00"). 103 if ((flags & FORMAT_ABBREV_TIME) == 0 || (flags & FORMAT_24HOUR) != 0) { 104 timePart += "m"; 105 } else { 106 // Otherwise, we're abbreviating a 12-hour time, and should only show the minutes 107 // if they're not both "00". 108 if (!(onTheHour(startCalendar) && onTheHour(endCalendar))) { 109 timePart = timePart + "m"; 110 } 111 } 112 113 if (fallOnDifferentDates(startCalendar, endCalendar)) { 114 flags |= FORMAT_SHOW_DATE; 115 } 116 117 if (fallInSameMonth(startCalendar, endCalendar) && (flags & FORMAT_NO_MONTH_DAY) != 0) { 118 flags &= (~FORMAT_SHOW_WEEKDAY); 119 flags &= (~FORMAT_SHOW_TIME); 120 } 121 122 if ((flags & (FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_SHOW_WEEKDAY)) == 0) { 123 flags |= FORMAT_SHOW_DATE; 124 } 125 126 StringBuilder builder = new StringBuilder(); 127 if ((flags & (FORMAT_SHOW_DATE | FORMAT_NO_MONTH_DAY)) != 0) { 128 if ((flags & FORMAT_SHOW_YEAR) != 0) { 129 builder.append("y"); 130 } 131 builder.append(monthPart); 132 if ((flags & FORMAT_NO_MONTH_DAY) == 0) { 133 builder.append("d"); 134 } 135 } 136 if ((flags & FORMAT_SHOW_WEEKDAY) != 0) { 137 builder.append(weekPart); 138 } 139 if ((flags & FORMAT_SHOW_TIME) != 0) { 140 builder.append(timePart); 141 } 142 return builder.toString(); 143 } 144 145 private static boolean onTheHour(Calendar c) { 146 return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0; 147 } 148 149 private static boolean fallOnDifferentDates(Calendar c1, Calendar c2) { 150 return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR) || 151 c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH) || 152 c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH); 153 } 154 155 private static boolean fallInSameMonth(Calendar c1, Calendar c2) { 156 return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH); 157 } 158 159 private static native String formatDateInterval(String skeleton, String localeName, String timeZoneName, long fromDate, long toDate); 160} 161