ICU-20651 add setContext/getContext for [U]DateIntervalFormat
diff --git a/icu4c/source/i18n/dtitvfmt.cpp b/icu4c/source/i18n/dtitvfmt.cpp
index 3b65c94..2433a80 100644
--- a/icu4c/source/i18n/dtitvfmt.cpp
+++ b/icu4c/source/i18n/dtitvfmt.cpp
@@ -23,6 +23,7 @@
 #include "unicode/dtptngen.h"
 #include "unicode/dtitvinf.h"
 #include "unicode/simpleformatter.h"
+#include "unicode/udisplaycontext.h"
 #include "cmemory.h"
 #include "cstring.h"
 #include "dtitv_impl.h"
@@ -143,7 +144,8 @@
     fLocale(Locale::getRoot()),
     fDatePattern(nullptr),
     fTimePattern(nullptr),
-    fDateTimeFormat(nullptr)
+    fDateTimeFormat(nullptr),
+    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
 {}
 
 
@@ -156,7 +158,8 @@
     fLocale(itvfmt.fLocale),
     fDatePattern(nullptr),
     fTimePattern(nullptr),
-    fDateTimeFormat(nullptr) {
+    fDateTimeFormat(nullptr),
+    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE) {
     *this = itvfmt;
 }
 
@@ -203,6 +206,7 @@
         fDatePattern    = (itvfmt.fDatePattern)?    itvfmt.fDatePattern->clone(): nullptr;
         fTimePattern    = (itvfmt.fTimePattern)?    itvfmt.fTimePattern->clone(): nullptr;
         fDateTimeFormat = (itvfmt.fDateTimeFormat)? itvfmt.fDateTimeFormat->clone(): nullptr;
+        fCapitalizationContext = itvfmt.fCapitalizationContext;
     }
     return *this;
 }
@@ -254,6 +258,7 @@
         if (fIntervalPatterns[i].secondPart != fmt->fIntervalPatterns[i].secondPart ) {return FALSE;}
         if (fIntervalPatterns[i].laterDateFirst != fmt->fIntervalPatterns[i].laterDateFirst) {return FALSE;}
     }
+    if (fCapitalizationContext != fmt->fCapitalizationContext) {return FALSE;}
     return TRUE;
 }
 
@@ -409,6 +414,7 @@
 }
 
 
+// The following is only called from within the gFormatterMutex lock
 UnicodeString&
 DateIntervalFormat::formatImpl(Calendar& fromCalendar,
                            Calendar& toCalendar,
@@ -464,6 +470,11 @@
     if ( U_FAILURE(status) ) {
         return appendTo;
     }
+    UErrorCode tempStatus = U_ZERO_ERROR; // for setContext, ignored
+    // Set up fDateFormat to handle the first or only part of the interval
+    // (override later for any second part). Inside lock, OK to modify fDateFormat.
+    fDateFormat->setContext(fCapitalizationContext, tempStatus);
+
     if ( field == UCAL_FIELD_COUNT ) {
         /* ignore the millisecond etc. small fields' difference.
          * use single date when all the above are the same.
@@ -521,6 +532,9 @@
 
     if ( !intervalPattern.secondPart.isEmpty() ) {
         fDateFormat->applyPattern(intervalPattern.secondPart);
+        // No capitalization for second part of interval
+        tempStatus = U_ZERO_ERROR;
+        fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus);
         fDateFormat->_format(*secondCal, appendTo, fphandler, status);
     }
     fDateFormat->applyPattern(originalPattern);
@@ -622,6 +636,30 @@
     return *(TimeZone::createDefault());
 }
 
+void
+DateIntervalFormat::setContext(UDisplayContext value, UErrorCode& status)
+{
+    if (U_FAILURE(status))
+        return;
+    if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) {
+        fCapitalizationContext = value;
+    } else {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+    }
+}
+
+UDisplayContext
+DateIntervalFormat::getContext(UDisplayContextType type, UErrorCode& status) const
+{
+    if (U_FAILURE(status))
+        return (UDisplayContext)0;
+    if (type != UDISPCTX_TYPE_CAPITALIZATION) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return (UDisplayContext)0;
+    }
+    return fCapitalizationContext;
+}
+
 DateIntervalFormat::DateIntervalFormat(const Locale& locale,
                                        DateIntervalInfo* dtItvInfo,
                                        const UnicodeString* skeleton,
@@ -633,7 +671,8 @@
     fLocale(locale),
     fDatePattern(nullptr),
     fTimePattern(nullptr),
-    fDateTimeFormat(nullptr)
+    fDateTimeFormat(nullptr),
+    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
 {
     LocalPointer<DateIntervalInfo> info(dtItvInfo, status);
     LocalPointer<SimpleDateFormat> dtfmt(static_cast<SimpleDateFormat *>(
@@ -1506,6 +1545,7 @@
     return (i - count);
 }
 
+// The following is only called from fallbackFormat, i.e. within the gFormatterMutex lock
 void DateIntervalFormat::fallbackFormatRange(
         Calendar& fromCalendar,
         Calendar& toCalendar,
@@ -1522,12 +1562,15 @@
     int32_t offsets[2];
     UnicodeString patternBody = sf.getTextWithNoArguments(offsets, 2);
 
+    UErrorCode tempStatus = U_ZERO_ERROR; // for setContext, ignored
     // TODO(ICU-20406): Use SimpleFormatter Iterator interface when available.
     if (offsets[0] < offsets[1]) {
         firstIndex = 0;
         appendTo.append(patternBody.tempSubStringBetween(0, offsets[0]));
         fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
         appendTo.append(patternBody.tempSubStringBetween(offsets[0], offsets[1]));
+        // No capitalization for second part of interval
+        fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus);
         fDateFormat->_format(toCalendar, appendTo, fphandler, status);
         appendTo.append(patternBody.tempSubStringBetween(offsets[1]));
     } else {
@@ -1535,11 +1578,14 @@
         appendTo.append(patternBody.tempSubStringBetween(0, offsets[1]));
         fDateFormat->_format(toCalendar, appendTo, fphandler, status);
         appendTo.append(patternBody.tempSubStringBetween(offsets[1], offsets[0]));
+        // No capitalization for second part of interval
+        fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus);
         fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
         appendTo.append(patternBody.tempSubStringBetween(offsets[0]));
     }
 }
 
+// The following is only called from formatImpl, i.e. within the gFormatterMutex lock
 UnicodeString&
 DateIntervalFormat::fallbackFormat(Calendar& fromCalendar,
                                    Calendar& toCalendar,
@@ -1564,6 +1610,7 @@
         UnicodeString fullPattern; // for saving the pattern in fDateFormat
         fDateFormat->toPattern(fullPattern); // save current pattern, restore later
 
+        UErrorCode tempStatus = U_ZERO_ERROR; // for setContext, ignored
         // {0} is time range
         // {1} is single date portion
         // TODO(ICU-20406): Use SimpleFormatter Iterator interface when available.
@@ -1573,6 +1620,8 @@
             fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status);
             appendTo.append(patternBody.tempSubStringBetween(offsets[0], offsets[1]));
             fDateFormat->applyPattern(*fDatePattern);
+            // No capitalization for second portion
+            fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus);
             fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
             appendTo.append(patternBody.tempSubStringBetween(offsets[1]));
         } else {
@@ -1581,6 +1630,8 @@
             fDateFormat->_format(fromCalendar, appendTo, fphandler, status);
             appendTo.append(patternBody.tempSubStringBetween(offsets[1], offsets[0]));
             fDateFormat->applyPattern(*fTimePattern);
+            // No capitalization for second portion
+            fDateFormat->setContext(UDISPCTX_CAPITALIZATION_NONE, tempStatus);
             fallbackFormatRange(fromCalendar, toCalendar, appendTo, firstIndex, fphandler, status);
             appendTo.append(patternBody.tempSubStringBetween(offsets[0]));
         }
diff --git a/icu4c/source/i18n/udateintervalformat.cpp b/icu4c/source/i18n/udateintervalformat.cpp
index 4bae622..3557443 100644
--- a/icu4c/source/i18n/udateintervalformat.cpp
+++ b/icu4c/source/i18n/udateintervalformat.cpp
@@ -18,6 +18,7 @@
 #include "unicode/timezone.h"
 #include "unicode/locid.h"
 #include "unicode/unistr.h"
+#include "unicode/udisplaycontext.h"
 #include "formattedval_impl.h"
 
 U_NAMESPACE_USE
@@ -151,5 +152,25 @@
     }
 }
 
+U_CAPI void U_EXPORT2
+udtitvfmt_setContext(UDateIntervalFormat* formatter,
+                     UDisplayContext value,
+                     UErrorCode* status) {
+    if (U_FAILURE(*status)) {
+        return;
+    }
+    reinterpret_cast<DateIntervalFormat*>(formatter)->setContext( value, *status );
+}
+
+U_CAPI UDisplayContext U_EXPORT2
+udtitvfmt_getContext(const UDateIntervalFormat* formatter,
+                     UDisplayContextType type,
+                     UErrorCode* status) {
+    if (U_FAILURE(*status)) {
+        return (UDisplayContext)0;
+    }
+    return reinterpret_cast<const DateIntervalFormat*>(formatter)->getContext( type, *status );
+}
+
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/unicode/dtitvfmt.h b/icu4c/source/i18n/unicode/dtitvfmt.h
index 3ff0aff..9fa1908 100644
--- a/icu4c/source/i18n/unicode/dtitvfmt.h
+++ b/icu4c/source/i18n/unicode/dtitvfmt.h
@@ -31,6 +31,7 @@
 #include "unicode/dtitvinf.h"
 #include "unicode/dtptngen.h"
 #include "unicode/formattedvalue.h"
+#include "unicode/udisplaycontext.h"
 
 U_NAMESPACE_BEGIN
 
@@ -652,6 +653,32 @@
     virtual void setTimeZone(const TimeZone& zone);
 
     /**
+     * Set a particular UDisplayContext value in the formatter, such as
+     * UDISPCTX_CAPITALIZATION_FOR_STANDALONE. This causes the formatted
+     * result to be capitalized appropriately for the context in which
+     * it is intended to be used, considering both the locale and the
+     * type of field at the beginning of the formatted result.
+     * @param value The UDisplayContext value to set.
+     * @param status Input/output status. If at entry this indicates a failure
+     *               status, the function will do nothing; otherwise this will be
+     *               updated with any new status from the function.
+     * @draft ICU 68
+     */
+    virtual void setContext(UDisplayContext value, UErrorCode& status);
+
+    /**
+     * Get the formatter's UDisplayContext value for the specified UDisplayContextType,
+     * such as UDISPCTX_TYPE_CAPITALIZATION.
+     * @param type The UDisplayContextType whose value to return
+     * @param status Input/output status. If at entry this indicates a failure
+     *               status, the function will do nothing; otherwise this will be
+     *               updated with any new status from the function.
+     * @return The UDisplayContextValue for the specified type.
+     * @draft ICU 68
+     */
+    virtual UDisplayContext getContext(UDisplayContextType type, UErrorCode& status) const;
+
+    /**
      * Return the class ID for this class. This is useful only for comparing to
      * a return value from getDynamicClassID(). For example:
      * <pre>
@@ -1152,6 +1179,11 @@
     UnicodeString* fDatePattern;
     UnicodeString* fTimePattern;
     UnicodeString* fDateTimeFormat;
+
+    /**
+     * Other formatting information
+     */
+    UDisplayContext fCapitalizationContext;
 };
 
 inline UBool
diff --git a/icu4c/source/i18n/unicode/udateintervalformat.h b/icu4c/source/i18n/unicode/udateintervalformat.h
index 03b7361..b100ea6 100644
--- a/icu4c/source/i18n/unicode/udateintervalformat.h
+++ b/icu4c/source/i18n/unicode/udateintervalformat.h
@@ -17,6 +17,7 @@
 #include "unicode/ucal.h"
 #include "unicode/umisc.h"
 #include "unicode/uformattedvalue.h"
+#include "unicode/udisplaycontext.h"
 
 #if U_SHOW_CPLUSPLUS_API
 #include "unicode/localpointer.h"
@@ -302,6 +303,34 @@
                 UErrorCode*     status);
 #endif /* U_HIDE_DRAFT_API */
 
+#ifndef U_HIDE_DRAFT_API
+/**
+ * Set a particular UDisplayContext value in the formatter, such as
+ * UDISPCTX_CAPITALIZATION_FOR_STANDALONE. This causes the formatted
+ * result to be capitalized appropriately for the context in which
+ * it is intended to be used, considering both the locale and the
+ * type of field at the beginning of the formatted result.
+ * @param formatter The formatter for which to set a UDisplayContext value.
+ * @param value The UDisplayContext value to set.
+ * @param status A pointer to an UErrorCode to receive any errors
+ * @draft ICU 68
+ */
+U_CAPI void U_EXPORT2
+udtitvfmt_setContext(UDateIntervalFormat* formatter, UDisplayContext value, UErrorCode* status);
+
+/**
+ * Get the formatter's UDisplayContext value for the specified UDisplayContextType,
+ * such as UDISPCTX_TYPE_CAPITALIZATION.
+ * @param formatter The formatter to query.
+ * @param type The UDisplayContextType whose value to return
+ * @param status A pointer to an UErrorCode to receive any errors
+ * @return The UDisplayContextValue for the specified type.
+ * @draft ICU 68
+ */
+U_CAPI UDisplayContext U_EXPORT2
+udtitvfmt_getContext(const UDateIntervalFormat* formatter, UDisplayContextType type, UErrorCode* status);
+
+#endif /* U_HIDE_DRAFT_API */
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
 
diff --git a/icu4c/source/test/cintltst/cdateintervalformattest.c b/icu4c/source/test/cintltst/cdateintervalformattest.c
index fb5bc73..eb3877e 100644
--- a/icu4c/source/test/cintltst/cdateintervalformattest.c
+++ b/icu4c/source/test/cintltst/cdateintervalformattest.c
@@ -14,6 +14,7 @@
 #include "unicode/udat.h"
 #include "unicode/ucal.h"
 #include "unicode/ustring.h"
+#include "unicode/udisplaycontext.h"
 #include "cintltst.h"
 #include "cmemory.h"
 #include "cformtst.h"
@@ -48,23 +49,41 @@
 typedef struct {
     const char * locale;
     const char * skeleton;
+    UDisplayContext context;
     const char * tzid;
     const UDate  from;
     const UDate  to;
     const char * resultExpected;
 } DateIntervalFormatTestItem;
 
+#define CAP_NONE  UDISPCTX_CAPITALIZATION_NONE
+#define CAP_BEGIN UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
+#define CAP_LIST  UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
+#define CAP_ALONE UDISPCTX_CAPITALIZATION_FOR_STANDALONE
+
 /* Just a small set of tests for now, the real functionality is tested in the C++ tests */
 static const DateIntervalFormatTestItem testItems[] = {
-    { "en", "MMMdHHmm", tzUSPacific, Date201103021030, Date201103021030 + 7.0*_HOUR,  "Mar 2, 10:30 \\u2013 17:30" },
-    { "en", "MMMdHHmm", tzAsiaTokyo, Date201103021030, Date201103021030 + 7.0*_HOUR,  "Mar 3, 03:30 \\u2013 10:30" },
-    { "en", "yMMMEd",   tzUSPacific, Date201009270800, Date201009270800 + 12.0*_HOUR, "Mon, Sep 27, 2010" },
-    { "en", "yMMMEd",   tzUSPacific, Date201009270800, Date201009270800 + 31.0*_DAY,  "Mon, Sep 27 \\u2013 Thu, Oct 28, 2010" },
-    { "en", "yMMMEd",   tzUSPacific, Date201009270800, Date201009270800 + 410.0*_DAY, "Mon, Sep 27, 2010 \\u2013 Fri, Nov 11, 2011" },
-    { "de", "Hm",       tzUSPacific, Date201009270800, Date201009270800 + 12.0*_HOUR, "08:00\\u201320:00 Uhr" },
-    { "de", "Hm",       tzUSPacific, Date201009270800, Date201009270800 + 31.0*_DAY,  "27.9.2010, 08:00 \\u2013 28.10.2010, 08:00" },
-    { "ja", "MMMd",     tzUSPacific, Date201009270800, Date201009270800 + 1.0*_DAY,   "9\\u670827\\u65E5\\uFF5E28\\u65E5" },
-    { NULL, NULL,       NULL,        0,                0,                             NULL }
+    { "en", "MMMdHHmm", CAP_NONE,  tzUSPacific, Date201103021030, Date201103021030 + 7.0*_HOUR,  "Mar 2, 10:30 \\u2013 17:30" },
+    { "en", "MMMdHHmm", CAP_NONE,  tzAsiaTokyo, Date201103021030, Date201103021030 + 7.0*_HOUR,  "Mar 3, 03:30 \\u2013 10:30" },
+    { "en", "yMMMEd",   CAP_NONE,  tzUSPacific, Date201009270800, Date201009270800 + 12.0*_HOUR, "Mon, Sep 27, 2010" },
+    { "en", "yMMMEd",   CAP_NONE,  tzUSPacific, Date201009270800, Date201009270800 + 31.0*_DAY,  "Mon, Sep 27 \\u2013 Thu, Oct 28, 2010" },
+    { "en", "yMMMEd",   CAP_NONE,  tzUSPacific, Date201009270800, Date201009270800 + 410.0*_DAY, "Mon, Sep 27, 2010 \\u2013 Fri, Nov 11, 2011" },
+    { "de", "Hm",       CAP_NONE,  tzUSPacific, Date201009270800, Date201009270800 + 12.0*_HOUR, "08:00\\u201320:00 Uhr" },
+    { "de", "Hm",       CAP_NONE,  tzUSPacific, Date201009270800, Date201009270800 + 31.0*_DAY,  "27.9.2010, 08:00 \\u2013 28.10.2010, 08:00" },
+    { "ja", "MMMd",     CAP_NONE,  tzUSPacific, Date201009270800, Date201009270800 + 1.0*_DAY,   "9\\u670827\\u65E5\\uFF5E28\\u65E5" },
+    { "cs", "MMMEd",    CAP_NONE,  tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY,  "po 27. 9. \\u2013 p\\u00E1 26. 11." },
+    { "cs", "yMMMM",    CAP_NONE,  tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY,  "z\\u00E1\\u0159\\u00ED\\u2013listopad 2010" },
+    { "cs", "yMMMM",    CAP_NONE,  tzUSPacific, Date201009270800, Date201009270800 + 1.0*_DAY,   "z\\u00E1\\u0159\\u00ED 2010" },
+    { "cs", "MMMEd",    CAP_BEGIN, tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY,  "Po 27. 9. \\u2013 p\\u00E1 26. 11." },
+    { "cs", "yMMMM",    CAP_BEGIN, tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY,  "Z\\u00E1\\u0159\\u00ED\\u2013listopad 2010" },
+    { "cs", "yMMMM",    CAP_BEGIN, tzUSPacific, Date201009270800, Date201009270800 + 1.0*_DAY,   "Z\\u00E1\\u0159\\u00ED 2010" },
+    { "cs", "MMMEd",    CAP_LIST,  tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY,  "Po 27. 9. \\u2013 p\\u00E1 26. 11." },
+    { "cs", "yMMMM",    CAP_LIST,  tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY,  "Z\\u00E1\\u0159\\u00ED\\u2013listopad 2010" },
+    { "cs", "yMMMM",    CAP_LIST,  tzUSPacific, Date201009270800, Date201009270800 + 1.0*_DAY,   "Z\\u00E1\\u0159\\u00ED 2010" },
+    { "cs", "MMMEd",    CAP_ALONE, tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY,  "po 27. 9. \\u2013 p\\u00E1 26. 11." },
+    { "cs", "yMMMM",    CAP_ALONE, tzUSPacific, Date201009270800, Date201009270800 + 60.0*_DAY,  "z\\u00E1\\u0159\\u00ED\\u2013listopad 2010" },
+    { "cs", "yMMMM",    CAP_ALONE, tzUSPacific, Date201009270800, Date201009270800 + 1.0*_DAY,   "z\\u00E1\\u0159\\u00ED 2010" },
+    { NULL, NULL,       CAP_NONE,  NULL,        0,                0,                             NULL }
 };
 
 enum {
@@ -98,6 +117,22 @@
         if ( U_SUCCESS(status) ) {
             UChar result[kFormatBufLen];
             UChar resultExpected[kFormatBufLen];
+
+            udtitvfmt_setContext(udtitvfmt, testItemPtr->context, &status);
+            if ( U_FAILURE(status) ) {
+                log_err("FAIL: udtitvfmt_setContext for locale %s, skeleton %s, context %04X -  %s\n",
+                        testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, myErrorName(status) );
+            } else {
+                UDisplayContext getContext = udtitvfmt_getContext(udtitvfmt, UDISPCTX_TYPE_CAPITALIZATION, &status);
+                if ( U_FAILURE(status) ) {
+                    log_err("FAIL: udtitvfmt_getContext for locale %s, skeleton %s, context %04X -  %s\n",
+                            testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, myErrorName(status) );
+                } else if (getContext != testItemPtr->context) {
+                    log_err("FAIL: udtitvfmt_getContext for locale %s, skeleton %s, context %04X -  got context %04X\n",
+                            testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, (unsigned)getContext );
+                }
+            }
+            status = U_ZERO_ERROR;
             int32_t fmtLen = udtitvfmt_format(udtitvfmt, testItemPtr->from, testItemPtr->to, result, kFormatBufLen, NULL, &status);
             if (fmtLen >= kFormatBufLen) {
                 result[kFormatBufLen-1] = 0;
diff --git a/icu4c/source/test/intltest/dtifmtts.cpp b/icu4c/source/test/intltest/dtifmtts.cpp
index 85d74c6..cc91257 100644
--- a/icu4c/source/test/intltest/dtifmtts.cpp
+++ b/icu4c/source/test/intltest/dtifmtts.cpp
@@ -61,6 +61,7 @@
         TESTCASE(12, testTicket20707);
         TESTCASE(13, testFormatMillisecond);
         TESTCASE(14, testHourMetacharacters);
+        TESTCASE(15, testContext);
         default: name = ""; break;
     }
 }
@@ -1275,6 +1276,83 @@
     expectUserDII(DATA, UPRV_LENGTHOF(DATA));
 }
 
+/*
+ * Test format using UDisplayContext
+ */
+#define CAP_NONE  UDISPCTX_CAPITALIZATION_NONE
+#define CAP_BEGIN UDISPCTX_CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE
+#define CAP_LIST  UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU
+#define CAP_ALONE UDISPCTX_CAPITALIZATION_FOR_STANDALONE
+#define _DAY    (24.0*60.0*60.0*1000.0)
+
+void DateIntervalFormatTest::testContext() {
+    static const UDate startDate = 1285599629000.0; // 2010-Sep-27 0800 in America/Los_Angeles
+    typedef struct {
+        const char * locale;
+        const char * skeleton;
+        UDisplayContext context;
+        const UDate  deltaDate;
+        const UChar* expectResult;
+    } DateIntervalContextItem;
+    static const DateIntervalContextItem testItems[] = {
+        { "cs",    "MMMEd",    CAP_NONE,  60.0*_DAY,  u"po 27. 9. – pá 26. 11." },
+        { "cs",    "yMMMM",    CAP_NONE,  60.0*_DAY,  u"září–listopad 2010" },
+        { "cs",    "yMMMM",    CAP_NONE,  1.0*_DAY,   u"září 2010" },
+        { "cs",    "MMMEd",    CAP_BEGIN, 60.0*_DAY,  u"Po 27. 9. – pá 26. 11." },
+        { "cs",    "yMMMM",    CAP_BEGIN, 60.0*_DAY,  u"Září–listopad 2010" },
+        { "cs",    "yMMMM",    CAP_BEGIN, 1.0*_DAY,   u"Září 2010" },
+        { "cs",    "MMMEd",    CAP_LIST,  60.0*_DAY,  u"Po 27. 9. – pá 26. 11." },
+        { "cs",    "yMMMM",    CAP_LIST,  60.0*_DAY,  u"Září–listopad 2010" },
+        { "cs",    "yMMMM",    CAP_LIST,  1.0*_DAY,   u"Září 2010" },
+        { "cs",    "MMMEd",    CAP_ALONE, 60.0*_DAY,  u"po 27. 9. – pá 26. 11." },
+        { "cs",    "yMMMM",    CAP_ALONE, 60.0*_DAY,  u"září–listopad 2010" },
+        { "cs",    "yMMMM",    CAP_ALONE, 1.0*_DAY,   u"září 2010" },
+        { nullptr, nullptr,    CAP_NONE,  0,          nullptr }
+    };
+    const DateIntervalContextItem* testItemPtr;
+    for ( testItemPtr = testItems; testItemPtr->locale != nullptr; ++testItemPtr ) {
+        UErrorCode status = U_ZERO_ERROR;
+        Locale locale(testItemPtr->locale);
+        UnicodeString skeleton(testItemPtr->skeleton, -1, US_INV);
+        LocalPointer<DateIntervalFormat> fmt(DateIntervalFormat::createInstance(skeleton, locale, status));
+        if (U_FAILURE(status)) {
+            errln("createInstance failed for locale %s skeleton %s: %s",
+                    testItemPtr->locale, testItemPtr->skeleton, u_errorName(status));
+            continue;
+        }
+        fmt->adoptTimeZone(TimeZone::createTimeZone("America/Los_Angeles"));
+
+        fmt->setContext(testItemPtr->context, status);
+        if (U_FAILURE(status)) {
+            errln("setContext failed for locale %s skeleton %s context %04X: %s",
+                    testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, u_errorName(status));
+        } else {
+            UDisplayContext getContext = fmt->getContext(UDISPCTX_TYPE_CAPITALIZATION, status);
+            if (U_FAILURE(status)) {
+                errln("getContext failed for locale %s skeleton %s context %04X: %s",
+                        testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, u_errorName(status));
+            } else if (getContext != testItemPtr->context) {
+                errln("getContext failed for locale %s skeleton %s context %04X: got context %04X",
+                        testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, (unsigned)getContext);
+            }
+        }
+
+        status = U_ZERO_ERROR;
+        DateInterval interval(startDate, startDate + testItemPtr->deltaDate);
+        UnicodeString getResult;
+        FieldPosition pos(FieldPosition::DONT_CARE);
+        fmt->format(&interval, getResult, pos, status);
+        if (U_FAILURE(status)) {
+            errln("format failed for locale %s skeleton %s context %04X: %s",
+                    testItemPtr->locale, testItemPtr->skeleton, (unsigned)testItemPtr->context, u_errorName(status));
+            continue;
+        }
+        UnicodeString expectResult(true, testItemPtr->expectResult, -1);
+        if (getResult != expectResult) {
+            errln(UnicodeString("format expected ") + expectResult + UnicodeString(" but got ") + getResult);
+        }
+    }
+}
 
 void DateIntervalFormatTest::testSetIntervalPatternNoSideEffect() {
     UErrorCode ec = U_ZERO_ERROR;
diff --git a/icu4c/source/test/intltest/dtifmtts.h b/icu4c/source/test/intltest/dtifmtts.h
index 7c96eea..700eb4f 100644
--- a/icu4c/source/test/intltest/dtifmtts.h
+++ b/icu4c/source/test/intltest/dtifmtts.h
@@ -47,6 +47,11 @@
      */
     void testFormatUserDII();
 
+    /*
+     * Test format using UDisplayContext
+     */
+    void testContext();
+
     /**
      * Test for no unwanted side effects when setting
      * interval patterns.
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
index e682b74..8a847d7 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DateIntervalFormat.java
@@ -28,6 +28,7 @@
 import com.ibm.icu.impl.SimpleFormatterImpl;
 import com.ibm.icu.impl.Utility;
 import com.ibm.icu.text.DateIntervalInfo.PatternInfo;
+import com.ibm.icu.text.DisplayContext;
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.DateInterval;
 import com.ibm.icu.util.Output;
@@ -484,6 +485,10 @@
     private String fTimePattern = null;
     private String fDateTimeFormat = null;
 
+    /*
+     * Capitalization context, new in ICU 68
+     */
+    private DisplayContext fCapitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
 
     /*
      * default constructor; private because we don't want anyone to use
@@ -722,6 +727,7 @@
         other.fDatePattern = fDatePattern;
         other.fTimePattern = fTimePattern;
         other.fDateTimeFormat = fDateTimeFormat;
+        other.fCapitalizationSetting = fCapitalizationSetting;
         return other;
     }
 
@@ -920,6 +926,10 @@
             throw new IllegalArgumentException("can not format on two different calendars");
         }
 
+        // Set up fDateFormat to handle the first or only part of the interval
+        // (override later for any second part).
+        fDateFormat.setContext(fCapitalizationSetting);
+
         // First, find the largest different calendar field.
         int field = -1; //init with an invalid value.
 
@@ -1008,6 +1018,8 @@
         }
         if ( intervalPattern.getSecondPart() != null ) {
             fDateFormat.applyPattern(intervalPattern.getSecondPart());
+            // No capitalization for second part of interval
+            fDateFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
             fDateFormat.format(secondCal, appendTo, pos, attributes);
         }
         fDateFormat.applyPattern(originalPattern);
@@ -1045,6 +1057,8 @@
             if (pos.getEndIndex() > 0) {
                 pos = new FieldPosition(0);
             }
+            // No capitalization for second portion
+            fDateFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
         }
     }
 
@@ -1099,6 +1113,8 @@
                 if (pos.getEndIndex() > 0) {
                     pos = new FieldPosition(0);
                 }
+                // No capitalization for second portion
+                fDateFormat.setContext(DisplayContext.CAPITALIZATION_NONE);
             }
 
             // restore full pattern
@@ -1245,6 +1261,39 @@
     }
 
     /**
+     * {@icu} Set a particular DisplayContext value in the formatter,
+     * such as CAPITALIZATION_FOR_STANDALONE. This causes the formatted
+     * result to be capitalized appropriately for the context in which
+     * it is intended to be used, considering both the locale and the
+     * type of field at the beginning of the formatted result.
+     *
+     * @param context The DisplayContext value to set.
+     * @draft ICU 68
+     * @provisional This API might change or be removed in a future release.
+     */
+    public void setContext(DisplayContext context)
+    {
+        if (context.type() == DisplayContext.Type.CAPITALIZATION) {
+            fCapitalizationSetting = context;
+        }
+    }
+
+    /**
+     * {@icu} Get the formatter's DisplayContext value for the specified DisplayContext.Type,
+     * such as CAPITALIZATION.
+     *
+     * @param type the DisplayContext.Type whose value to return
+     * @return the current DisplayContext setting for the specified type
+     * @draft ICU 68
+     * @provisional This API might change or be removed in a future release.
+     */
+    public DisplayContext getContext(DisplayContext.Type type)
+    {
+        return (type == DisplayContext.Type.CAPITALIZATION && fCapitalizationSetting != null)?
+                fCapitalizationSetting: DisplayContext.CAPITALIZATION_NONE;
+    }
+
+    /**
      * Gets the date formatter
      * @return a copy of the date formatter associated with
      * this date interval formatter.
@@ -2197,6 +2246,10 @@
         throws IOException, ClassNotFoundException {
         stream.defaultReadObject();
         initializePattern(isDateIntervalInfoDefault ? LOCAL_PATTERN_CACHE : null);
+        // if deserialized from a release that didn't have fCapitalizationSetting, set it to default
+        if (fCapitalizationSetting == null) {
+            fCapitalizationSetting = DisplayContext.CAPITALIZATION_NONE;
+        }
     }
 
     /**
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java
index cb1572d..d6b1945 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/DateIntervalFormatTest.java
@@ -36,6 +36,7 @@
 import com.ibm.icu.text.DateIntervalFormat.FormattedDateInterval;
 import com.ibm.icu.text.DateIntervalInfo;
 import com.ibm.icu.text.DateIntervalInfo.PatternInfo;
+import com.ibm.icu.text.DisplayContext;
 import com.ibm.icu.text.SimpleDateFormat;
 import com.ibm.icu.util.Calendar;
 import com.ibm.icu.util.DateInterval;
@@ -943,7 +944,65 @@
         }
     }
 
+    /*
+     * Test format using DisplayContext
+     */
+    @Test
+    public void TestContext() {
+        final long startDate = 1285599629000L; // 2010-Sep-27 0800 in America/Los_Angeles
+        final long day = 24*60*60*1000; // milliseconds in a day
 
+        class DateIntervalContextItem {
+            public String locale;
+            public String skeleton;
+            public DisplayContext context;
+            public long deltaDate;
+            public String expectResult;
+             // Simple constructor
+            public DateIntervalContextItem(String loc, String skel, DisplayContext ctxt, long delta, String expect) {
+                locale = loc;
+                skeleton = skel;
+                context = ctxt;
+                deltaDate = delta;
+                expectResult = expect;
+            }
+        };
+
+        final DateIntervalContextItem[] testItems = {
+           new DateIntervalContextItem( "cs", "MMMEd", DisplayContext.CAPITALIZATION_NONE,                      60*day, "po 27. 9. – pá 26. 11." ),
+           new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_NONE,                      60*day, "září–listopad 2010" ),
+           new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_NONE,                       1*day, "září 2010" ),
+           new DateIntervalContextItem( "cs", "MMMEd", DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, 60*day, "Po 27. 9. – pá 26. 11." ),
+           new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE, 60*day, "Září–listopad 2010" ),
+           new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE,  1*day, "Září 2010" ),
+           new DateIntervalContextItem( "cs", "MMMEd", DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU,       60*day, "Po 27. 9. – pá 26. 11." ),
+           new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU,       60*day, "Září–listopad 2010" ),
+           new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU,        1*day, "Září 2010" ),
+           new DateIntervalContextItem( "cs", "MMMEd", DisplayContext.CAPITALIZATION_FOR_STANDALONE,            60*day, "po 27. 9. – pá 26. 11." ),
+           new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_FOR_STANDALONE,            60*day, "září–listopad 2010" ),
+           new DateIntervalContextItem( "cs", "yMMMM", DisplayContext.CAPITALIZATION_FOR_STANDALONE,             1*day, "září 2010" ),
+        };
+
+        for (DateIntervalContextItem item: testItems) {
+            DateIntervalFormat difmt = DateIntervalFormat.getInstance(item.skeleton, new ULocale(item.locale));
+            difmt.setTimeZone(TimeZone.getFrozenTimeZone("America/Los_Angeles"));
+
+            difmt.setContext(item.context);
+            DisplayContext getContext = difmt.getContext(DisplayContext.Type.CAPITALIZATION);
+            if (getContext != item.context) {
+                errln("For locale "  + item.locale + ", skeleton " + item.skeleton + ", context " + item.context +
+                        ": getContext returned " + getContext);
+            }
+            DateInterval interval = new DateInterval(startDate, startDate + item.deltaDate);
+            FieldPosition pos = new FieldPosition(0);
+            StringBuffer getResult = new StringBuffer();
+            difmt.format(interval, getResult, pos);
+            if (!getResult.toString().equals(item.expectResult)) {
+                errln("For locale "  + item.locale + ", skeleton " + item.skeleton + ", context " + item.context +
+                       ": expected " + item.expectResult + ", got " + getResult.toString());
+            }
+        }
+    }
 
     /*
      * Test format using user defined DateIntervalInfo