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