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.compat.annotation.UnsupportedAppUsage; 20 import android.icu.util.Calendar; 21 import android.icu.util.ULocale; 22 23 import java.text.FieldPosition; 24 import java.util.TimeZone; 25 26 import static libcore.icu.DateUtilsBridge.FORMAT_UTC; 27 28 /** 29 * This class is only kept for @UnsupportedAppUsage, and should not used by libcore/frameworks. 30 * 31 * @hide 32 */ 33 public final class DateIntervalFormat { 34 DateIntervalFormat()35 private DateIntervalFormat() { 36 } 37 38 @UnsupportedAppUsage formatDateRange(long startMs, long endMs, int flags, String olsonId)39 public static String formatDateRange(long startMs, long endMs, int flags, String olsonId) { 40 if ((flags & FORMAT_UTC) != 0) { 41 olsonId = "UTC"; 42 } 43 // We create a java.util.TimeZone here to use libcore's data and libcore's olson ID / pseudo-tz 44 // logic. 45 TimeZone tz = (olsonId != null) ? TimeZone.getTimeZone(olsonId) : TimeZone.getDefault(); 46 android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz); 47 ULocale icuLocale = ULocale.getDefault(); 48 return formatDateRange(icuLocale, icuTimeZone, startMs, endMs, flags); 49 } 50 51 // This is our slightly more sensible internal API. (A better replacement would take a 52 // skeleton instead of int flags.) formatDateRange(ULocale icuLocale, android.icu.util.TimeZone icuTimeZone, long startMs, long endMs, int flags)53 public static String formatDateRange(ULocale icuLocale, android.icu.util.TimeZone icuTimeZone, 54 long startMs, long endMs, int flags) { 55 Calendar startCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, startMs); 56 Calendar endCalendar; 57 if (startMs == endMs) { 58 endCalendar = startCalendar; 59 } else { 60 endCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, endMs); 61 } 62 63 // Special handling when the range ends at midnight: 64 // - If we're not showing times, and the range is non-empty, we fudge the end date so we don't 65 // count the day that's about to start. 66 // - If we are showing times, and the range ends at exactly 00:00 of the day following its start 67 // (which can be thought of as 24:00 the same day), we fudge the end date so we don't show the 68 // dates --- unless the start is anything displayed as 00:00, in which case we include both 69 // dates to disambiguate. 70 // This is not the behavior of icu4j's DateIntervalFormat, but it's the required behavior 71 // of Android's DateUtils.formatDateRange. 72 if (isExactlyMidnight(endCalendar)) { 73 boolean showTime = 74 (flags & DateUtilsBridge.FORMAT_SHOW_TIME) == DateUtilsBridge.FORMAT_SHOW_TIME; 75 boolean endsDayAfterStart = DateUtilsBridge.dayDistance(startCalendar, endCalendar) == 1; 76 if ((!showTime && startMs != endMs) 77 || (endsDayAfterStart 78 && !DateUtilsBridge.isDisplayMidnightUsingSkeleton(startCalendar))) { 79 endCalendar.add(Calendar.DAY_OF_MONTH, -1); 80 } 81 } 82 83 String skeleton = DateUtilsBridge.toSkeleton(startCalendar, endCalendar, flags); 84 android.icu.text.DateIntervalFormat formatter = 85 android.icu.text.DateIntervalFormat.getInstance(skeleton, icuLocale); 86 formatter.setTimeZone(icuTimeZone); 87 return formatter.format(startCalendar, endCalendar, new StringBuffer(), 88 new FieldPosition(0)).toString(); 89 } 90 isExactlyMidnight(Calendar c)91 private static boolean isExactlyMidnight(Calendar c) { 92 return c.get(Calendar.HOUR_OF_DAY) == 0 && 93 c.get(Calendar.MINUTE) == 0 && 94 c.get(Calendar.SECOND) == 0 && 95 c.get(Calendar.MILLISECOND) == 0; 96 } 97 } 98