/* Function: InvokeCallbackForDateTimePattern Gets the DateTime pattern for the specified skeleton and invokes the callback with the retrieved value. */ static int InvokeCallbackForDateTimePattern(const char* locale, const UChar* patternSkeleton, EnumCalendarInfoCallback callback, const void* context) { UErrorCode err = U_ZERO_ERROR; UDateTimePatternGenerator* pGenerator = udatpg_open(locale, &err); if (U_FAILURE(err)) return FALSE; UErrorCode ignore = U_ZERO_ERROR; int32_t patternLen = udatpg_getBestPattern(pGenerator, patternSkeleton, -1, NULL, 0, &ignore) + 1; UChar* bestPattern = calloc(patternLen, sizeof(UChar)); if (bestPattern == NULL) { udatpg_close(pGenerator); return FALSE; } udatpg_getBestPattern(pGenerator, patternSkeleton, -1, bestPattern, patternLen, &err); udatpg_close(pGenerator); if (U_SUCCESS(err)) { callback(bestPattern, context); } free(bestPattern); return UErrorCodeToBool(err); }
/* Function: GetMonthDayPattern Gets the Month-Day DateTime pattern for the specified locale. */ static ResultCode GetMonthDayPattern(const char* locale, UChar* sMonthDay, int32_t stringCapacity) { UErrorCode err = U_ZERO_ERROR; UDateTimePatternGenerator* pGenerator = udatpg_open(locale, &err); udatpg_getBestPattern(pGenerator, UDAT_MONTH_DAY_UCHAR, -1, sMonthDay, stringCapacity, &err); udatpg_close(pGenerator); return GetResultCode(err); }
static String getFormatForSkeleton(const char* locale, const String& skeleton) { String format = ASCIILiteral("yyyy-MM"); UErrorCode status = U_ZERO_ERROR; UDateTimePatternGenerator* patternGenerator = udatpg_open(locale, &status); if (!patternGenerator) return format; status = U_ZERO_ERROR; int32_t length = udatpg_getBestPattern(patternGenerator, skeleton.characters(), skeleton.length(), 0, 0, &status); if (status == U_BUFFER_OVERFLOW_ERROR && length) { Vector<UChar> buffer(length); status = U_ZERO_ERROR; udatpg_getBestPattern(patternGenerator, skeleton.characters(), skeleton.length(), buffer.data(), length, &status); if (U_SUCCESS(status)) format = String::adopt(buffer); } udatpg_close(patternGenerator); return format; }
static void TestBuilder() { UErrorCode errorCode=U_ZERO_ERROR; UDateTimePatternGenerator *dtpg; UDateTimePatternConflict conflict; UEnumeration *en; UChar result[20]; int32_t length, pLength; const UChar *s, *p; const UChar* ptrResult[2]; int32_t count=0; UDateTimePatternGenerator *generator; int32_t formattedCapacity, resultLen,patternCapacity ; UChar pattern[40], formatted[40]; UDateFormat *formatter; UDate sampleDate = 837039928046.0; static const char locale[]= "fr"; UErrorCode status=U_ZERO_ERROR; /* test create an empty DateTimePatternGenerator */ dtpg=udatpg_openEmpty(&errorCode); if(U_FAILURE(errorCode)) { log_err("udatpg_openEmpty() failed - %s\n", u_errorName(errorCode)); return; } /* Add a pattern */ conflict = udatpg_addPattern(dtpg, redundantPattern, 5, FALSE, result, 20, &length, &errorCode); if(U_FAILURE(errorCode)) { log_err("udatpg_addPattern() failed - %s\n", u_errorName(errorCode)); return; } /* Add a redundant pattern */ conflict = udatpg_addPattern(dtpg, redundantPattern, 5, FALSE, result, 20, &length, &errorCode); if(conflict == UDATPG_NO_CONFLICT) { log_err("udatpg_addPattern() failed to find the duplicate pattern.\n"); return; } /* Test pattern == NULL */ s=NULL; length = udatpg_addPattern(dtpg, s, 0, FALSE, result, 20, &length, &errorCode); if(!U_FAILURE(errorCode)&&(length!=0) ) { log_err("udatpg_addPattern failed in illegal argument - pattern is NULL.\n"); return; } /* replace field type */ errorCode=U_ZERO_ERROR; conflict = udatpg_addPattern(dtpg, testPattern2, 7, FALSE, result, 20, &length, &errorCode); if((conflict != UDATPG_NO_CONFLICT)||U_FAILURE(errorCode)) { log_err("udatpg_addPattern() failed to add HH:mm v. - %s\n", u_errorName(errorCode)); return; } length = udatpg_replaceFieldTypes(dtpg, testPattern2, 7, replacedStr, 4, result, 20, &errorCode); if (U_FAILURE(errorCode) || (length==0) ) { log_err("udatpg_replaceFieldTypes failed!\n"); return; } /* Get all skeletons and the crroespong pattern for each skeleton. */ ptrResult[0] = testPattern2; ptrResult[1] = redundantPattern; count=0; en = udatpg_openSkeletons(dtpg, &errorCode); if (U_FAILURE(errorCode) || (length==0) ) { log_err("udatpg_openSkeletons failed!\n"); return; } while ( (s=uenum_unext(en, &length, &errorCode))!= NULL) { p = udatpg_getPatternForSkeleton(dtpg, s, length, &pLength); if (U_FAILURE(errorCode) || p==NULL || u_memcmp(p, ptrResult[count], pLength)!=0 ) { log_err("udatpg_getPatternForSkeleton failed!\n"); return; } count++; } uenum_close(en); /* Get all baseSkeletons */ en = udatpg_openBaseSkeletons(dtpg, &errorCode); count=0; while ( (s=uenum_unext(en, &length, &errorCode))!= NULL) { p = udatpg_getPatternForSkeleton(dtpg, s, length, &pLength); if (U_FAILURE(errorCode) || p==NULL || u_memcmp(p, resultBaseSkeletons[count], pLength)!=0 ) { log_err("udatpg_getPatternForSkeleton failed!\n"); return; } count++; } if (U_FAILURE(errorCode) || (length==0) ) { log_err("udatpg_openSkeletons failed!\n"); return; } uenum_close(en); udatpg_close(dtpg); /* sample code in Userguide */ patternCapacity = (int32_t)(sizeof(pattern)/sizeof((pattern)[0])); status=U_ZERO_ERROR; generator=udatpg_open(locale, &status); if(U_FAILURE(status)) { return; } /* get a pattern for an abbreviated month and day */ length = udatpg_getBestPattern(generator, skeleton, 4, pattern, patternCapacity, &status); formatter = udat_open(UDAT_IGNORE, UDAT_DEFAULT, locale, timeZoneGMT, -1, pattern, length, &status); if (formatter==NULL) { log_err("Failed to initialize the UDateFormat of the sample code in Userguide.\n"); udatpg_close(generator); return; } /* use it to format (or parse) */ formattedCapacity = (int32_t)(sizeof(formatted)/sizeof((formatted)[0])); resultLen=udat_format(formatter, ucal_getNow(), formatted, formattedCapacity, NULL, &status); /* for French, the result is "13 sept." */ /* cannot use the result from ucal_getNow() because the value change evreyday. */ resultLen=udat_format(formatter, sampleDate, formatted, formattedCapacity, NULL, &status); if ( u_memcmp(sampleFormatted, formatted, resultLen) != 0 ) { log_err("Failed udat_format() of sample code in Userguide.\n"); } udatpg_close(generator); udat_close(formatter); }
static void TestUsage() { UErrorCode errorCode=U_ZERO_ERROR; UDateTimePatternGenerator *dtpg; UChar bestPattern[20]; UChar result[20]; int32_t length; UChar *s; const UChar *r; dtpg=udatpg_open("fi", &errorCode); if(U_FAILURE(errorCode)) { log_err_status(errorCode, "udatpg_open(fi) failed - %s\n", u_errorName(errorCode)); return; } length = udatpg_getBestPattern(dtpg, testSkeleton1, 4, bestPattern, 20, &errorCode); if(U_FAILURE(errorCode)) { log_err("udatpg_getBestPattern failed - %s\n", u_errorName(errorCode)); return; } if((u_memcmp(bestPattern, expectingBestPattern, length)!=0) || bestPattern[length]!=0) { log_err("udatpg_getBestPattern did not return the expected string\n"); return; } /* Test skeleton == NULL */ s=NULL; length = udatpg_getBestPattern(dtpg, s, 0, bestPattern, 20, &errorCode); if(!U_FAILURE(errorCode)&&(length!=0) ) { log_err("udatpg_getBestPattern failed in illegal argument - skeleton is NULL.\n"); return; } /* Test udatpg_getSkeleton */ length = udatpg_getSkeleton(dtpg, testPattern, 5, result, 20, &errorCode); if(U_FAILURE(errorCode)) { log_err("udatpg_getSkeleton failed - %s\n", u_errorName(errorCode)); return; } if((u_memcmp(result, expectingSkeleton, length)!=0) || result[length]!=0) { log_err("udatpg_getSkeleton did not return the expected string\n"); return; } /* Test pattern == NULL */ s=NULL; length = udatpg_getSkeleton(dtpg, s, 0, result, 20, &errorCode); if(!U_FAILURE(errorCode)&&(length!=0) ) { log_err("udatpg_getSkeleton failed in illegal argument - pattern is NULL.\n"); return; } /* Test udatpg_getBaseSkeleton */ length = udatpg_getBaseSkeleton(dtpg, testPattern, 5, result, 20, &errorCode); if(U_FAILURE(errorCode)) { log_err("udatpg_getBaseSkeleton failed - %s\n", u_errorName(errorCode)); return; } if((u_memcmp(result, expectingBaseSkeleton, length)!=0) || result[length]!=0) { log_err("udatpg_getBaseSkeleton did not return the expected string\n"); return; } /* Test pattern == NULL */ s=NULL; length = udatpg_getBaseSkeleton(dtpg, s, 0, result, 20, &errorCode); if(!U_FAILURE(errorCode)&&(length!=0) ) { log_err("udatpg_getBaseSkeleton failed in illegal argument - pattern is NULL.\n"); return; } /* set append format to {1}{0} */ udatpg_setAppendItemFormat( dtpg, UDATPG_MONTH_FIELD, testFormat, 7 ); r = udatpg_getAppendItemFormat(dtpg, UDATPG_MONTH_FIELD, &length); if(length!=7 || 0!=u_memcmp(r, testFormat, length) || r[length]!=0) { log_err("udatpg_setAppendItemFormat did not return the expected string\n"); return; } /* set append name to hr */ udatpg_setAppendItemName( dtpg, UDATPG_HOUR_FIELD, appendItemName, 7 ); r = udatpg_getAppendItemName(dtpg, UDATPG_HOUR_FIELD, &length); if(length!=7 || 0!=u_memcmp(r, appendItemName, length) || r[length]!=0) { log_err("udatpg_setAppendItemName did not return the expected string\n"); return; } /* set date time format to {1}{0} */ udatpg_setDateTimeFormat( dtpg, testFormat, 7 ); r = udatpg_getDateTimeFormat(dtpg, &length); if(length!=7 || 0!=u_memcmp(r, testFormat, length) || r[length]!=0) { log_err("udatpg_setDateTimeFormat did not return the expected string\n"); return; } udatpg_close(dtpg); }
void IntlDateTimeFormat::initializeDateTimeFormat(ExecState& exec, JSValue locales, JSValue originalOptions) { VM& vm = exec.vm(); auto scope = DECLARE_THROW_SCOPE(vm); // 12.1.1 InitializeDateTimeFormat (dateTimeFormat, locales, options) (ECMA-402 2.0) // 1. If dateTimeFormat.[[initializedIntlObject]] is true, throw a TypeError exception. // 2. Set dateTimeFormat.[[initializedIntlObject]] to true. // 3. Let requestedLocales be CanonicalizeLocaleList(locales). Vector<String> requestedLocales = canonicalizeLocaleList(exec, locales); // 4. ReturnIfAbrupt(requestedLocales), RETURN_IF_EXCEPTION(scope, void()); // 5. Let options be ToDateTimeOptions(options, "any", "date"). JSObject* options = toDateTimeOptionsAnyDate(exec, originalOptions); // 6. ReturnIfAbrupt(options). RETURN_IF_EXCEPTION(scope, void()); // 7. Let opt be a new Record. HashMap<String, String> localeOpt; // 8. Let matcher be GetOption(options, "localeMatcher", "string", «"lookup", "best fit"», "best fit"). String localeMatcher = intlStringOption(exec, options, vm.propertyNames->localeMatcher, { "lookup", "best fit" }, "localeMatcher must be either \"lookup\" or \"best fit\"", "best fit"); // 9. ReturnIfAbrupt(matcher). RETURN_IF_EXCEPTION(scope, void()); // 10. Set opt.[[localeMatcher]] to matcher. localeOpt.add(vm.propertyNames->localeMatcher.string(), localeMatcher); // 11. Let localeData be the value of %DateTimeFormat%.[[localeData]]. // 12. Let r be ResolveLocale( %DateTimeFormat%.[[availableLocales]], requestedLocales, opt, %DateTimeFormat%.[[relevantExtensionKeys]], localeData). const HashSet<String> availableLocales = exec.jsCallee()->globalObject()->intlDateTimeFormatAvailableLocales(); HashMap<String, String> resolved = resolveLocale(exec, availableLocales, requestedLocales, localeOpt, relevantExtensionKeys, WTF_ARRAY_LENGTH(relevantExtensionKeys), localeData); // 13. Set dateTimeFormat.[[locale]] to the value of r.[[locale]]. m_locale = resolved.get(vm.propertyNames->locale.string()); if (m_locale.isEmpty()) { throwTypeError(&exec, scope, ASCIILiteral("failed to initialize DateTimeFormat due to invalid locale")); return; } // 14. Set dateTimeFormat.[[calendar]] to the value of r.[[ca]]. m_calendar = resolved.get(ASCIILiteral("ca")); // Switch to preferred aliases. if (m_calendar == "gregory") m_calendar = ASCIILiteral("gregorian"); else if (m_calendar == "islamicc") m_calendar = ASCIILiteral("islamic-civil"); else if (m_calendar == "ethioaa") m_calendar = ASCIILiteral("ethiopic-amete-alem"); // 15. Set dateTimeFormat.[[numberingSystem]] to the value of r.[[nu]]. m_numberingSystem = resolved.get(ASCIILiteral("nu")); // 16. Let dataLocale be the value of r.[[dataLocale]]. String dataLocale = resolved.get(ASCIILiteral("dataLocale")); // 17. Let tz be Get(options, "timeZone"). JSValue tzValue = options->get(&exec, vm.propertyNames->timeZone); // 18. ReturnIfAbrupt(tz). RETURN_IF_EXCEPTION(scope, void()); // 19. If tz is not undefined, then String tz; if (!tzValue.isUndefined()) { // a. Let tz be ToString(tz). String originalTz = tzValue.toWTFString(&exec); // b. ReturnIfAbrupt(tz). RETURN_IF_EXCEPTION(scope, void()); // c. If the result of IsValidTimeZoneName(tz) is false, then i. Throw a RangeError exception. // d. Let tz be CanonicalizeTimeZoneName(tz). tz = canonicalizeTimeZoneName(originalTz); if (tz.isNull()) { throwRangeError(&exec, scope, String::format("invalid time zone: %s", originalTz.utf8().data())); return; } } else { // 20. Else, // a. Let tz be DefaultTimeZone(). tz = defaultTimeZone(); } // 21. Set dateTimeFormat.[[timeZone]] to tz. m_timeZone = tz; // 22. Let opt be a new Record. // Rather than building a record, build the skeleton pattern. StringBuilder skeletonBuilder; // 23. For each row of Table 3, except the header row, do: // a. Let prop be the name given in the Property column of the row. // b. Let value be GetOption(options, prop, "string", «the strings given in the Values column of the row», undefined). // c. ReturnIfAbrupt(value). // d. Set opt.[[<prop>]] to value. auto narrowShortLong = { "narrow", "short", "long" }; auto twoDigitNumeric = { "2-digit", "numeric" }; auto twoDigitNumericNarrowShortLong = { "2-digit", "numeric", "narrow", "short", "long" }; auto shortLong = { "short", "long" }; String weekday = intlStringOption(exec, options, vm.propertyNames->weekday, narrowShortLong, "weekday must be \"narrow\", \"short\", or \"long\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!weekday.isNull()) { if (weekday == "narrow") skeletonBuilder.appendLiteral("EEEEE"); else if (weekday == "short") skeletonBuilder.appendLiteral("EEE"); else if (weekday == "long") skeletonBuilder.appendLiteral("EEEE"); } String era = intlStringOption(exec, options, vm.propertyNames->era, narrowShortLong, "era must be \"narrow\", \"short\", or \"long\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!era.isNull()) { if (era == "narrow") skeletonBuilder.appendLiteral("GGGGG"); else if (era == "short") skeletonBuilder.appendLiteral("GGG"); else if (era == "long") skeletonBuilder.appendLiteral("GGGG"); } String year = intlStringOption(exec, options, vm.propertyNames->year, twoDigitNumeric, "year must be \"2-digit\" or \"numeric\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!year.isNull()) { if (year == "2-digit") skeletonBuilder.appendLiteral("yy"); else if (year == "numeric") skeletonBuilder.append('y'); } String month = intlStringOption(exec, options, vm.propertyNames->month, twoDigitNumericNarrowShortLong, "month must be \"2-digit\", \"numeric\", \"narrow\", \"short\", or \"long\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!month.isNull()) { if (month == "2-digit") skeletonBuilder.appendLiteral("MM"); else if (month == "numeric") skeletonBuilder.append('M'); else if (month == "narrow") skeletonBuilder.appendLiteral("MMMMM"); else if (month == "short") skeletonBuilder.appendLiteral("MMM"); else if (month == "long") skeletonBuilder.appendLiteral("MMMM"); } String day = intlStringOption(exec, options, vm.propertyNames->day, twoDigitNumeric, "day must be \"2-digit\" or \"numeric\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!day.isNull()) { if (day == "2-digit") skeletonBuilder.appendLiteral("dd"); else if (day == "numeric") skeletonBuilder.append('d'); } String hour = intlStringOption(exec, options, vm.propertyNames->hour, twoDigitNumeric, "hour must be \"2-digit\" or \"numeric\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); // We need hour12 to make the hour skeleton pattern decision, so do this early. // 32. Let hr12 be GetOption(options, "hour12", "boolean", undefined, undefined). bool isHour12Undefined; bool hr12 = intlBooleanOption(exec, options, vm.propertyNames->hour12, isHour12Undefined); // 33. ReturnIfAbrupt(hr12). RETURN_IF_EXCEPTION(scope, void()); if (!hour.isNull()) { if (isHour12Undefined) { if (hour == "2-digit") skeletonBuilder.appendLiteral("jj"); else if (hour == "numeric") skeletonBuilder.append('j'); } else if (hr12) { if (hour == "2-digit") skeletonBuilder.appendLiteral("hh"); else if (hour == "numeric") skeletonBuilder.append('h'); } else { if (hour == "2-digit") skeletonBuilder.appendLiteral("HH"); else if (hour == "numeric") skeletonBuilder.append('H'); } } String minute = intlStringOption(exec, options, vm.propertyNames->minute, twoDigitNumeric, "minute must be \"2-digit\" or \"numeric\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!minute.isNull()) { if (minute == "2-digit") skeletonBuilder.appendLiteral("mm"); else if (minute == "numeric") skeletonBuilder.append('m'); } String second = intlStringOption(exec, options, vm.propertyNames->second, twoDigitNumeric, "second must be \"2-digit\" or \"numeric\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!second.isNull()) { if (second == "2-digit") skeletonBuilder.appendLiteral("ss"); else if (second == "numeric") skeletonBuilder.append('s'); } String timeZoneName = intlStringOption(exec, options, vm.propertyNames->timeZoneName, shortLong, "timeZoneName must be \"short\" or \"long\"", nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!timeZoneName.isNull()) { if (timeZoneName == "short") skeletonBuilder.append('z'); else if (timeZoneName == "long") skeletonBuilder.appendLiteral("zzzz"); } // 24. Let dataLocaleData be Get(localeData, dataLocale). // 25. Let formats be Get(dataLocaleData, "formats"). // 26. Let matcher be GetOption(options, "formatMatcher", "string", «"basic", "best fit"», "best fit"). intlStringOption(exec, options, vm.propertyNames->formatMatcher, { "basic", "best fit" }, "formatMatcher must be either \"basic\" or \"best fit\"", "best fit"); // 27. ReturnIfAbrupt(matcher). RETURN_IF_EXCEPTION(scope, void()); // Always use ICU date format generator, rather than our own pattern list and matcher. // Covers steps 28-36. UErrorCode status = U_ZERO_ERROR; UDateTimePatternGenerator* generator = udatpg_open(dataLocale.utf8().data(), &status); if (U_FAILURE(status)) { throwTypeError(&exec, scope, ASCIILiteral("failed to initialize DateTimeFormat")); return; } String skeleton = skeletonBuilder.toString(); StringView skeletonView(skeleton); Vector<UChar, 32> patternBuffer(32); status = U_ZERO_ERROR; auto patternLength = udatpg_getBestPattern(generator, skeletonView.upconvertedCharacters(), skeletonView.length(), patternBuffer.data(), patternBuffer.size(), &status); if (status == U_BUFFER_OVERFLOW_ERROR) { status = U_ZERO_ERROR; patternBuffer.grow(patternLength); udatpg_getBestPattern(generator, skeletonView.upconvertedCharacters(), skeletonView.length(), patternBuffer.data(), patternLength, &status); } udatpg_close(generator); if (U_FAILURE(status)) { throwTypeError(&exec, scope, ASCIILiteral("failed to initialize DateTimeFormat")); return; } StringView pattern(patternBuffer.data(), patternLength); setFormatsFromPattern(pattern); status = U_ZERO_ERROR; StringView timeZoneView(m_timeZone); m_dateFormat = std::unique_ptr<UDateFormat, UDateFormatDeleter>(udat_open(UDAT_PATTERN, UDAT_PATTERN, m_locale.utf8().data(), timeZoneView.upconvertedCharacters(), timeZoneView.length(), pattern.upconvertedCharacters(), pattern.length(), &status)); if (U_FAILURE(status)) { throwTypeError(&exec, scope, ASCIILiteral("failed to initialize DateTimeFormat")); return; } // 37. Set dateTimeFormat.[[boundFormat]] to undefined. // Already undefined. // 38. Set dateTimeFormat.[[initializedDateTimeFormat]] to true. m_initializedDateTimeFormat = true; // 39. Return dateTimeFormat. }