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 17 package libcore.icu; 18 19 import android.icu.util.Calendar; 20 import android.icu.util.ULocale; 21 22 import java.text.FieldPosition; 23 import java.util.TimeZone; 24 import libcore.util.BasicLruCache; 25 26 import static libcore.icu.DateUtilsBridge.FORMAT_UTC; 27 28 /** 29 * Exposes icu4j's DateIntervalFormat. 30 */ 31 public final class DateIntervalFormat { 32 33 private static final BasicLruCache<String, android.icu.text.DateIntervalFormat> CACHED_FORMATTERS 34 = new BasicLruCache<>(8); 35 DateIntervalFormat()36 private DateIntervalFormat() { 37 } 38 39 // This is public DateUtils API in frameworks/base. formatDateRange(long startMs, long endMs, int flags, String olsonId)40 public static String formatDateRange(long startMs, long endMs, int flags, String olsonId) { 41 if ((flags & FORMAT_UTC) != 0) { 42 olsonId = "UTC"; 43 } 44 // We create a java.util.TimeZone here to use libcore's data and libcore's olson ID / pseudo-tz 45 // logic. 46 TimeZone tz = (olsonId != null) ? TimeZone.getTimeZone(olsonId) : TimeZone.getDefault(); 47 android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz); 48 ULocale icuLocale = ULocale.getDefault(); 49 return formatDateRange(icuLocale, icuTimeZone, startMs, endMs, flags); 50 } 51 52 // This is our slightly more sensible internal API. (A truly sane replacement would take a 53 // skeleton instead of int flags.) formatDateRange(ULocale icuLocale, android.icu.util.TimeZone icuTimeZone, long startMs, long endMs, int flags)54 public static String formatDateRange(ULocale icuLocale, android.icu.util.TimeZone icuTimeZone, 55 long startMs, long endMs, int flags) { 56 Calendar startCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, startMs); 57 Calendar endCalendar; 58 if (startMs == endMs) { 59 endCalendar = startCalendar; 60 } else { 61 endCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, endMs); 62 } 63 64 boolean endsAtMidnight = isMidnight(endCalendar); 65 66 // If we're not showing the time or the start and end times are on the same day, and the 67 // end time is midnight, fudge the end date so we don't count the day that's about to start. 68 // This is not the behavior of icu4j's DateIntervalFormat, but it's the historical behavior 69 // of Android's DateUtils.formatDateRange. 70 if (startMs != endMs && endsAtMidnight && 71 ((flags & DateUtilsBridge.FORMAT_SHOW_TIME) == 0 72 || DateUtilsBridge.dayDistance(startCalendar, endCalendar) <= 1)) { 73 endCalendar.add(Calendar.DAY_OF_MONTH, -1); 74 } 75 76 String skeleton = DateUtilsBridge.toSkeleton(startCalendar, endCalendar, flags); 77 synchronized (CACHED_FORMATTERS) { 78 android.icu.text.DateIntervalFormat formatter = 79 getFormatter(skeleton, icuLocale, icuTimeZone); 80 return formatter.format(startCalendar, endCalendar, new StringBuffer(), 81 new FieldPosition(0)).toString(); 82 } 83 } 84 getFormatter(String skeleton, ULocale locale, android.icu.util.TimeZone icuTimeZone)85 private static android.icu.text.DateIntervalFormat getFormatter(String skeleton, ULocale locale, 86 android.icu.util.TimeZone icuTimeZone) { 87 String key = skeleton + "\t" + locale + "\t" + icuTimeZone; 88 android.icu.text.DateIntervalFormat formatter = CACHED_FORMATTERS.get(key); 89 if (formatter != null) { 90 return formatter; 91 } 92 formatter = android.icu.text.DateIntervalFormat.getInstance(skeleton, locale); 93 formatter.setTimeZone(icuTimeZone); 94 CACHED_FORMATTERS.put(key, formatter); 95 return formatter; 96 } 97 isMidnight(Calendar c)98 private static boolean isMidnight(Calendar c) { 99 return c.get(Calendar.HOUR_OF_DAY) == 0 && 100 c.get(Calendar.MINUTE) == 0 && 101 c.get(Calendar.SECOND) == 0 && 102 c.get(Calendar.MILLISECOND) == 0; 103 } 104 105 } 106