Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/intl/icu/source/i18n/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 111 kB image not shown  

Quelle  dtptngen.cpp   Sprache: C

 
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
*******************************************************************************
* Copyright (C) 2007-2016, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
*
* File DTPTNGEN.CPP
*
*******************************************************************************
*/


#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING

#include "unicode/datefmt.h"
#include "unicode/decimfmt.h"
#include "unicode/dtfmtsym.h"
#include "unicode/dtptngen.h"
#include "unicode/localpointer.h"
#include "unicode/simpleformatter.h"
#include "unicode/smpdtfmt.h"
#include "unicode/udat.h"
#include "unicode/udatpg.h"
#include "unicode/uniset.h"
#include "unicode/uloc.h"
#include "unicode/ures.h"
#include "unicode/ustring.h"
#include "unicode/rep.h"
#include "unicode/region.h"
#include "cpputils.h"
#include "mutex.h"
#include "umutex.h"
#include "cmemory.h"
#include "cstring.h"
#include "locbased.h"
#include "hash.h"
#include "uhash.h"
#include "ulocimp.h"
#include "uresimp.h"
#include "ulocimp.h"
#include "dtptngen_impl.h"
#include "ucln_in.h"
#include "charstr.h"
#include "uassert.h"

#if U_CHARSET_FAMILY==U_EBCDIC_FAMILY
/**
 * If we are on EBCDIC, use an iterator which will
 * traverse the bundles in ASCII order.
 */

#define U_USE_ASCII_BUNDLE_ITERATOR
#define U_SORT_ASCII_BUNDLE_ITERATOR
#endif

#if defined(U_USE_ASCII_BUNDLE_ITERATOR)

#include "unicode/ustring.h"
#include "uarrsort.h"

struct UResAEntry {
    char16_t *key;
    UResourceBundle *item;
};

struct UResourceBundleAIterator {
    UResourceBundle  *bund;
    UResAEntry *entries;
    int32_t num;
    int32_t cursor;
};

/* Must be C linkage to pass function pointer to the sort function */

U_CDECL_BEGIN

static int32_t U_CALLCONV
ures_a_codepointSort(const void *context, const void *left, const void *right) {
    //CompareContext *cmp=(CompareContext *)context;
    return u_strcmp(((const UResAEntry *)left)->key,
                    ((const UResAEntry *)right)->key);
}

U_CDECL_END

static void ures_a_open(UResourceBundleAIterator *aiter, UResourceBundle *bund, UErrorCode *status) {
    if(U_FAILURE(*status)) {
        return;
    }
    aiter->bund = bund;
    aiter->num = ures_getSize(aiter->bund);
    aiter->cursor = 0;
#if !defined(U_SORT_ASCII_BUNDLE_ITERATOR)
    aiter->entries = nullptr;
#else
    aiter->entries = (UResAEntry*)uprv_malloc(sizeof(UResAEntry)*aiter->num);
    for(int i=0;i<aiter->num;i++) {
        aiter->entries[i].item = ures_getByIndex(aiter->bund, i, nullptr, status);
        const char *akey = ures_getKey(aiter->entries[i].item);
        int32_t len = uprv_strlen(akey)+1;
        aiter->entries[i].key = (char16_t*)uprv_malloc(len*sizeof(char16_t));
        u_charsToUChars(akey, aiter->entries[i].key, len);
    }
    uprv_sortArray(aiter->entries, aiter->num, sizeof(UResAEntry), ures_a_codepointSort, nullptr, true, status);
#endif
}

static void ures_a_close(UResourceBundleAIterator *aiter) {
#if defined(U_SORT_ASCII_BUNDLE_ITERATOR)
    for(int i=0;i<aiter->num;i++) {
        uprv_free(aiter->entries[i].key);
        ures_close(aiter->entries[i].item);
    }
#endif
}

static const char16_t *ures_a_getNextString(UResourceBundleAIterator *aiter, int32_t *len, const char **key, UErrorCode *err) {
#if !defined(U_SORT_ASCII_BUNDLE_ITERATOR)
    return ures_getNextString(aiter->bund, len, key, err);
#else
    if(U_FAILURE(*err)) return nullptr;
    UResourceBundle *item = aiter->entries[aiter->cursor].item;
    const char16_t* ret = ures_getString(item, len, err);
    *key = ures_getKey(item);
    aiter->cursor++;
    return ret;
#endif
}


#endif


U_NAMESPACE_BEGIN

// *****************************************************************************
// class DateTimePatternGenerator
// *****************************************************************************
static const char16_t Canonical_Items[] = {
    // GyQMwWEDFdaHmsSv
    CAP_G, LOW_Y, CAP_Q, CAP_M, LOW_W, CAP_W, CAP_E,
    CAP_D, CAP_F, LOW_D, LOW_A, // The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
    CAP_H, LOW_M, LOW_S, CAP_S, LOW_V, 0
};

static const dtTypeElem dtTypes[] = {
    // patternChar, field, type, minLen, weight
    {CAP_G, UDATPG_ERA_FIELD, DT_SHORT, 1, 3,},
    {CAP_G, UDATPG_ERA_FIELD, DT_LONG,  4, 0},
    {CAP_G, UDATPG_ERA_FIELD, DT_NARROW, 5, 0},

    {LOW_Y, UDATPG_YEAR_FIELD, DT_NUMERIC, 1, 20},
    {CAP_Y, UDATPG_YEAR_FIELD, DT_NUMERIC + DT_DELTA, 1, 20},
    {LOW_U, UDATPG_YEAR_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 20},
    {LOW_R, UDATPG_YEAR_FIELD, DT_NUMERIC + 3*DT_DELTA, 1, 20},
    {CAP_U, UDATPG_YEAR_FIELD, DT_SHORT, 1, 3},
    {CAP_U, UDATPG_YEAR_FIELD, DT_LONG, 4, 0},
    {CAP_U, UDATPG_YEAR_FIELD, DT_NARROW, 5, 0},

    {CAP_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC, 1, 2},
    {CAP_Q, UDATPG_QUARTER_FIELD, DT_SHORT, 3, 0},
    {CAP_Q, UDATPG_QUARTER_FIELD, DT_LONG, 4, 0},
    {CAP_Q, UDATPG_QUARTER_FIELD, DT_NARROW, 5, 0},
    {LOW_Q, UDATPG_QUARTER_FIELD, DT_NUMERIC + DT_DELTA, 1, 2},
    {LOW_Q, UDATPG_QUARTER_FIELD, DT_SHORT - DT_DELTA, 3, 0},
    {LOW_Q, UDATPG_QUARTER_FIELD, DT_LONG - DT_DELTA, 4, 0},
    {LOW_Q, UDATPG_QUARTER_FIELD, DT_NARROW - DT_DELTA, 5, 0},

    {CAP_M, UDATPG_MONTH_FIELD, DT_NUMERIC, 1, 2},
    {CAP_M, UDATPG_MONTH_FIELD, DT_SHORT, 3, 0},
    {CAP_M, UDATPG_MONTH_FIELD, DT_LONG, 4, 0},
    {CAP_M, UDATPG_MONTH_FIELD, DT_NARROW, 5, 0},
    {CAP_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 2},
    {CAP_L, UDATPG_MONTH_FIELD, DT_SHORT - DT_DELTA, 3, 0},
    {CAP_L, UDATPG_MONTH_FIELD, DT_LONG - DT_DELTA, 4, 0},
    {CAP_L, UDATPG_MONTH_FIELD, DT_NARROW - DT_DELTA, 5, 0},
    {LOW_L, UDATPG_MONTH_FIELD, DT_NUMERIC + DT_DELTA, 1, 1},

    {LOW_W, UDATPG_WEEK_OF_YEAR_FIELD, DT_NUMERIC, 1, 2},

    {CAP_W, UDATPG_WEEK_OF_MONTH_FIELD, DT_NUMERIC, 1, 0},

    {CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORT, 1, 3},
    {CAP_E, UDATPG_WEEKDAY_FIELD, DT_LONG, 4, 0},
    {CAP_E, UDATPG_WEEKDAY_FIELD, DT_NARROW, 5, 0},
    {CAP_E, UDATPG_WEEKDAY_FIELD, DT_SHORTER, 6, 0},
    {LOW_C, UDATPG_WEEKDAY_FIELD, DT_NUMERIC + 2*DT_DELTA, 1, 2},
    {LOW_C, UDATPG_WEEKDAY_FIELD, DT_SHORT - 2*DT_DELTA, 3, 0},
    {LOW_C, UDATPG_WEEKDAY_FIELD, DT_LONG - 2*DT_DELTA, 4, 0},
    {LOW_C, UDATPG_WEEKDAY_FIELD, DT_NARROW - 2*DT_DELTA, 5, 0},
    {LOW_C, UDATPG_WEEKDAY_FIELD, DT_SHORTER - 2*DT_DELTA, 6, 0},
    {LOW_E, UDATPG_WEEKDAY_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // LOW_E is currently not used in CLDR data, should not be canonical
    {LOW_E, UDATPG_WEEKDAY_FIELD, DT_SHORT - DT_DELTA, 3, 0},
    {LOW_E, UDATPG_WEEKDAY_FIELD, DT_LONG - DT_DELTA, 4, 0},
    {LOW_E, UDATPG_WEEKDAY_FIELD, DT_NARROW - DT_DELTA, 5, 0},
    {LOW_E, UDATPG_WEEKDAY_FIELD, DT_SHORTER - DT_DELTA, 6, 0},

    {LOW_D, UDATPG_DAY_FIELD, DT_NUMERIC, 1, 2},
    {LOW_G, UDATPG_DAY_FIELD, DT_NUMERIC + DT_DELTA, 1, 20}, // really internal use, so we don't care

    {CAP_D, UDATPG_DAY_OF_YEAR_FIELD, DT_NUMERIC, 1, 3},

    {CAP_F, UDATPG_DAY_OF_WEEK_IN_MONTH_FIELD, DT_NUMERIC, 1, 0},

    {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_SHORT, 1, 3},
    {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_LONG, 4, 0},
    {LOW_A, UDATPG_DAYPERIOD_FIELD, DT_NARROW, 5, 0},
    {LOW_B, UDATPG_DAYPERIOD_FIELD, DT_SHORT - DT_DELTA, 1, 3},
    {LOW_B, UDATPG_DAYPERIOD_FIELD, DT_LONG - DT_DELTA, 4, 0},
    {LOW_B, UDATPG_DAYPERIOD_FIELD, DT_NARROW - DT_DELTA, 5, 0},
    // b needs to be closer to a than to B, so we make this 3*DT_DELTA
    {CAP_B, UDATPG_DAYPERIOD_FIELD, DT_SHORT - 3*DT_DELTA, 1, 3},
    {CAP_B, UDATPG_DAYPERIOD_FIELD, DT_LONG - 3*DT_DELTA, 4, 0},
    {CAP_B, UDATPG_DAYPERIOD_FIELD, DT_NARROW - 3*DT_DELTA, 5, 0},

    {CAP_H, UDATPG_HOUR_FIELD, DT_NUMERIC + 10*DT_DELTA, 1, 2}, // 24 hour
    {LOW_K, UDATPG_HOUR_FIELD, DT_NUMERIC + 11*DT_DELTA, 1, 2}, // 24 hour
    {LOW_H, UDATPG_HOUR_FIELD, DT_NUMERIC, 1, 2}, // 12 hour
    {CAP_K, UDATPG_HOUR_FIELD, DT_NUMERIC + DT_DELTA, 1, 2}, // 12 hour
    // The C code has had versions of the following 3, keep & update. Should not need these, but...
    // Without these, certain tests using e.g. staticGetSkeleton fail because j/J in patterns
    // get skipped instead of mapped to the right hour chars, for example in
    //   DateFormatTest::TestPatternFromSkeleton
    //   IntlTestDateTimePatternGeneratorAPI:: testStaticGetSkeleton
    //   DateIntervalFormatTest::testTicket11985
    // Need to investigate better handling of jJC replacement e.g. in staticGetSkeleton.
    {CAP_J, UDATPG_HOUR_FIELD, DT_NUMERIC + 5*DT_DELTA, 1, 2}, // 12/24 hour no AM/PM
    {LOW_J, UDATPG_HOUR_FIELD, DT_NUMERIC + 6*DT_DELTA, 1, 6}, // 12/24 hour
    {CAP_C, UDATPG_HOUR_FIELD, DT_NUMERIC + 7*DT_DELTA, 1, 6}, // 12/24 hour with preferred dayPeriods for 12

    {LOW_M, UDATPG_MINUTE_FIELD, DT_NUMERIC, 1, 2},

    {LOW_S, UDATPG_SECOND_FIELD, DT_NUMERIC, 1, 2},
    {CAP_A, UDATPG_SECOND_FIELD, DT_NUMERIC + DT_DELTA, 1, 1000},

    {CAP_S, UDATPG_FRACTIONAL_SECOND_FIELD, DT_NUMERIC, 1, 1000},

    {LOW_V, UDATPG_ZONE_FIELD, DT_SHORT - 2*DT_DELTA, 1, 0},
    {LOW_V, UDATPG_ZONE_FIELD, DT_LONG - 2*DT_DELTA, 4, 0},
    {LOW_Z, UDATPG_ZONE_FIELD, DT_SHORT, 1, 3},
    {LOW_Z, UDATPG_ZONE_FIELD, DT_LONG, 4, 0},
    {CAP_Z, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 3},
    {CAP_Z, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
    {CAP_Z, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 5, 0},
    {CAP_O, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0},
    {CAP_O, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
    {CAP_V, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 1, 0},
    {CAP_V, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 2, 0},
    {CAP_V, UDATPG_ZONE_FIELD, DT_LONG-1 - DT_DELTA, 3, 0},
    {CAP_V, UDATPG_ZONE_FIELD, DT_LONG-2 - DT_DELTA, 4, 0},
    {CAP_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0},
    {CAP_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0},
    {CAP_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},
    {LOW_X, UDATPG_ZONE_FIELD, DT_NARROW - DT_DELTA, 1, 0},
    {LOW_X, UDATPG_ZONE_FIELD, DT_SHORT - DT_DELTA, 2, 0},
    {LOW_X, UDATPG_ZONE_FIELD, DT_LONG - DT_DELTA, 4, 0},

    {0, UDATPG_FIELD_COUNT, 0, 0, 0} , // last row of dtTypes[]
 };

static const charconst CLDR_FIELD_APPEND[] = {
    "Era""Year""Quarter""Month""Week""*""Day-Of-Week",
    "*""*""Day""DayPeriod"// The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
    "Hour""Minute""Second""FractionalSecond""Timezone"
};

static const charconst CLDR_FIELD_NAME[UDATPG_FIELD_COUNT] = {
    "era""year""quarter""month""week""weekOfMonth""weekday",
    "dayOfYear""weekdayOfMonth""day""dayperiod"// The UDATPG_x_FIELD constants and these fields have a different order than in ICU4J
    "hour""minute""second""fractionalSecond""zone"
};

static const charconst CLDR_FIELD_WIDTH[] = { // [UDATPG_WIDTH_COUNT]
    """-short""-narrow"
};

static constexpr UDateTimePGDisplayWidth UDATPG_WIDTH_APPENDITEM = UDATPG_WIDE;
static constexpr int32_t UDATPG_FIELD_KEY_MAX = 24; // max length of CLDR field tag (type + width)

// For appendItems
static const char16_t UDATPG_ItemFormat[]= {0x7B, 0x30, 0x7D, 0x20, 0x251C, 0x7B, 0x32, 0x7D, 0x3A,
    0x20, 0x7B, 0x31, 0x7D, 0x2524, 0};  // {0} \u251C{2}: {1}\u2524

//static const char16_t repeatedPatterns[6]={CAP_G, CAP_E, LOW_Z, LOW_V, CAP_Q, 0}; // "GEzvQ"

static const char DT_DateTimePatternsTag[]="DateTimePatterns";
static const char DT_DateAtTimePatternsTag[]="DateTimePatterns%atTime";
static const char DT_DateTimeCalendarTag[]="calendar";
static const char DT_DateTimeGregorianTag[]="gregorian";
static const char DT_DateTimeAppendItemsTag[]="appendItems";
static const char DT_DateTimeFieldsTag[]="fields";
static const char DT_DateTimeAvailableFormatsTag[]="availableFormats";
//static const UnicodeString repeatedPattern=UnicodeString(repeatedPatterns);

UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DateTimePatternGenerator)
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTSkeletonEnumeration)
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DTRedundantEnumeration)

DateTimePatternGenerator*  U_EXPORT2
DateTimePatternGenerator::createInstance(UErrorCode& status) {
    return createInstance(Locale::getDefault(), status);
}

DateTimePatternGenerator* U_EXPORT2
DateTimePatternGenerator::createInstance(const Locale& locale, UErrorCode& status) {
    if (U_FAILURE(status)) {
        return nullptr;
    }
    LocalPointer<DateTimePatternGenerator> result(
            new DateTimePatternGenerator(locale, status), status);
    return U_SUCCESS(status) ? result.orphan() : nullptr;
}

DateTimePatternGenerator* U_EXPORT2
DateTimePatternGenerator::createInstanceNoStdPat(const Locale& locale, UErrorCode& status) {
    if (U_FAILURE(status)) {
        return nullptr;
    }
    LocalPointer<DateTimePatternGenerator> result(
            new DateTimePatternGenerator(locale, status, true), status);
    return U_SUCCESS(status) ? result.orphan() : nullptr;
}

DateTimePatternGenerator*  U_EXPORT2
DateTimePatternGenerator::createEmptyInstance(UErrorCode& status) {
    if (U_FAILURE(status)) {
        return nullptr;
    }
    LocalPointer<DateTimePatternGenerator> result(
            new DateTimePatternGenerator(status), status);
    return U_SUCCESS(status) ? result.orphan() : nullptr;
}

DateTimePatternGenerator::DateTimePatternGenerator(UErrorCode &status) :
    skipMatcher(nullptr),
    fAvailableFormatKeyHash(nullptr),
    fDefaultHourFormatChar(0),
    internalErrorCode(U_ZERO_ERROR)
{
    fp = new FormatParser();
    dtMatcher = new DateTimeMatcher();
    distanceInfo = new DistanceInfo();
    patternMap = new PatternMap();
    if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) {
        internalErrorCode = status = U_MEMORY_ALLOCATION_ERROR;
    }
}

DateTimePatternGenerator::DateTimePatternGenerator(const Locale& locale, UErrorCode &status, UBool skipStdPatterns) :
    skipMatcher(nullptr),
    fAvailableFormatKeyHash(nullptr),
    fDefaultHourFormatChar(0),
    internalErrorCode(U_ZERO_ERROR)
{
    fp = new FormatParser();
    dtMatcher = new DateTimeMatcher();
    distanceInfo = new DistanceInfo();
    patternMap = new PatternMap();
    if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) {
        internalErrorCode = status = U_MEMORY_ALLOCATION_ERROR;
    }
    else {
        initData(locale, status, skipStdPatterns);
    }
}

DateTimePatternGenerator::DateTimePatternGenerator(const DateTimePatternGenerator&&nbsp;other) :
    UObject(),
    skipMatcher(nullptr),
    fAvailableFormatKeyHash(nullptr),
    fDefaultHourFormatChar(0),
    internalErrorCode(U_ZERO_ERROR)
{
    fp = new FormatParser();
    dtMatcher = new DateTimeMatcher();
    distanceInfo = new DistanceInfo();
    patternMap = new PatternMap();
    if (fp == nullptr || dtMatcher == nullptr || distanceInfo == nullptr || patternMap == nullptr) {
        internalErrorCode = U_MEMORY_ALLOCATION_ERROR;
    }
    *this=other;
}

DateTimePatternGenerator&
DateTimePatternGenerator::operator=(const DateTimePatternGenerator& other) {
    // reflexive case
    if (&other == this) {
        return *this;
    }
    internalErrorCode = other.internalErrorCode;
    pLocale = other.pLocale;
    fDefaultHourFormatChar = other.fDefaultHourFormatChar;
    *fp = *(other.fp);
    dtMatcher->copyFrom(other.dtMatcher->skeleton);
    *distanceInfo = *(other.distanceInfo);
    for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) {
        dateTimeFormat[style] = other.dateTimeFormat[style];
    }
    decimal = other.decimal;
    for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) {
        dateTimeFormat[style].getTerminatedBuffer(); // NUL-terminate for the C API.
    }
    decimal.getTerminatedBuffer();
    delete skipMatcher;
    if ( other.skipMatcher == nullptr ) {
        skipMatcher = nullptr;
    }
    else {
        skipMatcher = new DateTimeMatcher(*other.skipMatcher);
        if (skipMatcher == nullptr)
        {
            internalErrorCode = U_MEMORY_ALLOCATION_ERROR;
            return *this;
        }
    }
    for (int32_t i=0; i< UDATPG_FIELD_COUNT; ++i ) {
        appendItemFormats[i] = other.appendItemFormats[i];
        appendItemFormats[i].getTerminatedBuffer(); // NUL-terminate for the C API.
        for (int32_t j=0; j< UDATPG_WIDTH_COUNT; ++j ) {
            fieldDisplayNames[i][j] = other.fieldDisplayNames[i][j];
            fieldDisplayNames[i][j].getTerminatedBuffer(); // NUL-terminate for the C API.
        }
    }
    patternMap->copyFrom(*other.patternMap, internalErrorCode);
    copyHashtable(other.fAvailableFormatKeyHash, internalErrorCode);
    return *this;
}


bool
DateTimePatternGenerator::operator==(const DateTimePatternGenerator& other) const {
    if (this == &other) {
        return true;
    }
    if ((pLocale==other.pLocale) && (patternMap->equals(*other.patternMap)) &&
        (decimal==other.decimal)) {
        for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) {
            if (dateTimeFormat[style] != other.dateTimeFormat[style]) {
                return false;
            }
        }
        for ( int32_t i=0 ; i<UDATPG_FIELD_COUNT; ++i ) {
            if (appendItemFormats[i] != other.appendItemFormats[i]) {
                return false;
            }
            for (int32_t j=0; j< UDATPG_WIDTH_COUNT; ++j ) {
                if (fieldDisplayNames[i][j] != other.fieldDisplayNames[i][j]) {
                    return false;
                }
            }
        }
        return true;
    }
    else {
        return false;
    }
}

bool
DateTimePatternGenerator::operator!=(const DateTimePatternGenerator& other) const {
    return  !operator==(other);
}

DateTimePatternGenerator::~DateTimePatternGenerator() {
    delete fAvailableFormatKeyHash;
    delete fp;
    delete dtMatcher;
    delete distanceInfo;
    delete patternMap;
    delete skipMatcher;
}

namespace {

UInitOnce initOnce {};
UHashtable *localeToAllowedHourFormatsMap = nullptr;

// Value deleter for hashmap.
U_CFUNC void U_CALLCONV deleteAllowedHourFormats(void *ptr) {
    uprv_free(ptr);
}

// Close hashmap at cleanup.
U_CFUNC UBool U_CALLCONV allowedHourFormatsCleanup() {
    uhash_close(localeToAllowedHourFormatsMap);
    return true;
}

enum AllowedHourFormat{
    ALLOWED_HOUR_FORMAT_UNKNOWN = -1,
    ALLOWED_HOUR_FORMAT_h,
    ALLOWED_HOUR_FORMAT_H,
    ALLOWED_HOUR_FORMAT_K,  // Added ICU-20383, used by JP
    ALLOWED_HOUR_FORMAT_k,  // Added ICU-20383, not currently used
    ALLOWED_HOUR_FORMAT_hb,
    ALLOWED_HOUR_FORMAT_hB,
    ALLOWED_HOUR_FORMAT_Kb, // Added ICU-20383, not currently used
    ALLOWED_HOUR_FORMAT_KB, // Added ICU-20383, not currently used
    // ICU-20383 The following are unlikely and not currently used
    ALLOWED_HOUR_FORMAT_Hb,
    ALLOWED_HOUR_FORMAT_HB
};

}  // namespace

void
DateTimePatternGenerator::initData(const Locale& locale, UErrorCode &status, UBool skipStdPatterns) {
    //const char *baseLangName = locale.getBaseName(); // unused
    if (U_FAILURE(status)) { return; }
    if (locale.isBogus()) {
        status = U_ILLEGAL_ARGUMENT_ERROR;
        return;
    }

    skipMatcher = nullptr;
    fAvailableFormatKeyHash=nullptr;
    addCanonicalItems(status);
    if (!skipStdPatterns) { // skip to prevent circular dependency when called from SimpleDateFormat::construct
        addICUPatterns(locale, status);
    }
    addCLDRData(locale, status);
    setDateTimeFromCalendar(locale, status);
    setDecimalSymbols(locale, status);
    umtx_initOnce(initOnce, loadAllowedHourFormatsData, status);
    getAllowedHourFormats(locale, status);
    // If any of the above methods failed then the object is in an invalid state.
    internalErrorCode = status;
// DateTimePatternGenerator::initData

namespace {

struct AllowedHourFormatsSink : public ResourceSink {
    // Initialize sub-sinks.
    AllowedHourFormatsSink() {}
    virtual ~AllowedHourFormatsSink();

    virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
                     UErrorCode &errorCode) override {
        ResourceTable timeData = value.getTable(errorCode);
        if (U_FAILURE(errorCode)) { return; }
        for (int32_t i = 0; timeData.getKeyAndValue(i, key, value); ++i) {
            const char *regionOrLocale = key;
            ResourceTable formatList = value.getTable(errorCode);
            if (U_FAILURE(errorCode)) { return; }
            // below we construct a list[] that has an entry for the "preferred" value at [0],
            // followed by 1 or more entries for the "allowed" values, terminated with an
            // entry for ALLOWED_HOUR_FORMAT_UNKNOWN (not included in length below)
            LocalMemory<int32_t> list;
            int32_t length = 0;
            int32_t preferredFormat = ALLOWED_HOUR_FORMAT_UNKNOWN;
            for (int32_t j = 0; formatList.getKeyAndValue(j, key, value); ++j) {
                if (uprv_strcmp(key, "allowed") == 0) {
                    if (value.getType() == URES_STRING) {
                        length = 2; // 1 preferred to add later, 1 allowed to add now
                        if (list.allocateInsteadAndReset(length + 1) == nullptr) {
                            errorCode = U_MEMORY_ALLOCATION_ERROR;
                            return;
                        }
                        list[1] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode));
                    }
                    else {
                        ResourceArray allowedFormats = value.getArray(errorCode);
                        length = allowedFormats.getSize() + 1; // 1 preferred, getSize allowed
                        if (list.allocateInsteadAndReset(length + 1) == nullptr) {
                            errorCode = U_MEMORY_ALLOCATION_ERROR;
                            return;
                        }
                        for (int32_t k = 1; k < length; ++k) {
                            allowedFormats.getValue(k-1, value);
                            list[k] = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode));
                        }
                    }
                } else if (uprv_strcmp(key, "preferred") == 0) {
                    preferredFormat = getHourFormatFromUnicodeString(value.getUnicodeString(errorCode));
                }
            }
            if (length > 1) {
                list[0] = (preferredFormat!=ALLOWED_HOUR_FORMAT_UNKNOWN)? preferredFormat: list[1];
            } else {
                // fallback handling for missing data
                length = 2; // 1 preferred, 1 allowed
                if (list.allocateInsteadAndReset(length + 1) == nullptr) {
                    errorCode = U_MEMORY_ALLOCATION_ERROR;
                    return;
                }
                list[0] = (preferredFormat!=ALLOWED_HOUR_FORMAT_UNKNOWN)? preferredFormat: ALLOWED_HOUR_FORMAT_H;
                list[1] = list[0];
            }
            list[length] = ALLOWED_HOUR_FORMAT_UNKNOWN;
            // At this point list[] will have at least two non-ALLOWED_HOUR_FORMAT_UNKNOWN entries,
            // followed by ALLOWED_HOUR_FORMAT_UNKNOWN.
            uhash_put(localeToAllowedHourFormatsMap, const_cast<char *>(regionOrLocale), list.orphan(), &errorCode);
            if (U_FAILURE(errorCode)) { return; }
        }
    }

    AllowedHourFormat getHourFormatFromUnicodeString(const UnicodeString &s) {
        if (s.length() == 1) {
            if (s[0] == LOW_H) { return ALLOWED_HOUR_FORMAT_h; }
            if (s[0] == CAP_H) { return ALLOWED_HOUR_FORMAT_H; }
            if (s[0] == CAP_K) { return ALLOWED_HOUR_FORMAT_K; }
            if (s[0] == LOW_K) { return ALLOWED_HOUR_FORMAT_k; }
        } else if (s.length() == 2) {
            if (s[0] == LOW_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_hb; }
            if (s[0] == LOW_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_hB; }
            if (s[0] == CAP_K && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_Kb; }
            if (s[0] == CAP_K && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_KB; }
            if (s[0] == CAP_H && s[1] == LOW_B) { return ALLOWED_HOUR_FORMAT_Hb; }
            if (s[0] == CAP_H && s[1] == CAP_B) { return ALLOWED_HOUR_FORMAT_HB; }
        }

        return ALLOWED_HOUR_FORMAT_UNKNOWN;
    }
};

}  // namespace

AllowedHourFormatsSink::~AllowedHourFormatsSink() {}

U_CFUNC void U_CALLCONV DateTimePatternGenerator::loadAllowedHourFormatsData(UErrorCode &status) {
    if (U_FAILURE(status)) { return; }
    localeToAllowedHourFormatsMap = uhash_open(
        uhash_hashChars, uhash_compareChars, nullptr, &status);
    if (U_FAILURE(status)) { return; }

    uhash_setValueDeleter(localeToAllowedHourFormatsMap, deleteAllowedHourFormats);
    ucln_i18n_registerCleanup(UCLN_I18N_ALLOWED_HOUR_FORMATS, allowedHourFormatsCleanup);

    LocalUResourceBundlePointer rb(ures_openDirect(nullptr, "supplementalData", &status));
    if (U_FAILURE(status)) { return; }

    AllowedHourFormatsSink sink;
    // TODO: Currently in the enumeration each table allocates a new array.
    // Try to reduce the number of memory allocations. Consider storing a
    // UVector32 with the concatenation of all of the sub-arrays, put the start index
    // into the hashmap, store 6 single-value sub-arrays right at the beginning of the
    // vector (at index enum*2) for easy data sharing, copy sub-arrays into runtime
    // object. Remember to clean up the vector, too.
    ures_getAllItemsWithFallback(rb.getAlias(), "timeData", sink, status);    
}

static int32_t* getAllowedHourFormatsLangCountry(const char* language, const char* country, UErrorCode& status) {
    CharString langCountry;
    langCountry.append(language, status);
    langCountry.append('_', status);
    langCountry.append(country, status);

    int32_t* allowedFormats;
    allowedFormats = static_cast<int32_t*>(uhash_get(localeToAllowedHourFormatsMap, langCountry.data()));
    if (allowedFormats == nullptr) {
        allowedFormats = static_cast<int32_t*>(uhash_get(localeToAllowedHourFormatsMap, const_cast<char*>(country)));
    }

    return allowedFormats;
}

void DateTimePatternGenerator::getAllowedHourFormats(const Locale &locale, UErrorCode &status)&nbsp;{
    if (U_FAILURE(status)) { return; }

    const char *language = locale.getLanguage();
    CharString baseCountry = ulocimp_getRegionForSupplementalData(locale.getName(), false, status);
    const char* country = baseCountry.data();

    Locale maxLocale;  // must be here for correct lifetime
    if (*language == '\0' || *country == '\0') {
        maxLocale = locale;
        UErrorCode localStatus = U_ZERO_ERROR;
        maxLocale.addLikelySubtags(localStatus);
        if (U_SUCCESS(localStatus)) {
            language = maxLocale.getLanguage();
            country = maxLocale.getCountry();
        }
    }
    if (*language == '\0') {
        // Unexpected, but fail gracefully
        language = "und";
    }
    if (*country == '\0') {
        country = "001";
    }

    int32_t* allowedFormats = getAllowedHourFormatsLangCountry(language, country, status);

    // We need to check if there is an hour cycle on locale
    char buffer[8];
    int32_t count = locale.getKeywordValue("hours", buffer, sizeof(buffer), status);

    fDefaultHourFormatChar = 0;
    if (U_SUCCESS(status) && count > 0) {
        if(uprv_strcmp(buffer, "h24") == 0) {
            fDefaultHourFormatChar = LOW_K;
        } else if(uprv_strcmp(buffer, "h23") == 0) {
            fDefaultHourFormatChar = CAP_H;
        } else if(uprv_strcmp(buffer, "h12") == 0) {
            fDefaultHourFormatChar = LOW_H;
        } else if(uprv_strcmp(buffer, "h11") == 0) {
            fDefaultHourFormatChar = CAP_K;
        }
    }

    // Check if the region has an alias
    if (allowedFormats == nullptr) {
        UErrorCode localStatus = U_ZERO_ERROR;
        const Region* region = Region::getInstance(country, localStatus);
        if (U_SUCCESS(localStatus)) {
            country = region->getRegionCode(); // the real region code
            allowedFormats = getAllowedHourFormatsLangCountry(language, country, status);
        }
    }

    if (allowedFormats != nullptr) {  // Lookup is successful
        // Here allowedFormats points to a list consisting of key for preferredFormat,
        // followed by one or more keys for allowedFormats, then followed by ALLOWED_HOUR_FORMAT_UNKNOWN.
        if (!fDefaultHourFormatChar) {
            switch (allowedFormats[0]) {
                case ALLOWED_HOUR_FORMAT_h: fDefaultHourFormatChar = LOW_H; break;
                case ALLOWED_HOUR_FORMAT_H: fDefaultHourFormatChar = CAP_H; break;
                case ALLOWED_HOUR_FORMAT_K: fDefaultHourFormatChar = CAP_K; break;
                case ALLOWED_HOUR_FORMAT_k: fDefaultHourFormatChar = LOW_K; break;
                default: fDefaultHourFormatChar = CAP_H; break;
            }
        }

        for (int32_t i = 0; i < UPRV_LENGTHOF(fAllowedHourFormats); ++i) {
            fAllowedHourFormats[i] = allowedFormats[i + 1];
            if (fAllowedHourFormats[i] == ALLOWED_HOUR_FORMAT_UNKNOWN) {
                break;
            }
        }
    } else {  // Lookup failed, twice
        if (!fDefaultHourFormatChar) {
            fDefaultHourFormatChar = CAP_H;
        }
        fAllowedHourFormats[0] = ALLOWED_HOUR_FORMAT_H;
        fAllowedHourFormats[1] = ALLOWED_HOUR_FORMAT_UNKNOWN;
    }
}

UDateFormatHourCycle
DateTimePatternGenerator::getDefaultHourCycle(UErrorCode& status) const {
    if (U_FAILURE(status)) {
        return UDAT_HOUR_CYCLE_23;
    }
    if (fDefaultHourFormatChar == 0) {
        // We need to return something, but the caller should ignore it
        // anyways since the returned status is a failure.
        status = U_UNSUPPORTED_ERROR;
        return UDAT_HOUR_CYCLE_23;
    }
    switch (fDefaultHourFormatChar) {
        case CAP_K:
            return UDAT_HOUR_CYCLE_11;
        case LOW_H:
            return UDAT_HOUR_CYCLE_12;
        case CAP_H:
            return UDAT_HOUR_CYCLE_23;
        case LOW_K:
            return UDAT_HOUR_CYCLE_24;
        default:
            UPRV_UNREACHABLE_EXIT;
    }
}

UnicodeString
DateTimePatternGenerator::getSkeleton(const UnicodeString& pattern, UErrorCode&
/*status*/) {
    FormatParser fp2;
    DateTimeMatcher matcher;
    PtnSkeleton localSkeleton;
    matcher.set(pattern, &fp2, localSkeleton);
    return localSkeleton.getSkeleton();
}

UnicodeString
DateTimePatternGenerator::staticGetSkeleton(
        const UnicodeString& pattern, UErrorCode& /*status*/) {
    FormatParser fp;
    DateTimeMatcher matcher;
    PtnSkeleton localSkeleton;
    matcher.set(pattern, &fp, localSkeleton);
    return localSkeleton.getSkeleton();
}

UnicodeString
DateTimePatternGenerator::getBaseSkeleton(const UnicodeString& pattern, UErrorCode& /*status*/) {
    FormatParser fp2;
    DateTimeMatcher matcher;
    PtnSkeleton localSkeleton;
    matcher.set(pattern, &fp2, localSkeleton);
    return localSkeleton.getBaseSkeleton();
}

UnicodeString
DateTimePatternGenerator::staticGetBaseSkeleton(
        const UnicodeString& pattern, UErrorCode& /*status*/) {
    FormatParser fp;
    DateTimeMatcher matcher;
    PtnSkeleton localSkeleton;
    matcher.set(pattern, &fp, localSkeleton);
    return localSkeleton.getBaseSkeleton();
}

void
DateTimePatternGenerator::addICUPatterns(const Locale& locale, UErrorCode& status) {
    if (U_FAILURE(status)) {
        return;
    }
    
    LocalUResourceBundlePointer rb(ures_open(nullptr, locale.getBaseName(), &status));
    CharString calendarTypeToUse; // to be filled in with the type to use, if all goes well
    getCalendarTypeToUse(locale, calendarTypeToUse, status);

    // HACK to get around the fact that the old SimpleDateFormat code (actually, Calendar::getCalendarTypeForLocale() )
    // returns "gregorian" for ja_JP_TRADITIONAL instead of "japanese"
    if (uprv_strcmp(locale.getBaseName(), "ja_JP_TRADITIONAL") == 0) {
        calendarTypeToUse.clear().append("gregorian", status);
    }
    
    if (U_FAILURE(status)) {
        return;
    }

    // TODO: See ICU-22867
    CharString patternResourcePath;
    patternResourcePath.append(DT_DateTimeCalendarTag, status)
        .append('/', status)
        .append(calendarTypeToUse, status)
        .append('/', status)
        .append(DT_DateTimePatternsTag, status);

    LocalUResourceBundlePointer dateTimePatterns(ures_getByKeyWithFallback(rb.getAlias(), patternResourcePath.data(),
                                                                           nullptr, &status));
    if (ures_getType(dateTimePatterns.getAlias()) != URES_ARRAY || ures_getSize(dateTimePatterns.getAlias()) < 8) {
        status = U_INVALID_FORMAT_ERROR;
        return;
    }

    for (int32_t i = 0; U_SUCCESS(status) && i < DateFormat::kDateTime; i++) {
        LocalUResourceBundlePointer patternRes(ures_getByIndex(dateTimePatterns.getAlias(), i, nullptr, &status));
        UnicodeString pattern;
        switch (ures_getType(patternRes.getAlias())) {
            case URES_STRING:
                pattern = ures_getUnicodeString(patternRes.getAlias(), &status);
                break;
            case URES_ARRAY:
                pattern = ures_getUnicodeStringByIndex(patternRes.getAlias(), 0, &status);
                break;
            default:
                status = U_INVALID_FORMAT_ERROR;
                return;
        }
        
        if (U_SUCCESS(status)) {
            UnicodeString conflictingPattern;
            addPatternWithSkeleton(pattern, nullptr, false, conflictingPattern, status);
        }
    }
}

void
DateTimePatternGenerator::hackTimes(const UnicodeString& hackPattern, UErrorCode& status)  {
    UnicodeString conflictingString;

    fp->set(hackPattern);
    UnicodeString mmss;
    UBool gotMm=false;
    for (int32_t i=0; i<fp->itemNumber; ++i) {
        UnicodeString field = fp->items[i];
        if ( fp->isQuoteLiteral(field) ) {
            if ( gotMm ) {
               UnicodeString quoteLiteral;
               fp->getQuoteLiteral(quoteLiteral, &i);
               mmss += quoteLiteral;
            }
        }
        else {
            if (fp->isPatternSeparator(field) && gotMm) {
                mmss+=field;
            }
            else {
                char16_t ch=field.charAt(0);
                if (ch==LOW_M) {
                    gotMm=true;
                    mmss+=field;
                }
                else {
                    if (ch==LOW_S) {
                        if (!gotMm) {
                            break;
                        }
                        mmss+= field;
                        addPattern(mmss, false, conflictingString, status);
                        break;
                    }
                    else {
                        if (gotMm || ch==LOW_Z || ch==CAP_Z || ch==LOW_V || ch==CAP_V) {
                            break;
                        }
                    }
                }
            }
        }
    }
}

#define ULOC_LOCALE_IDENTIFIER_CAPACITY (ULOC_FULLNAME_CAPACITY + 1 + ULOC_KEYWORD_AND_VALUES_CAPACITY)

void
DateTimePatternGenerator::getCalendarTypeToUse(const Locale& locale, CharString&&nbsp;destination, UErrorCode& err) {
    destination.clear().append(DT_DateTimeGregorianTag, -1, err); // initial default
    if ( U_SUCCESS(err) ) {
        UErrorCode localStatus = U_ZERO_ERROR;
        char localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY];
        // obtain a locale that always has the calendar key value that should be used
        ures_getFunctionalEquivalent(
            localeWithCalendarKey,
            ULOC_LOCALE_IDENTIFIER_CAPACITY,
            nullptr,
            "calendar",
            "calendar",
            locale.getName(),
            nullptr,
            false,
            &localStatus);
        localeWithCalendarKey[ULOC_LOCALE_IDENTIFIER_CAPACITY-1] = 0; // ensure null termination
        // now get the calendar key value from that locale
        // (the call to ures_getFunctionalEquivalent() above might fail, and if it does, localeWithCalendarKey
        // won't contain a `calendar` keyword.  If this happens, the line below will stomp on `destination`,
        // so we have to check the return code before overwriting `destination`.)
        if (U_SUCCESS(localStatus)) {
            destination = ulocimp_getKeywordValue(localeWithCalendarKey, "calendar", localStatus);
        }
        // If the input locale was invalid, don't fail with missing resource error, instead
        // continue with default of Gregorian.
        if (U_FAILURE(localStatus) && localStatus != U_MISSING_RESOURCE_ERROR) {
            err = localStatus;
        }
    }
}

void
DateTimePatternGenerator::consumeShortTimePattern(const UnicodeString& shortTimePattern,
        UErrorCode& status) {
    if (U_FAILURE(status)) { return; }
    // ICU-20383 No longer set fDefaultHourFormatChar to the hour format character from
    // this pattern; instead it is set from localeToAllowedHourFormatsMap which now
    // includes entries for both preferred and allowed formats.

    // HACK for hh:ss
    hackTimes(shortTimePattern, status);
}

struct DateTimePatternGenerator::AppendItemFormatsSink : public ResourceSink {

    // Destination for data, modified via setters.
    DateTimePatternGenerator& dtpg;

    AppendItemFormatsSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {}
    virtual ~AppendItemFormatsSink();

    virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
            UErrorCode &errorCode) override {
        UDateTimePatternField field = dtpg.getAppendFormatNumber(key);
        if (field == UDATPG_FIELD_COUNT) { return; }
        const UnicodeString& valueStr = value.getUnicodeString(errorCode);
        if (dtpg.getAppendItemFormat(field).isEmpty() && !valueStr.isEmpty()) {
            dtpg.setAppendItemFormat(field, valueStr);
        }
    }

    void fillInMissing() {
        UnicodeString defaultItemFormat(true, UDATPG_ItemFormat, UPRV_LENGTHOF(UDATPG_ItemFormat)-1);  // Read-only alias.
        for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) {
            UDateTimePatternField field = static_cast<UDateTimePatternField>(i);
            if (dtpg.getAppendItemFormat(field).isEmpty()) {
                dtpg.setAppendItemFormat(field, defaultItemFormat);
            }
        }
    }
};

struct DateTimePatternGenerator::AppendItemNamesSink : public ResourceSink {

    // Destination for data, modified via setters.
    DateTimePatternGenerator& dtpg;

    AppendItemNamesSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {}
    virtual ~AppendItemNamesSink();

    virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
            UErrorCode &errorCode) override {
        UDateTimePGDisplayWidth width;
        UDateTimePatternField field = dtpg.getFieldAndWidthIndices(key, &width);
        if (field == UDATPG_FIELD_COUNT) { return; }
        ResourceTable detailsTable = value.getTable(errorCode);
        if (U_FAILURE(errorCode)) { return; }
        if (!detailsTable.findValue("dn", value)) { return; }
        const UnicodeString& valueStr = value.getUnicodeString(errorCode);
        if (U_SUCCESS(errorCode) && dtpg.getFieldDisplayName(field,width).isEmpty() && !valueStr.isEmpty()) {
            dtpg.setFieldDisplayName(field,width,valueStr);
        }
    }

    void fillInMissing() {
        for (int32_t i = 0; i < UDATPG_FIELD_COUNT; i++) {
            UnicodeString& valueStr = dtpg.getMutableFieldDisplayName(static_cast<UDateTimePatternField>(i), UDATPG_WIDE);
            if (valueStr.isEmpty()) {
                valueStr = CAP_F;
                U_ASSERT(i < 20);
                if (i < 10) {
                    // F0, F1, ..., F9
                    valueStr += static_cast<char16_t>(i + 0x30);
                } else {
                    // F10, F11, ...
                    valueStr += static_cast<char16_t>(0x31);
                    valueStr += static_cast<char16_t>(i - 10 + 0x30);
                }
                // NUL-terminate for the C API.
                valueStr.getTerminatedBuffer();
            }
            for (int32_t j = 1; j < UDATPG_WIDTH_COUNT; j++) {
                UnicodeString& valueStr2 = dtpg.getMutableFieldDisplayName(static_cast<UDateTimePatternField>(i), static_cast<UDateTimePGDisplayWidth>(j));
                if (valueStr2.isEmpty()) {
                    valueStr2 = dtpg.getFieldDisplayName(static_cast<UDateTimePatternField>(i), static_cast<UDateTimePGDisplayWidth>(j - 1));
                }
            }
        }
    }
};

struct DateTimePatternGenerator::AvailableFormatsSink : public ResourceSink {

    // Destination for data, modified via setters.
    DateTimePatternGenerator& dtpg;

    // Temporary variable, required for calling addPatternWithSkeleton.
    UnicodeString conflictingPattern;

    AvailableFormatsSink(DateTimePatternGenerator& _dtpg) : dtpg(_dtpg) {}
    virtual ~AvailableFormatsSink();

    virtual void put(const char *key, ResourceValue &value, UBool /*isRoot*/,
            UErrorCode &errorCode) override {
        const UnicodeString formatKey(key, -1, US_INV);
        if (!dtpg.isAvailableFormatSet(formatKey) ) {
            dtpg.setAvailableFormat(formatKey, errorCode);
            // Add pattern with its associated skeleton. Override any duplicate
            // derived from std patterns, but not a previous availableFormats entry:
            const UnicodeString& formatValue = value.getUnicodeString(errorCode);
            conflictingPattern.remove();
            dtpg.addPatternWithSkeleton(formatValue, &formatKey, true, conflictingPattern, errorCode);
        }
    }
};

// Virtual destructors must be defined out of line.
DateTimePatternGenerator::AppendItemFormatsSink::~AppendItemFormatsSink() {}
DateTimePatternGenerator::AppendItemNamesSink::~AppendItemNamesSink() {}
DateTimePatternGenerator::AvailableFormatsSink::~AvailableFormatsSink() {}

void
DateTimePatternGenerator::addCLDRData(const Locale& locale, UErrorCode& errorCode) {
    if (U_FAILURE(errorCode)) { return; }
    UnicodeString rbPattern, value, field;
    CharString path;

    LocalUResourceBundlePointer rb(ures_open(nullptr, locale.getName(), &errorCode));
    if (U_FAILURE(errorCode)) { return; }

    CharString calendarTypeToUse; // to be filled in with the type to use, if all goes well
    getCalendarTypeToUse(locale, calendarTypeToUse, errorCode);
    if (U_FAILURE(errorCode)) { return; }

    // Local err to ignore resource not found exceptions
    UErrorCode err = U_ZERO_ERROR;

    // Load append item formats.
    AppendItemFormatsSink appendItemFormatsSink(*this);
    path.clear()
        .append(DT_DateTimeCalendarTag, errorCode)
        .append('/', errorCode)
        .append(calendarTypeToUse, errorCode)
        .append('/', errorCode)
        .append(DT_DateTimeAppendItemsTag, errorCode); // i.e., calendar/xxx/appendItems
    if (U_FAILURE(errorCode)) { return; }
    ures_getAllChildrenWithFallback(rb.getAlias(), path.data(), appendItemFormatsSink, err);
    appendItemFormatsSink.fillInMissing();

    // Load CLDR item names.
    err = U_ZERO_ERROR;
    AppendItemNamesSink appendItemNamesSink(*this);
    ures_getAllChildrenWithFallback(rb.getAlias(), DT_DateTimeFieldsTag, appendItemNamesSink, err);
    appendItemNamesSink.fillInMissing();

    // Load the available formats from CLDR.
    err = U_ZERO_ERROR;
    initHashtable(errorCode);
    if (U_FAILURE(errorCode)) { return; }
    AvailableFormatsSink availableFormatsSink(*this);
    path.clear()
        .append(DT_DateTimeCalendarTag, errorCode)
        .append('/', errorCode)
        .append(calendarTypeToUse, errorCode)
        .append('/', errorCode)
        .append(DT_DateTimeAvailableFormatsTag, errorCode); // i.e., calendar/xxx/availableFormats
    if (U_FAILURE(errorCode)) { return; }
    ures_getAllChildrenWithFallback(rb.getAlias(), path.data(), availableFormatsSink, err);
}

void
DateTimePatternGenerator::initHashtable(UErrorCode& err) {
    if (U_FAILURE(err)) { return; }
    if (fAvailableFormatKeyHash!=nullptr) {
        return;
    }
    LocalPointer<Hashtable> hash(new Hashtable(false, err), err);
    if (U_SUCCESS(err)) {
        fAvailableFormatKeyHash = hash.orphan();
    }
}

void
DateTimePatternGenerator::setAppendItemFormat(UDateTimePatternField field, const UnicodeString& value) {
    appendItemFormats[field] = value;
    // NUL-terminate for the C API.
    appendItemFormats[field].getTerminatedBuffer();
}

const UnicodeString&
DateTimePatternGenerator::getAppendItemFormat(UDateTimePatternField field) const {
    return appendItemFormats[field];
}

void
DateTimePatternGenerator::setAppendItemName(UDateTimePatternField field, const UnicodeString& value) {
    setFieldDisplayName(field, UDATPG_WIDTH_APPENDITEM, value);
}

const UnicodeString&
DateTimePatternGenerator::getAppendItemName(UDateTimePatternField field) const {
    return fieldDisplayNames[field][UDATPG_WIDTH_APPENDITEM];
}

void
DateTimePatternGenerator::setFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width, const UnicodeString& value) {
    fieldDisplayNames[field][width] = value;
    // NUL-terminate for the C API.
    fieldDisplayNames[field][width].getTerminatedBuffer();
}

UnicodeString
DateTimePatternGenerator::getFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) const {
    return fieldDisplayNames[field][width];
}

UnicodeString&
DateTimePatternGenerator::getMutableFieldDisplayName(UDateTimePatternField field, UDateTimePGDisplayWidth width) {
    return fieldDisplayNames[field][width];
}

void
DateTimePatternGenerator::getAppendName(UDateTimePatternField field, UnicodeString&&nbsp;value) {
    value = SINGLE_QUOTE;
    value += fieldDisplayNames[field][UDATPG_WIDTH_APPENDITEM];
    value += SINGLE_QUOTE;
}

UnicodeString
DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UErrorCode& status) {
    return getBestPattern(patternForm, UDATPG_MATCH_NO_OPTIONS, status);
}

UnicodeString
DateTimePatternGenerator::getBestPattern(const UnicodeString& patternForm, UDateTimePatternMatchOptions options, UErrorCode& status) {
    if (U_FAILURE(status)) {
        return {};
    }
    if (U_FAILURE(internalErrorCode)) {
        status = internalErrorCode;
        return {};
    }
    const UnicodeString *bestPattern = nullptr;
    UnicodeString dtFormat;
    UnicodeString resultPattern;
    int32_t flags = kDTPGNoFlags;

    int32_t dateMask=(1<<UDATPG_DAYPERIOD_FIELD) - 1;
    int32_t timeMask=(1<<UDATPG_FIELD_COUNT) - 1 - dateMask;

    // Replace hour metacharacters 'j', 'C' and 'J', set flags as necessary
    UnicodeString patternFormMapped = mapSkeletonMetacharacters(patternForm, &flags, status);
    if (U_FAILURE(status)) {
        return {};
    }

    resultPattern.remove();
    dtMatcher->set(patternFormMapped, fp);
    const PtnSkeleton* specifiedSkeleton = nullptr;
    bestPattern=getBestRaw(*dtMatcher, -1, distanceInfo, status, &specifiedSkeleton);
    if (U_FAILURE(status)) {
        return {};
    }

    if ( distanceInfo->missingFieldMask==0 && distanceInfo->extraFieldMask==0 ) {
        resultPattern = adjustFieldTypes(*bestPattern, specifiedSkeleton, flags, options);

        return resultPattern;
    }
    int32_t neededFields = dtMatcher->getFieldMask();
    UnicodeString datePattern=getBestAppending(neededFields & dateMask, flags, status, options);
    UnicodeString timePattern=getBestAppending(neededFields & timeMask, flags, status, options);
    if (U_FAILURE(status)) {
        return {};
    }
    if (datePattern.length()==0) {
        if (timePattern.length()==0) {
            resultPattern.remove();
        }
        else {
            return timePattern;
        }
    }
    if (timePattern.length()==0) {
        return datePattern;
    }
    resultPattern.remove();
    status = U_ZERO_ERROR;
    // determine which dateTimeFormat to use
    PtnSkeleton* reqSkeleton = dtMatcher->getSkeletonPtr();
    UDateFormatStyle style = UDAT_SHORT;
    int32_t monthFieldLen = reqSkeleton->baseOriginal.getFieldLength(UDATPG_MONTH_FIELD);
    if (monthFieldLen == 4) {
        if (reqSkeleton->baseOriginal.getFieldLength(UDATPG_WEEKDAY_FIELD) > 0) {
            style = UDAT_FULL;
        } else {
            style = UDAT_LONG;
        }
    } else if (monthFieldLen == 3) {
        style = UDAT_MEDIUM;
    }
    // and now use it to compose date and time
    dtFormat=getDateTimeFormat(style, status);
    SimpleFormatter(dtFormat, 2, 2, status).format(timePattern, datePattern, resultPattern, status);
    return resultPattern;
}

/*
 * Map a skeleton that may have metacharacters jJC to one without, by replacing
 * the metacharacters with locale-appropriate fields of h/H/k/K and of a/b/B
 * (depends on fDefaultHourFormatChar and fAllowedHourFormats being set, which in
 * turn depends on initData having been run). This method also updates the flags
 * as necessary. Returns the updated skeleton.
 */

UnicodeString
DateTimePatternGenerator::mapSkeletonMetacharacters(const UnicodeString& patternForm, int32_t* flags, UErrorCode& status) {
    UnicodeString patternFormMapped;
    patternFormMapped.remove();
    UBool inQuoted = false;
    int32_t patPos, patLen = patternForm.length();
    for (patPos = 0; patPos < patLen; patPos++) {
        char16_t patChr = patternForm.charAt(patPos);
        if (patChr == SINGLE_QUOTE) {
            inQuoted = !inQuoted;
        } else if (!inQuoted) {
            // Handle special mappings for 'j' and 'C' in which fields lengths
            // 1,3,5 => hour field length 1
            // 2,4,6 => hour field length 2
            // 1,2 => abbreviated dayPeriod (field length 1..3)
            // 3,4 => long dayPeriod (field length 4)
            // 5,6 => narrow dayPeriod (field length 5)
            if (patChr == LOW_J || patChr == CAP_C) {
                int32_t extraLen = 0; // 1 less than total field length
                while (patPos+1 < patLen && patternForm.charAt(patPos+1)==patChr) {
                    extraLen++;
                    patPos++;
                }
                int32_t hourLen = 1 + (extraLen & 1);
                int32_t dayPeriodLen = (extraLen < 2)? 1: 3 + (extraLen >> 1);
                char16_t hourChar = LOW_H;
                char16_t dayPeriodChar = LOW_A;
                if (patChr == LOW_J) {
                    hourChar = fDefaultHourFormatChar;
                } else {
                    AllowedHourFormat bestAllowed;
                    if (fAllowedHourFormats[0] != ALLOWED_HOUR_FORMAT_UNKNOWN) {
                        bestAllowed = static_cast<AllowedHourFormat>(fAllowedHourFormats[0]);
                    } else {
                        status = U_INVALID_FORMAT_ERROR;
                        return {};
                    }
                    if (bestAllowed == ALLOWED_HOUR_FORMAT_H || bestAllowed == ALLOWED_HOUR_FORMAT_HB || bestAllowed == ALLOWED_HOUR_FORMAT_Hb) {
                        hourChar = CAP_H;
                    } else if (bestAllowed == ALLOWED_HOUR_FORMAT_K || bestAllowed == ALLOWED_HOUR_FORMAT_KB || bestAllowed == ALLOWED_HOUR_FORMAT_Kb) {
                        hourChar = CAP_K;
                    } else if (bestAllowed == ALLOWED_HOUR_FORMAT_k) {
                        hourChar = LOW_K;
                    }
                    // in #13183 just add b/B to skeleton, no longer need to set special flags
                    if (bestAllowed == ALLOWED_HOUR_FORMAT_HB || bestAllowed == ALLOWED_HOUR_FORMAT_hB || bestAllowed == ALLOWED_HOUR_FORMAT_KB) {
                        dayPeriodChar = CAP_B;
                    } else if (bestAllowed == ALLOWED_HOUR_FORMAT_Hb || bestAllowed == ALLOWED_HOUR_FORMAT_hb || bestAllowed == ALLOWED_HOUR_FORMAT_Kb) {
                        dayPeriodChar = LOW_B;
                    }
                }
                if (hourChar==CAP_H || hourChar==LOW_K) {
                    dayPeriodLen = 0;
                }
                while (dayPeriodLen-- > 0) {
                    patternFormMapped.append(dayPeriodChar);
                }
                while (hourLen-- > 0) {
                    patternFormMapped.append(hourChar);
                }
            } else if (patChr == CAP_J) {
                // Get pattern for skeleton with H, then replace H or k
                // with fDefaultHourFormatChar (if different)
                patternFormMapped.append(CAP_H);
                *flags |= kDTPGSkeletonUsesCapJ;
            } else {
                patternFormMapped.append(patChr);
            }
        }
    }
    return patternFormMapped;
}

UnicodeString
DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern,
                                            const UnicodeString& skeleton,
                                            UErrorCode& status) {
    return replaceFieldTypes(pattern, skeleton, UDATPG_MATCH_NO_OPTIONS, status);
}

UnicodeString
DateTimePatternGenerator::replaceFieldTypes(const UnicodeString& pattern,
                                            const UnicodeString& skeleton,
                                            UDateTimePatternMatchOptions options,
                                            UErrorCode& status) {
    if (U_FAILURE(status)) {
        return {};
    }
    if (U_FAILURE(internalErrorCode)) {
        status = internalErrorCode;
        return {};
    }
    dtMatcher->set(skeleton, fp);
    UnicodeString result = adjustFieldTypes(pattern, nullptr, kDTPGNoFlags, options);
    return result;
}

void
DateTimePatternGenerator::setDecimal(const UnicodeString& newDecimal) {
    this->decimal = newDecimal;
    // NUL-terminate for the C API.
    this->decimal.getTerminatedBuffer();
}

const UnicodeString&
DateTimePatternGenerator::getDecimal() const {
    return decimal;
}

void
DateTimePatternGenerator::addCanonicalItems(UErrorCode& status) {
    if (U_FAILURE(status)) { return; }
    UnicodeString  conflictingPattern;

    for (int32_t i=0; i<UDATPG_FIELD_COUNT; i++) {
        if (Canonical_Items[i] > 0) {
            addPattern(UnicodeString(Canonical_Items[i]), false, conflictingPattern, status);
        }
        if (U_FAILURE(status)) { return; }
    }
}

void
DateTimePatternGenerator::setDateTimeFormat(const UnicodeString& dtFormat) {
    UErrorCode status = U_ZERO_ERROR;
    for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) {
        setDateTimeFormat(static_cast<UDateFormatStyle>(style), dtFormat, status);
    }
}

const UnicodeString&
DateTimePatternGenerator::getDateTimeFormat() const {
    UErrorCode status = U_ZERO_ERROR;
    return getDateTimeFormat(UDAT_MEDIUM, status);
}

void
DateTimePatternGenerator::setDateTimeFormat(UDateFormatStyle style, const UnicodeString& dtFormat, UErrorCode& status) {
    if (U_FAILURE(status)) {
        return;
    }
    if (style < UDAT_FULL || style > UDAT_SHORT) {
        status = U_ILLEGAL_ARGUMENT_ERROR;
        return;
    }
    dateTimeFormat[style] = dtFormat;
    // Note for the following: getTerminatedBuffer() can re-allocate the UnicodeString
    // buffer so we do this here before clients request a const ref to the UnicodeString
    // or its buffer.
    dateTimeFormat[style].getTerminatedBuffer(); // NUL-terminate for the C API.
}

const UnicodeString&
DateTimePatternGenerator::getDateTimeFormat(UDateFormatStyle style, UErrorCode& ;status) const {
    static const UnicodeString emptyString = UNICODE_STRING_SIMPLE("");
    if (U_FAILURE(status)) {
        return emptyString;
    }
    if (style < UDAT_FULL || style > UDAT_SHORT) {
        status = U_ILLEGAL_ARGUMENT_ERROR;
        return emptyString;
    }
    return dateTimeFormat[style];
}

static const int32_t cTypeBufMax = 32;

void
DateTimePatternGenerator::setDateTimeFromCalendar(const Locale& locale, UErrorCode& status) {
    if (U_FAILURE(status)) { return; }

    const char16_t *resStr;
    int32_t resStrLen = 0;

    LocalUResourceBundlePointer calData(ures_open(nullptr, locale.getBaseName(), &status));
    if (U_FAILURE(status)) { return; }
    ures_getByKey(calData.getAlias(), DT_DateTimeCalendarTag, calData.getAlias(), &status);
    if (U_FAILURE(status)) { return; }

    char cType[cTypeBufMax + 1];
    Calendar::getCalendarTypeFromLocale(locale, cType, cTypeBufMax, status);
    cType[cTypeBufMax] = 0;
    if (U_FAILURE(status) || cType[0] == 0) {
        status = U_ZERO_ERROR;
        uprv_strcpy(cType, DT_DateTimeGregorianTag);
    }
    UBool cTypeIsGregorian = (uprv_strcmp(cType, DT_DateTimeGregorianTag) == 0);

    // Currently, for compatibility with pre-CLDR-42 data, we default to the "atTime"
    // combining patterns. Depending on guidance in CLDR 42 spec and on DisplayOptions,
    // we may change this.
    LocalUResourceBundlePointer specificCalBundle;
    LocalUResourceBundlePointer dateTimePatterns;
    int32_t dateTimeOffset = 0; // initially for DateTimePatterns%atTime
    if (!cTypeIsGregorian) {
        specificCalBundle.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), cType,
                                        nullptr, &status));
        dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(specificCalBundle.getAlias(), DT_DateAtTimePatternsTag, // the %atTime variant, 4 entries
                                        nullptr, &status));
    }
    if (dateTimePatterns.isNull() || status == U_MISSING_RESOURCE_ERROR) {
        status = U_ZERO_ERROR;
        specificCalBundle.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), DT_DateTimeGregorianTag,
                                        nullptr, &status));
        dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(specificCalBundle.getAlias(), DT_DateAtTimePatternsTag, // the %atTime variant, 4 entries
                                        nullptr, &status));
    }
    if (U_SUCCESS(status) && (ures_getSize(dateTimePatterns.getAlias()) < 4)) {
        status = U_INVALID_FORMAT_ERROR;
    }
    if (status == U_MISSING_RESOURCE_ERROR) {
        // Try again with standard variant
        status = U_ZERO_ERROR;
        dateTimePatterns.orphan();
        dateTimeOffset = static_cast<int32_t>(DateFormat::kDateTimeOffset);
        if (!cTypeIsGregorian) {
            specificCalBundle.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), cType,
                                            nullptr, &status));
            dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(specificCalBundle.getAlias(), DT_DateTimePatternsTag, // the standard variant, 13 entries
                                            nullptr, &status));
        }
        if (dateTimePatterns.isNull() || status == U_MISSING_RESOURCE_ERROR) {
            status = U_ZERO_ERROR;
            specificCalBundle.adoptInstead(ures_getByKeyWithFallback(calData.getAlias(), DT_DateTimeGregorianTag,
                                            nullptr, &status));
            dateTimePatterns.adoptInstead(ures_getByKeyWithFallback(specificCalBundle.getAlias(), DT_DateTimePatternsTag, // the standard variant, 13 entries
                                            nullptr, &status));
        }
        if (U_SUCCESS(status) && (ures_getSize(dateTimePatterns.getAlias()) <= DateFormat::kDateTimeOffset + DateFormat::kShort)) {
            status = U_INVALID_FORMAT_ERROR;
        }
    }
    if (U_FAILURE(status)) { return; }
    for (int32_t style = UDAT_FULL; style <= UDAT_SHORT; style++) {
        resStr = ures_getStringByIndex(dateTimePatterns.getAlias(), dateTimeOffset + style, &resStrLen,&nbsp;&status);
        setDateTimeFormat(static_cast<UDateFormatStyle>(style), UnicodeString(true, resStr, resStrLen), status);
    }
}

void
DateTimePatternGenerator::setDecimalSymbols(const Locale& locale, UErrorCode&&nbsp;status) {
    DecimalFormatSymbols dfs = DecimalFormatSymbols(locale, status);
    if(U_SUCCESS(status)) {
        decimal = dfs.getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol);
        // NUL-terminate for the C API.
        decimal.getTerminatedBuffer();
    }
}

UDateTimePatternConflict
DateTimePatternGenerator::addPattern(
    const UnicodeString& pattern,
    UBool override,
    UnicodeString &conflictingPattern,
    UErrorCode& status)
{
    if (U_FAILURE(internalErrorCode)) {
        status = internalErrorCode;
        return UDATPG_NO_CONFLICT;
    }

    return addPatternWithSkeleton(pattern, nullptr, override, conflictingPattern, status);
}

// For DateTimePatternGenerator::addPatternWithSkeleton -
// If skeletonToUse is specified, then an availableFormats entry is being added. In this case:
// 1. We pass that skeleton to matcher.set instead of having it derive a skeleton from the pattern.
// 2. If the new entry's skeleton or basePattern does match an existing entry but that entry also had a skeleton specified
// (i.e. it was also from availableFormats), then the new entry does not override it regardless of the value of the override
// parameter. This prevents later availableFormats entries from a parent locale overriding earlier ones from the actual
// specified locale. However, availableFormats entries *should* override entries with matching skeleton whose skeleton was
// derived (i.e. entries derived from the standard date/time patters for the specified locale).
// 3. When adding the pattern (patternMap->add), we set a new boolean to indicate that the added entry had a
// specified skeleton (which sets a new field in the PtnElem in the PatternMap).
UDateTimePatternConflict
DateTimePatternGenerator::addPatternWithSkeleton(
    const UnicodeString& pattern,
    const UnicodeString* skeletonToUse,
    UBool override,
    UnicodeString& conflictingPattern,
    UErrorCode& status)
{
    if (U_FAILURE(internalErrorCode)) {
        status = internalErrorCode;
        return UDATPG_NO_CONFLICT;
    }

    UnicodeString basePattern;
    PtnSkeleton   skeleton;
    UDateTimePatternConflict conflictingStatus = UDATPG_NO_CONFLICT;

    DateTimeMatcher matcher;
    if ( skeletonToUse == nullptr ) {
        matcher.set(pattern, fp, skeleton);
        matcher.getBasePattern(basePattern);
    } else {
        matcher.set(*skeletonToUse, fp, skeleton); // no longer trims skeleton fields to max len 3, per #7930
        matcher.getBasePattern(basePattern); // or perhaps instead: basePattern = *skeletonToUse;
    }
    // We only care about base conflicts - and replacing the pattern associated with a base - if:
    // 1. the conflicting previous base pattern did *not* have an explicit skeleton; in that case the previous
    // base + pattern combination was derived from either (a) a canonical item, (b) a standard format, or
    // (c) a pattern specified programmatically with a previous call to addPattern (which would only happen
    // if we are getting here from a subsequent call to addPattern).
    // 2. a skeleton is specified for the current pattern, but override=false; in that case we are checking
    // availableFormats items from root, which should not override any previous entry with the same base.
    UBool entryHadSpecifiedSkeleton;
    const UnicodeString *duplicatePattern = patternMap->getPatternFromBasePattern(basePattern, entryHadSpecifiedSkeleton);
    if (duplicatePattern != nullptr && (!entryHadSpecifiedSkeleton || (skeletonToUse != nullptr && !override))) {
        conflictingStatus = UDATPG_BASE_CONFLICT;
        conflictingPattern = *duplicatePattern;
        if (!override) {
            return conflictingStatus;
        }
    }
    // The only time we get here with override=true and skeletonToUse!=null is when adding availableFormats
    // items from CLDR data. In that case, we don't want an item from a parent locale to replace an item with
    // same skeleton from the specified locale, so skip the current item if skeletonWasSpecified is true for
    // the previously-specified conflicting item.
    const PtnSkeleton* entrySpecifiedSkeleton = nullptr;
    duplicatePattern = patternMap->getPatternFromSkeleton(skeleton, &entrySpecifiedSkeleton);
    if (duplicatePattern != nullptr ) {
        conflictingStatus = UDATPG_CONFLICT;
        conflictingPattern = *duplicatePattern;
        if (!override || (skeletonToUse != nullptr && entrySpecifiedSkeleton != nullptr)) {
            return conflictingStatus;
        }
    }
    patternMap->add(basePattern, skeleton, pattern, skeletonToUse != nullptr, status);
    if(U_FAILURE(status)) {
        return conflictingStatus;
    }

    return UDATPG_NO_CONFLICT;
}


UDateTimePatternField
DateTimePatternGenerator::getAppendFormatNumber(const char* field) const {
    for (int32_t i=0; i<UDATPG_FIELD_COUNT; ++i ) {
        if (uprv_strcmp(CLDR_FIELD_APPEND[i], field)==0) {
            return static_cast<UDateTimePatternField>(i);
        }
    }
    return UDATPG_FIELD_COUNT;
}

UDateTimePatternField
DateTimePatternGenerator::getFieldAndWidthIndices(const char* key, UDateTimePGDisplayWidth* widthP) const {
    char cldrFieldKey[UDATPG_FIELD_KEY_MAX + 1];
    uprv_strncpy(cldrFieldKey, key, UDATPG_FIELD_KEY_MAX);
    cldrFieldKey[UDATPG_FIELD_KEY_MAX]=0; // ensure termination
    *widthP = UDATPG_WIDE;
    char* hyphenPtr = uprv_strchr(cldrFieldKey, '-');
    if (hyphenPtr) {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=89 H=92 G=90

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.103Bemerkung:  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.