Merge "Reenable EcdhTest#testModifiedPublic"
diff --git a/luni/src/main/java/libcore/icu/DateIntervalFormat.java b/luni/src/main/java/libcore/icu/DateIntervalFormat.java
index eb6b501..472d99b 100644
--- a/luni/src/main/java/libcore/icu/DateIntervalFormat.java
+++ b/luni/src/main/java/libcore/icu/DateIntervalFormat.java
@@ -61,16 +61,24 @@
       endCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, endMs);
     }
 
-    boolean endsAtMidnight = isMidnight(endCalendar);
-
-    // If we're not showing the time or the start and end times are on the same day, and the
-    // end time is midnight, fudge the end date so we don't count the day that's about to start.
-    // This is not the behavior of icu4j's DateIntervalFormat, but it's the historical behavior
+    // Special handling when the range ends at midnight:
+    // - If we're not showing times, and the range is non-empty, we fudge the end date so we don't
+    //   count the day that's about to start.
+    // - If we are showing times, and the range ends at exactly 00:00 of the day following its start
+    //   (which can be thought of as 24:00 the same day), we fudge the end date so we don't show the
+    //    dates --- unless the start is anything displayed as 00:00, in which case we include both
+    //    dates to disambiguate.
+    // This is not the behavior of icu4j's DateIntervalFormat, but it's the required behavior
     // of Android's DateUtils.formatDateRange.
-    if (startMs != endMs && endsAtMidnight &&
-        ((flags & DateUtilsBridge.FORMAT_SHOW_TIME) == 0
-            || DateUtilsBridge.dayDistance(startCalendar, endCalendar) <= 1)) {
-      endCalendar.add(Calendar.DAY_OF_MONTH, -1);
+    if (isExactlyMidnight(endCalendar)) {
+      boolean showTime =
+          (flags & DateUtilsBridge.FORMAT_SHOW_TIME) == DateUtilsBridge.FORMAT_SHOW_TIME;
+      boolean endsDayAfterStart = DateUtilsBridge.dayDistance(startCalendar, endCalendar) == 1;
+      if ((!showTime && startMs != endMs)
+          || (endsDayAfterStart
+                  && !DateUtilsBridge.isDisplayMidnightUsingSkeleton(startCalendar))) {
+        endCalendar.add(Calendar.DAY_OF_MONTH, -1);
+      }
     }
 
     String skeleton = DateUtilsBridge.toSkeleton(startCalendar, endCalendar, flags);
@@ -95,11 +103,10 @@
     return formatter;
   }
 
-  private static boolean isMidnight(Calendar c) {
+  private static boolean isExactlyMidnight(Calendar c) {
     return c.get(Calendar.HOUR_OF_DAY) == 0 &&
         c.get(Calendar.MINUTE) == 0 &&
         c.get(Calendar.SECOND) == 0 &&
         c.get(Calendar.MILLISECOND) == 0;
   }
-
 }
diff --git a/luni/src/main/java/libcore/icu/DateUtilsBridge.java b/luni/src/main/java/libcore/icu/DateUtilsBridge.java
index 6e28874..ac7a036 100644
--- a/luni/src/main/java/libcore/icu/DateUtilsBridge.java
+++ b/luni/src/main/java/libcore/icu/DateUtilsBridge.java
@@ -151,6 +151,16 @@
     return c2.get(Calendar.JULIAN_DAY) - c1.get(Calendar.JULIAN_DAY);
   }
 
+  /**
+   * Returns whether the argument will be displayed as if it were midnight, using any of the
+   * skeletons provided by {@link #toSkeleton}.
+   */
+  public static boolean isDisplayMidnightUsingSkeleton(Calendar c) {
+    // All the skeletons returned by toSkeleton have minute precision (they may abbreviate 4:00 PM
+    // to 4 PM but will still show the following minute as 4:01 PM).
+    return c.get(Calendar.HOUR_OF_DAY) == 0 && c.get(Calendar.MINUTE) == 0;
+  }
+
   private static boolean onTheHour(Calendar c) {
     return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0;
   }
diff --git a/luni/src/test/java/libcore/libcore/icu/DateIntervalFormatTest.java b/luni/src/test/java/libcore/libcore/icu/DateIntervalFormatTest.java
index 3c6049b..6b33f13 100644
--- a/luni/src/test/java/libcore/libcore/icu/DateIntervalFormatTest.java
+++ b/luni/src/test/java/libcore/libcore/icu/DateIntervalFormatTest.java
@@ -431,4 +431,42 @@
     assertEquals("11:00 PM – 12:00 AM", formatDateRange(locale, timeZone,
             1430434800000L, 1430438400000L, FORMAT_SHOW_TIME));
   }
+
+  // http://b/68847519
+  public void testEndAtMidnight() {
+    ULocale locale = new ULocale("en");
+    TimeZone timeZone = TimeZone.getTimeZone("UTC");
+    int dateTimeFlags = FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_24HOUR;
+    // If we're showing times and the end-point is midnight the following day, we want the
+    // behaviour of suppressing the date for the end...
+    assertEquals("February 27, 04:00 – 00:00",
+        formatDateRange(locale, timeZone, 1519704000000L, 1519776000000L, dateTimeFlags));
+    // ...unless the start-point is also midnight, in which case we need dates to disambiguate.
+    assertEquals("February 27, 00:00 – February 28, 00:00",
+        formatDateRange(locale, timeZone, 1519689600000L, 1519776000000L, dateTimeFlags));
+    // We want to show the date if the end-point is a millisecond after midnight the following
+    // day, or if it is exactly midnight the day after that.
+    assertEquals("February 27, 04:00 – February 28, 00:00",
+        formatDateRange(locale, timeZone, 1519704000000L, 1519776000001L, dateTimeFlags));
+    assertEquals("February 27, 04:00 – March 1, 00:00",
+        formatDateRange(locale, timeZone, 1519704000000L, 1519862400000L, dateTimeFlags));
+    // We want to show the date if the start-point is anything less than a minute after midnight,
+    // since that gets displayed as midnight...
+    assertEquals("February 27, 00:00 – February 28, 00:00",
+        formatDateRange(locale, timeZone, 1519689659999L, 1519776000000L, dateTimeFlags));
+    // ...but not if it is exactly one minute after midnight.
+    assertEquals("February 27, 00:01 – 00:00",
+        formatDateRange(locale, timeZone, 1519689660000L, 1519776000000L, dateTimeFlags));
+    int dateOnlyFlags = FORMAT_SHOW_DATE;
+    // If we're only showing dates and the end-point is midnight of any day, we want the
+    // behaviour of showing an end date one earlier. So if the end-point is March 2, 00:00, show
+    // March 1 instead (whether the start-point is midnight or not).
+    assertEquals("February 27 – March 1",
+        formatDateRange(locale, timeZone, 1519689600000L, 1519948800000L, dateOnlyFlags));
+    assertEquals("February 27 – March 1",
+        formatDateRange(locale, timeZone, 1519704000000L, 1519948800000L, dateOnlyFlags));
+    // We want to show the true date if the end-point is a millisecond after midnight.
+    assertEquals("February 27 – March 2",
+        formatDateRange(locale, timeZone, 1519689600000L, 1519948800001L, dateOnlyFlags));
+  }
 }