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