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