| /* CFDateFormatter.c |
| Copyright (c) 2002-2016, Apple Inc. and the Swift project authors |
| |
| Portions Copyright (c) 2014-2016 Apple Inc. and the Swift project authors |
| Licensed under Apache License v2.0 with Runtime Library Exception |
| See http://swift.org/LICENSE.txt for license information |
| See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| Responsibility: David Smith |
| */ |
| |
| #define U_SHOW_INTERNAL_API 1 |
| |
| #include <CoreFoundation/CFDateFormatter.h> |
| #include <CoreFoundation/CFDateFormatter_Private.h> |
| #include <CoreFoundation/CFDate.h> |
| #include <CoreFoundation/CFTimeZone.h> |
| #include <CoreFoundation/CFCalendar.h> |
| #include <CoreFoundation/CFNumber.h> |
| #include "CFPriv.h" |
| #include "CFInternal.h" |
| #include "CFLocaleInternal.h" |
| #include "CFICULogging.h" |
| #include <math.h> |
| #include <float.h> |
| |
| |
| typedef CF_ENUM(CFIndex, CFDateFormatterAmbiguousYearHandling) { |
| kCFDateFormatterAmbiguousYearFailToParse = 0, // fail the parse; the default formatter behavior |
| kCFDateFormatterAmbiguousYearAssumeToNone = 1, // default to assuming era 1, or the year 0-99 |
| kCFDateFormatterAmbiguousYearAssumeToCurrent = 2, // default to assuming the current century or era |
| kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate = 3, |
| kCFDateFormatterAmbiguousYearAssumeToFuture = 4, |
| kCFDateFormatterAmbiguousYearAssumeToPast = 5, |
| kCFDateFormatterAmbiguousYearAssumeToLikelyFuture = 6, |
| kCFDateFormatterAmbiguousYearAssumeToLikelyPast = 7 |
| }; |
| |
| extern UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz); |
| |
| PE_CONST_STRING_DECL(kCFDateFormatterFormattingContextKey, "kCFDateFormatterFormattingContextKey"); |
| |
| CF_EXPORT const CFStringRef kCFDateFormatterCalendarIdentifierKey; |
| |
| #undef CFReleaseIfNotNull |
| #define CFReleaseIfNotNull(X) if (X) CFRelease(X) |
| |
| #define BUFFER_SIZE 768 |
| |
| static CFStringRef __CFDateFormatterCreateForcedTemplate(CFLocaleRef locale, CFStringRef inString, Boolean stripAMPM); |
| |
| // If you pass in a string in tmplate, you get back NULL (failure) or a CFStringRef. |
| // If you pass in an array in tmplate, you get back NULL (global failure) or a CFArrayRef with CFStringRefs or kCFNulls (per-template failure) at each corresponding index. |
| |
| CFArrayRef CFDateFormatterCreateDateFormatsFromTemplates(CFAllocatorRef allocator, CFArrayRef tmplates, CFOptionFlags options, CFLocaleRef locale) { |
| return (CFArrayRef)CFDateFormatterCreateDateFormatFromTemplate(allocator, (CFStringRef)tmplates, options, locale); |
| } |
| |
| static Boolean useTemplatePatternGenerator(CFLocaleRef locale, void(^work)(UDateTimePatternGenerator *ptg)) { |
| static UDateTimePatternGenerator *ptg; |
| static pthread_mutex_t ptgLock = PTHREAD_MUTEX_INITIALIZER; |
| static const char *ptgLocaleName; |
| CFStringRef ln = locale ? CFLocaleGetIdentifier(locale) : CFSTR(""); |
| char buffer[BUFFER_SIZE]; |
| const char *localeName = CFStringGetCStringPtr(ln, kCFStringEncodingASCII); |
| if (NULL == localeName) { |
| if (CFStringGetCString(ln, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) localeName = buffer; |
| } |
| |
| static void (^flushCache)() = ^{ |
| __cficu_udatpg_close(ptg); |
| ptg = NULL; |
| free((void *)ptgLocaleName); |
| ptgLocaleName = NULL; |
| }; |
| pthread_mutex_lock(&ptgLock); |
| if (ptgLocaleName && strcmp(ptgLocaleName, localeName) != 0) { |
| flushCache(); |
| } |
| UErrorCode status = U_ZERO_ERROR; |
| if (!ptg) { |
| ptg = __cficu_udatpg_open(localeName, &status); |
| if (ptg && !U_FAILURE(status)) { |
| ptgLocaleName = strdup(localeName); |
| } |
| } |
| Boolean result = (NULL != ptg && !U_FAILURE(status)); |
| if (result && work) { |
| work(ptg); |
| } |
| pthread_mutex_unlock(&ptgLock); |
| return result; |
| } |
| |
| /* |
| 1) Scan the string for an AM/PM indicator |
| 2) Back up past any spaces in front of the AM/PM indicator |
| 3) As long as the current character is whitespace, or an 'a', remove it and shift everything past it down |
| */ |
| static void _CFDateFormatterStripAMPMIndicators(UniChar **bpat, int32_t *bpatlen, CFIndex bufferSize) { |
| |
| //scan |
| for (CFIndex idx = 0; idx < *bpatlen; idx++) { |
| if (((*bpat)[idx] == 'a') || ((*bpat)[idx] == 'b') || ((*bpat)[idx] == 'B') || ((*bpat)[idx] == 'C')) { |
| |
| //back up |
| while ((*bpat)[idx - 1] == ' ') { |
| idx--; |
| } |
| |
| //shift |
| for (; (*bpat)[idx] == ' ' || (*bpat)[idx] == 'a' || (*bpat)[idx] == 'b' || (*bpat)[idx] == 'B' || (*bpat)[idx] == 'C'; idx++) { |
| for (CFIndex shiftIdx = idx; shiftIdx < *bpatlen && shiftIdx + 1 < bufferSize; shiftIdx++) { |
| (*bpat)[shiftIdx] = (*bpat)[shiftIdx + 1]; |
| } |
| //compensate for the character we just removed |
| (*bpatlen)--; |
| idx--; |
| } |
| } |
| } |
| } |
| |
| CFStringRef CFDateFormatterCreateDateFormatFromTemplate(CFAllocatorRef allocator, CFStringRef tmplate, CFOptionFlags options, CFLocaleRef locale) { |
| if (allocator) __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); |
| if (locale) __CFGenericValidateType(locale, CFLocaleGetTypeID()); |
| Boolean tmplateIsString = (CFStringGetTypeID() == CFGetTypeID(tmplate)); |
| if (!tmplateIsString) { |
| __CFGenericValidateType(tmplate, CFArrayGetTypeID()); |
| } |
| |
| __block CFTypeRef result = tmplateIsString ? NULL : (CFTypeRef)CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks); |
| |
| Boolean success = useTemplatePatternGenerator(locale, ^(UDateTimePatternGenerator *ptg) { |
| |
| |
| for (CFIndex idx = 0, cnt = tmplateIsString ? 1 : CFArrayGetCount((CFArrayRef)tmplate); idx < cnt; idx++) { |
| CFStringRef tmplateString = tmplateIsString ? (CFStringRef)tmplate : (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)tmplate, idx); |
| CFStringRef resultString = NULL; |
| |
| Boolean stripAMPM = CFStringFind(tmplateString, CFSTR("J"), 0).location != kCFNotFound; |
| tmplateString = __CFDateFormatterCreateForcedTemplate(locale ? locale : CFLocaleGetSystem(), tmplateString, stripAMPM); |
| |
| CFIndex jCount = 0; // the only interesting cases are 0, 1, and 2 (adjacent) |
| UniChar adjacentJs[2] = {-1, -1}; |
| CFRange r = CFStringFind(tmplateString, CFSTR("j"), kCFCompareCaseInsensitive); |
| if (kCFNotFound != r.location) { |
| adjacentJs[0] = CFStringGetCharacterAtIndex(tmplateString, r.location); |
| jCount++; |
| if ((r.location + 1 < CFStringGetLength(tmplateString)) && ('j' == CFStringGetCharacterAtIndex(tmplateString, r.location + 1) || 'J' == CFStringGetCharacterAtIndex(tmplateString, r.location + 1))) { |
| jCount++; |
| adjacentJs[1] = CFStringGetCharacterAtIndex(tmplateString, r.location + 1); |
| } |
| } |
| |
| UChar pattern[BUFFER_SIZE] = {0}, skel[BUFFER_SIZE] = {0}, bpat[BUFFER_SIZE] = {0}; |
| CFIndex tmpltLen = CFStringGetLength(tmplateString); |
| if (BUFFER_SIZE < tmpltLen) tmpltLen = BUFFER_SIZE; |
| CFStringGetCharacters(tmplateString, CFRangeMake(0, tmpltLen), (UniChar *)pattern); |
| CFRelease(tmplateString); |
| |
| int32_t patlen = tmpltLen; |
| UErrorCode status = U_ZERO_ERROR; |
| int32_t skellen = __cficu_udatpg_getSkeleton(ptg, pattern, patlen, skel, sizeof(skel) / sizeof(skel[0]), &status); |
| if (!U_FAILURE(status)) { |
| if ((0 < jCount) && (skellen + jCount < (sizeof(skel) / sizeof(skel[0])))) { |
| |
| skel[skellen++] = 'j'; //adjacentJs[0]; |
| if (1 < jCount) skel[skellen++] = 'j'; //adjacentJs[1]; |
| //stripAMPM = false; //'J' will take care of it. We only need to do it manually if we stripped the Js out ourselves while forcing 12/24 hour time |
| } |
| |
| status = U_ZERO_ERROR; |
| int32_t bpatlen = __cficu_udatpg_getBestPattern(ptg, skel, skellen, bpat, sizeof(bpat) / sizeof(bpat[0]), &status); |
| if (!U_FAILURE(status)) { |
| if (stripAMPM) { |
| UniChar *bpatptr = (UniChar *)bpat; |
| _CFDateFormatterStripAMPMIndicators(&bpatptr, &bpatlen, BUFFER_SIZE); |
| } |
| resultString = CFStringCreateWithCharacters(allocator, (const UniChar *)bpat, bpatlen); |
| } |
| } |
| |
| if (tmplateIsString) { |
| result = (CFTypeRef)resultString; |
| } else { |
| CFArrayAppendValue((CFMutableArrayRef)result, resultString ? (CFTypeRef)resultString : (CFTypeRef)kCFNull); |
| if (resultString) CFRelease(resultString); |
| } |
| } |
| }); |
| |
| if (!success) { |
| if (result) CFRelease(result); |
| result = NULL; |
| } |
| |
| return (CFStringRef)result; |
| } |
| |
| struct __CFDateFormatter { |
| CFRuntimeBase _base; |
| UDateFormat *_df; |
| CFLocaleRef _locale; |
| CFDateFormatterStyle _timeStyle; |
| CFDateFormatterStyle _dateStyle; |
| CFStringRef _format; |
| CFStringRef _defformat; |
| struct { |
| CFBooleanRef _IsLenient; |
| CFBooleanRef _DoesRelativeDateFormatting; |
| CFBooleanRef _HasCustomFormat; |
| CFTimeZoneRef _TimeZone; |
| CFCalendarRef _Calendar; |
| CFStringRef _CalendarName; |
| CFDateRef _TwoDigitStartDate; |
| CFDateRef _DefaultDate; |
| CFDateRef _GregorianStartDate; |
| CFArrayRef _EraSymbols; |
| CFArrayRef _LongEraSymbols; |
| CFArrayRef _MonthSymbols; |
| CFArrayRef _ShortMonthSymbols; |
| CFArrayRef _VeryShortMonthSymbols; |
| CFArrayRef _StandaloneMonthSymbols; |
| CFArrayRef _ShortStandaloneMonthSymbols; |
| CFArrayRef _VeryShortStandaloneMonthSymbols; |
| CFArrayRef _WeekdaySymbols; |
| CFArrayRef _ShortWeekdaySymbols; |
| CFArrayRef _VeryShortWeekdaySymbols; |
| CFArrayRef _StandaloneWeekdaySymbols; |
| CFArrayRef _ShortStandaloneWeekdaySymbols; |
| CFArrayRef _VeryShortStandaloneWeekdaySymbols; |
| CFArrayRef _QuarterSymbols; |
| CFArrayRef _ShortQuarterSymbols; |
| CFArrayRef _StandaloneQuarterSymbols; |
| CFArrayRef _ShortStandaloneQuarterSymbols; |
| CFStringRef _AMSymbol; |
| CFStringRef _PMSymbol; |
| CFNumberRef _AmbiguousYearStrategy; |
| CFBooleanRef _UsesCharacterDirection; |
| CFNumberRef _FormattingContext; |
| |
| // the following are from preferences |
| CFArrayRef _CustomEraSymbols; |
| CFArrayRef _CustomLongEraSymbols; |
| CFArrayRef _CustomMonthSymbols; |
| CFArrayRef _CustomShortMonthSymbols; |
| CFArrayRef _CustomVeryShortMonthSymbols; |
| CFArrayRef _CustomStandaloneMonthSymbols; |
| CFArrayRef _CustomShortStandaloneMonthSymbols; |
| CFArrayRef _CustomVeryShortStandaloneMonthSymbols; |
| CFArrayRef _CustomWeekdaySymbols; |
| CFArrayRef _CustomShortWeekdaySymbols; |
| CFArrayRef _CustomVeryShortWeekdaySymbols; |
| CFArrayRef _CustomStandaloneWeekdaySymbols; |
| CFArrayRef _CustomShortStandaloneWeekdaySymbols; |
| CFArrayRef _CustomVeryShortStandaloneWeekdaySymbols; |
| CFArrayRef _CustomQuarterSymbols; |
| CFArrayRef _CustomShortQuarterSymbols; |
| CFArrayRef _CustomStandaloneQuarterSymbols; |
| CFArrayRef _CustomShortStandaloneQuarterSymbols; |
| CFStringRef _CustomDateFormat; |
| CFStringRef _CustomTimeFormat; |
| CFBooleanRef _Custom24Hour; |
| CFBooleanRef _Custom12Hour; |
| CFStringRef _CustomAMSymbol; |
| CFStringRef _CustomPMSymbol; |
| CFDictionaryRef _CustomFirstWeekday; |
| CFDictionaryRef _CustomMinDaysInFirstWeek; |
| |
| } _property; |
| }; |
| |
| static CFStringRef __CFDateFormatterCopyDescription(CFTypeRef cf) { |
| CFDateFormatterRef formatter = (CFDateFormatterRef)cf; |
| return CFStringCreateWithFormat(CFGetAllocator(formatter), NULL, CFSTR("<CFDateFormatter %p [%p]>"), cf, CFGetAllocator(formatter)); |
| } |
| |
| static void __CFDateFormatterDeallocate(CFTypeRef cf) { |
| CFDateFormatterRef formatter = (CFDateFormatterRef)cf; |
| if (formatter->_df) __cficu_udat_close(formatter->_df); |
| if (formatter->_locale) CFRelease(formatter->_locale); |
| if (formatter->_format) CFRelease(formatter->_format); |
| if (formatter->_defformat) CFRelease(formatter->_defformat); |
| CFReleaseIfNotNull(formatter->_property._IsLenient); |
| CFReleaseIfNotNull(formatter->_property._DoesRelativeDateFormatting); |
| CFReleaseIfNotNull(formatter->_property._TimeZone); |
| CFReleaseIfNotNull(formatter->_property._Calendar); |
| CFReleaseIfNotNull(formatter->_property._CalendarName); |
| CFReleaseIfNotNull(formatter->_property._TwoDigitStartDate); |
| CFReleaseIfNotNull(formatter->_property._DefaultDate); |
| CFReleaseIfNotNull(formatter->_property._GregorianStartDate); |
| CFReleaseIfNotNull(formatter->_property._EraSymbols); |
| CFReleaseIfNotNull(formatter->_property._LongEraSymbols); |
| CFReleaseIfNotNull(formatter->_property._MonthSymbols); |
| CFReleaseIfNotNull(formatter->_property._ShortMonthSymbols); |
| CFReleaseIfNotNull(formatter->_property._VeryShortMonthSymbols); |
| CFReleaseIfNotNull(formatter->_property._StandaloneMonthSymbols); |
| CFReleaseIfNotNull(formatter->_property._ShortStandaloneMonthSymbols); |
| CFReleaseIfNotNull(formatter->_property._VeryShortStandaloneMonthSymbols); |
| CFReleaseIfNotNull(formatter->_property._WeekdaySymbols); |
| CFReleaseIfNotNull(formatter->_property._ShortWeekdaySymbols); |
| CFReleaseIfNotNull(formatter->_property._VeryShortWeekdaySymbols); |
| CFReleaseIfNotNull(formatter->_property._StandaloneWeekdaySymbols); |
| CFReleaseIfNotNull(formatter->_property._ShortStandaloneWeekdaySymbols); |
| CFReleaseIfNotNull(formatter->_property._VeryShortStandaloneWeekdaySymbols); |
| CFReleaseIfNotNull(formatter->_property._QuarterSymbols); |
| CFReleaseIfNotNull(formatter->_property._ShortQuarterSymbols); |
| CFReleaseIfNotNull(formatter->_property._StandaloneQuarterSymbols); |
| CFReleaseIfNotNull(formatter->_property._ShortStandaloneQuarterSymbols); |
| CFReleaseIfNotNull(formatter->_property._AMSymbol); |
| CFReleaseIfNotNull(formatter->_property._PMSymbol); |
| CFReleaseIfNotNull(formatter->_property._AmbiguousYearStrategy); |
| CFReleaseIfNotNull(formatter->_property._UsesCharacterDirection); |
| CFReleaseIfNotNull(formatter->_property._FormattingContext); |
| CFReleaseIfNotNull(formatter->_property._CustomEraSymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomMonthSymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomShortMonthSymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomWeekdaySymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomShortWeekdaySymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomLongEraSymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomVeryShortMonthSymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomVeryShortWeekdaySymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomStandaloneMonthSymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomShortStandaloneMonthSymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomVeryShortStandaloneMonthSymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomStandaloneWeekdaySymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomShortStandaloneWeekdaySymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomVeryShortStandaloneWeekdaySymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomQuarterSymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomShortQuarterSymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomShortStandaloneQuarterSymbols); |
| CFReleaseIfNotNull(formatter->_property._CustomDateFormat); |
| CFReleaseIfNotNull(formatter->_property._CustomTimeFormat); |
| CFReleaseIfNotNull(formatter->_property._Custom24Hour); |
| CFReleaseIfNotNull(formatter->_property._Custom12Hour); |
| CFReleaseIfNotNull(formatter->_property._CustomAMSymbol); |
| CFReleaseIfNotNull(formatter->_property._CustomPMSymbol); |
| CFReleaseIfNotNull(formatter->_property._CustomFirstWeekday); |
| CFReleaseIfNotNull(formatter->_property._CustomMinDaysInFirstWeek); |
| } |
| |
| static CFStringRef __CFDateFormatterCreateForcedString(CFDateFormatterRef formatter, CFStringRef inString); |
| |
| static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value, Boolean directToICU); |
| static void __CFDateFormatterStoreSymbolPrefs(const void *key, const void *value, void *context); |
| extern CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale); |
| static void __substituteFormatStringFromPrefsDFRelative(CFDateFormatterRef formatter); |
| static void __substituteFormatStringFromPrefsDF(CFDateFormatterRef formatter, bool doTime); |
| static void __CFDateFormatterSetSymbolsArray(UDateFormat *icudf, int32_t icucode, int index_base, CFTypeRef value); |
| |
| static void __ReadCustomUDateFormatProperty(CFDateFormatterRef formatter) { |
| CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale); |
| CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUDateTimeSymbols")) : NULL; |
| if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { |
| CFDictionaryApplyFunction((CFDictionaryRef)metapref, __CFDateFormatterStoreSymbolPrefs, formatter); |
| } |
| metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleFirstWeekday")) : NULL; |
| if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { |
| formatter->_property._CustomFirstWeekday = (CFDictionaryRef)CFRetain(metapref); |
| } |
| metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleMinDaysInFirstWeek")) : NULL; |
| if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { |
| formatter->_property._CustomMinDaysInFirstWeek = (CFDictionaryRef)CFRetain(metapref); |
| } |
| metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce24HourTime")) : NULL; |
| if (NULL != metapref && CFGetTypeID(metapref) == CFBooleanGetTypeID()) { |
| formatter->_property._Custom24Hour = (CFBooleanRef)CFRetain(metapref); |
| } |
| metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce12HourTime")) : NULL; |
| if (NULL != metapref && CFGetTypeID(metapref) == CFBooleanGetTypeID()) { |
| formatter->_property._Custom12Hour = (CFBooleanRef)CFRetain(metapref); |
| } |
| metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUDateFormatStrings")) : NULL; |
| if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { |
| CFStringRef key; |
| switch (formatter->_dateStyle) { |
| case kCFDateFormatterShortStyle: key = CFSTR("1"); break; |
| case kCFDateFormatterMediumStyle: key = CFSTR("2"); break; |
| case kCFDateFormatterLongStyle: key = CFSTR("3"); break; |
| case kCFDateFormatterFullStyle: key = CFSTR("4"); break; |
| default: key = CFSTR("0"); break; |
| } |
| CFStringRef dateFormat = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key); |
| if (NULL != dateFormat && CFGetTypeID(dateFormat) == CFStringGetTypeID()) { |
| formatter->_property._CustomDateFormat = (CFStringRef)CFRetain(dateFormat); |
| } |
| } |
| metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUTimeFormatStrings")) : NULL; |
| if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) { |
| CFStringRef key; |
| switch (formatter->_timeStyle) { |
| case kCFDateFormatterShortStyle: key = CFSTR("1"); break; |
| case kCFDateFormatterMediumStyle: key = CFSTR("2"); break; |
| case kCFDateFormatterLongStyle: key = CFSTR("3"); break; |
| case kCFDateFormatterFullStyle: key = CFSTR("4"); break; |
| default: key = CFSTR("0"); break; |
| } |
| CFStringRef timeFormat = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key); |
| if (NULL != timeFormat && CFGetTypeID(timeFormat) == CFStringGetTypeID()) { |
| formatter->_property._CustomTimeFormat = (CFStringRef)CFRetain(timeFormat); |
| } |
| } |
| } |
| |
| static void __ApplyUDateFormatSymbol(CFDateFormatterRef formatter) { |
| UDateFormatSymbolType types[18] = {UDAT_ERAS, |
| UDAT_ERA_NAMES, |
| UDAT_MONTHS, |
| UDAT_SHORT_MONTHS, |
| UDAT_NARROW_MONTHS, |
| UDAT_STANDALONE_MONTHS, |
| UDAT_STANDALONE_SHORT_MONTHS, |
| UDAT_STANDALONE_NARROW_MONTHS, |
| UDAT_WEEKDAYS, |
| UDAT_SHORT_WEEKDAYS, |
| UDAT_NARROW_WEEKDAYS, |
| UDAT_STANDALONE_WEEKDAYS, |
| UDAT_STANDALONE_SHORT_WEEKDAYS, |
| UDAT_STANDALONE_NARROW_WEEKDAYS, |
| UDAT_QUARTERS, |
| UDAT_SHORT_QUARTERS, |
| UDAT_STANDALONE_QUARTERS, |
| UDAT_STANDALONE_SHORT_QUARTERS}; |
| int offsets[18] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}; |
| CFArrayRef symbols[18] = {formatter->_property._EraSymbols, |
| formatter->_property._LongEraSymbols, |
| formatter->_property._MonthSymbols, |
| formatter->_property._ShortMonthSymbols, |
| formatter->_property._VeryShortMonthSymbols, |
| formatter->_property._StandaloneMonthSymbols, |
| formatter->_property._ShortStandaloneMonthSymbols, |
| formatter->_property._VeryShortStandaloneMonthSymbols, |
| formatter->_property._WeekdaySymbols, |
| formatter->_property._ShortWeekdaySymbols, |
| formatter->_property._VeryShortWeekdaySymbols, |
| formatter->_property._StandaloneWeekdaySymbols, |
| formatter->_property._ShortStandaloneWeekdaySymbols, |
| formatter->_property._VeryShortStandaloneWeekdaySymbols, |
| formatter->_property._QuarterSymbols, |
| formatter->_property._ShortQuarterSymbols, |
| formatter->_property._StandaloneQuarterSymbols, |
| formatter->_property._ShortStandaloneQuarterSymbols |
| }; |
| CFArrayRef customSymbols[18] = {formatter->_property._CustomEraSymbols, |
| formatter->_property._CustomLongEraSymbols, |
| formatter->_property._CustomMonthSymbols, |
| formatter->_property._CustomShortMonthSymbols, |
| formatter->_property._CustomVeryShortMonthSymbols, |
| formatter->_property._CustomStandaloneMonthSymbols, |
| formatter->_property._CustomShortStandaloneMonthSymbols, |
| formatter->_property._CustomVeryShortStandaloneMonthSymbols, |
| formatter->_property._CustomWeekdaySymbols, |
| formatter->_property._CustomShortWeekdaySymbols, |
| formatter->_property._CustomVeryShortWeekdaySymbols, |
| formatter->_property._CustomStandaloneWeekdaySymbols, |
| formatter->_property._CustomShortStandaloneWeekdaySymbols, |
| formatter->_property._CustomVeryShortStandaloneWeekdaySymbols, |
| formatter->_property._CustomQuarterSymbols, |
| formatter->_property._CustomShortQuarterSymbols, |
| formatter->_property._CustomStandaloneQuarterSymbols, |
| formatter->_property._CustomShortStandaloneQuarterSymbols |
| }; |
| |
| for (CFIndex i = 0; i < 18; i++) { |
| if (symbols[i] != NULL) { |
| __CFDateFormatterSetSymbolsArray(formatter->_df, types[i], offsets[i], symbols[i]); |
| } else if (customSymbols[i] != NULL) { |
| __CFDateFormatterSetSymbolsArray(formatter->_df, types[i], offsets[i], customSymbols[i]); |
| } |
| } |
| |
| CFStringRef ampm[2]; |
| ampm[0] = NULL; |
| ampm[1] = NULL; |
| |
| if (formatter->_property._AMSymbol != NULL) { |
| ampm[0] = formatter->_property._AMSymbol; |
| } else if (formatter->_property._CustomAMSymbol != NULL) { |
| ampm[0] = formatter->_property._CustomAMSymbol; |
| } |
| if (formatter->_property._PMSymbol != NULL) { |
| ampm[1] = formatter->_property._PMSymbol; |
| } else if (formatter->_property._CustomPMSymbol != NULL) { |
| ampm[1] = formatter->_property._CustomPMSymbol; |
| } |
| for (CFIndex i = 0; i < 2; i++) { |
| CFStringRef sym = ampm[i]; |
| if (sym != NULL) { |
| CFIndex item_cnt = CFStringGetLength(sym); |
| STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); |
| UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(sym); |
| if (NULL == item_ustr) { |
| item_cnt = __CFMin(BUFFER_SIZE, item_cnt); |
| CFStringGetCharacters(sym, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); |
| item_ustr = item_buffer; |
| } |
| UErrorCode status = U_ZERO_ERROR; |
| __cficu_udat_setSymbols(formatter->_df, UDAT_AM_PMS, i, item_ustr, item_cnt, &status); |
| } |
| } |
| } |
| |
| static void __SetCalendarProperties(CFDateFormatterRef df) { |
| CFStringRef calName = df->_property._CalendarName ? (df->_property._CalendarName) : NULL; |
| if (!calName) { |
| calName = (CFStringRef)CFLocaleGetValue(df->_locale, kCFLocaleCalendarIdentifierKey); |
| } |
| UErrorCode status = U_ZERO_ERROR; |
| const UCalendar *cal = __cficu_udat_getCalendar(df->_df); |
| UCalendar *new_cal = NULL; |
| |
| if (df->_property._Calendar != NULL || df->_property._CalendarName != NULL) { |
| UCalendar *caltmp = __CFCalendarCreateUCalendar(NULL, CFLocaleGetIdentifier(df->_locale), df->_property._TimeZone); |
| if (caltmp) { |
| new_cal = caltmp; |
| } |
| } |
| if (new_cal == NULL) { |
| new_cal = __cficu_ucal_clone(cal, &status); |
| } |
| |
| if (df->_property._IsLenient != NULL) { |
| status = U_ZERO_ERROR; |
| CFBooleanRef value = df->_property._IsLenient; |
| __cficu_ucal_setAttribute(new_cal, UCAL_LENIENT, (kCFBooleanTrue == value)); |
| } |
| if (df->_property._TimeZone != NULL) { |
| status = U_ZERO_ERROR; |
| UChar ubuffer[BUFFER_SIZE]; |
| CFStringRef tznam = CFTimeZoneGetName(df->_property._TimeZone); |
| CFIndex ucnt = CFStringGetLength(tznam); |
| if (BUFFER_SIZE < ucnt) ucnt = BUFFER_SIZE; |
| CFStringGetCharacters(tznam, CFRangeMake(0, ucnt), (UniChar *)ubuffer); |
| __cficu_ucal_setTimeZone(new_cal, ubuffer, ucnt, &status); |
| } |
| if (df->_property._GregorianStartDate != NULL) { |
| status = U_ZERO_ERROR; |
| CFAbsoluteTime at = CFDateGetAbsoluteTime((CFDateRef)df->_property._GregorianStartDate); |
| UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0; |
| __cficu_ucal_setGregorianChange(new_cal, udate, &status); |
| } else if (calName && CFEqual(calName, kCFCalendarIdentifierGregorian)) { |
| status = U_ZERO_ERROR; |
| UDate udate = __cficu_ucal_getGregorianChange(cal, &status); |
| CFAbsoluteTime at = U_SUCCESS(status) ? (udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970) : -13197600000.0; // Oct 15, 1582 |
| udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0; |
| status = U_ZERO_ERROR; |
| __cficu_ucal_setGregorianChange(new_cal, udate, &status); |
| } |
| if (df->_property._Calendar != NULL) { |
| __cficu_ucal_setAttribute(new_cal, UCAL_FIRST_DAY_OF_WEEK, CFCalendarGetFirstWeekday((CFCalendarRef)df->_property._Calendar)); |
| } else if (df->_property._CustomFirstWeekday != NULL) { |
| CFNumberRef firstWeekday = (CFNumberRef)CFDictionaryGetValue(df->_property._CustomFirstWeekday, calName); |
| if (NULL != firstWeekday && CFGetTypeID(firstWeekday) == CFNumberGetTypeID()) { |
| CFIndex wkdy; |
| if (CFNumberGetValue((CFNumberRef)firstWeekday, kCFNumberCFIndexType, &wkdy)) { |
| status = U_ZERO_ERROR; |
| __cficu_ucal_setAttribute(new_cal, UCAL_FIRST_DAY_OF_WEEK, wkdy); |
| } |
| } |
| } |
| |
| if (df->_property._Calendar != NULL) { |
| __cficu_ucal_setAttribute(new_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, CFCalendarGetMinimumDaysInFirstWeek((CFCalendarRef)df->_property._Calendar)); |
| } else if (df->_property._CustomMinDaysInFirstWeek != NULL) { |
| CFNumberRef minDays = (CFNumberRef)CFDictionaryGetValue(df->_property._CustomMinDaysInFirstWeek, calName); |
| if (NULL != minDays && CFGetTypeID(minDays) == CFNumberGetTypeID()) { |
| CFIndex mwd; |
| if (CFNumberGetValue((CFNumberRef)minDays, kCFNumberCFIndexType, &mwd)) { |
| status = U_ZERO_ERROR; |
| __cficu_ucal_setAttribute(new_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, mwd); |
| } |
| } |
| } |
| __cficu_udat_setCalendar(df->_df, new_cal); |
| __cficu_ucal_close(new_cal); |
| } |
| |
| #define RESET_PROPERTY(C, K) \ |
| if (df->_property. C) __CFDateFormatterSetProperty(df, K, df->_property. C, true); |
| |
| static void __ResetUDateFormat(CFDateFormatterRef df, Boolean goingToHaveCustomFormat) { |
| if (df->_df) __cficu_udat_close(df->_df); |
| df->_df = NULL; |
| |
| // uses _timeStyle, _dateStyle, _locale, _property._TimeZone; sets _df, _format, _defformat |
| char loc_buffer[BUFFER_SIZE]; |
| loc_buffer[0] = 0; |
| CFStringRef tmpLocName = df->_locale ? CFLocaleGetIdentifier(df->_locale) : CFSTR(""); |
| CFStringGetCString(tmpLocName, loc_buffer, BUFFER_SIZE, kCFStringEncodingASCII); |
| |
| UChar tz_buffer[BUFFER_SIZE]; |
| tz_buffer[0] = 0; |
| CFStringRef tmpTZName = df->_property._TimeZone ? CFTimeZoneGetName(df->_property._TimeZone) : CFSTR("GMT"); |
| CFStringGetCharacters(tmpTZName, CFRangeMake(0, CFStringGetLength(tmpTZName)), (UniChar *)tz_buffer); |
| |
| int32_t udstyle = 0, utstyle = 0; // effectively this makes UDAT_FULL the default for unknown dateStyle/timeStyle values |
| switch (df->_dateStyle) { |
| case kCFDateFormatterNoStyle: udstyle = UDAT_NONE; break; |
| case kCFDateFormatterShortStyle: udstyle = UDAT_SHORT; break; |
| case kCFDateFormatterMediumStyle: udstyle = UDAT_MEDIUM; break; |
| case kCFDateFormatterLongStyle: udstyle = UDAT_LONG; break; |
| case kCFDateFormatterFullStyle: udstyle = UDAT_FULL; break; |
| } |
| switch (df->_timeStyle) { |
| case kCFDateFormatterNoStyle: utstyle = UDAT_NONE; break; |
| case kCFDateFormatterShortStyle: utstyle = UDAT_SHORT; break; |
| case kCFDateFormatterMediumStyle: utstyle = UDAT_MEDIUM; break; |
| case kCFDateFormatterLongStyle: utstyle = UDAT_LONG; break; |
| case kCFDateFormatterFullStyle: utstyle = UDAT_FULL; break; |
| } |
| Boolean wantRelative = (NULL != df->_property._DoesRelativeDateFormatting && df->_property._DoesRelativeDateFormatting == kCFBooleanTrue); |
| Boolean hasFormat = (NULL != df->_property._HasCustomFormat && df->_property._HasCustomFormat == kCFBooleanTrue) || goingToHaveCustomFormat; |
| if (wantRelative && !hasFormat && kCFDateFormatterNoStyle != df->_dateStyle) { |
| udstyle |= UDAT_RELATIVE; |
| } |
| |
| UErrorCode status = U_ZERO_ERROR; |
| UDateFormat *icudf = __cficu_udat_open((UDateFormatStyle)utstyle, (UDateFormatStyle)udstyle, loc_buffer, tz_buffer, CFStringGetLength(tmpTZName), NULL, 0, &status); |
| |
| if (NULL == icudf || U_FAILURE(status)) { |
| return; |
| } |
| |
| // <rdar://problem/15420462> "Yesterday" and "Today" now appear in lower case |
| // ICU uses middle of sentence context for relative days by default. We need to have relative dates to be captalized by default for backward compatibility |
| if (wantRelative) { |
| __cficu_udat_setContext(icudf, UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU, &status); |
| } |
| |
| if (df->_property._IsLenient != NULL) { |
| __cficu_udat_setLenient(icudf, (kCFBooleanTrue == df->_property._IsLenient)); |
| } else { |
| __cficu_udat_setLenient(icudf, 0); |
| } |
| if (kCFDateFormatterNoStyle == df->_dateStyle && kCFDateFormatterNoStyle == df->_timeStyle) { |
| if (wantRelative && !hasFormat && kCFDateFormatterNoStyle != df->_dateStyle) { |
| UErrorCode s = U_ZERO_ERROR; |
| __cficu_udat_applyPatternRelative(icudf, NULL, 0, NULL, 0, &s); |
| } else { |
| __cficu_udat_applyPattern(icudf, false, NULL, 0); |
| } |
| } |
| if (!wantRelative && df->_property._HasCustomFormat == kCFBooleanTrue) { |
| CFIndex cnt = CFStringGetLength(df->_format); |
| STACK_BUFFER_DECL(UChar, ubuffer, cnt); |
| const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)df->_format); |
| if (NULL == ustr) { |
| CFStringGetCharacters(df->_format, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| ustr = ubuffer; |
| } |
| __cficu_udat_applyPattern(icudf, false, ustr, cnt); |
| } |
| |
| CFStringRef calident = (CFStringRef)CFLocaleGetValue(df->_locale, kCFLocaleCalendarIdentifierKey); |
| if (calident && CFEqual(calident, kCFCalendarIdentifierGregorian)) { |
| status = U_ZERO_ERROR; |
| __cficu_udat_set2DigitYearStart(icudf, -631152000000.0, &status); // 1950-01-01 00:00:00 GMT |
| } |
| df->_df = icudf; |
| |
| __ReadCustomUDateFormatProperty(df); |
| |
| __SetCalendarProperties(df); |
| |
| if (wantRelative && !hasFormat && kCFDateFormatterNoStyle != df->_dateStyle) { |
| __substituteFormatStringFromPrefsDFRelative(df); |
| } else { |
| __substituteFormatStringFromPrefsDF(df, false); |
| __substituteFormatStringFromPrefsDF(df, true); |
| } |
| |
| __ApplyUDateFormatSymbol(df); |
| |
| |
| if (wantRelative && !hasFormat && kCFDateFormatterNoStyle != df->_dateStyle) { |
| UChar dateBuffer[BUFFER_SIZE]; |
| UChar timeBuffer[BUFFER_SIZE]; |
| status = U_ZERO_ERROR; |
| CFIndex dateLen = __cficu_udat_toPatternRelativeDate(icudf, dateBuffer, BUFFER_SIZE, &status); |
| CFIndex timeLen = (utstyle != UDAT_NONE) ? __cficu_udat_toPatternRelativeTime(icudf, timeBuffer, BUFFER_SIZE, &status) : 0; |
| if (U_SUCCESS(status) && dateLen <= BUFFER_SIZE && timeLen <= BUFFER_SIZE) { |
| // We assume that the 12/24-hour forcing preferences only affect the Time component |
| CFStringRef newFormat = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)timeBuffer, timeLen); |
| CFStringRef formatString = __CFDateFormatterCreateForcedString(df, newFormat); |
| CFIndex cnt = CFStringGetLength(formatString); |
| CFAssert1(cnt <= BUFFER_SIZE, __kCFLogAssertion, "%s(): time format string too long", __PRETTY_FUNCTION__); |
| if (cnt <= BUFFER_SIZE) { |
| CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)timeBuffer); |
| timeLen = cnt; |
| status = U_ZERO_ERROR; |
| __cficu_udat_applyPatternRelative(icudf, dateBuffer, dateLen, timeBuffer, timeLen, &status); |
| // ignore error and proceed anyway, what else can be done? |
| |
| UChar ubuffer[BUFFER_SIZE]; |
| status = U_ZERO_ERROR; |
| int32_t ret = __cficu_udat_toPattern(icudf, false, ubuffer, BUFFER_SIZE, &status); // read out current pattern |
| if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { |
| if (df->_format) CFRelease(df->_format); |
| df->_format = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, ret); |
| } |
| } |
| CFRelease(formatString); |
| CFRelease(newFormat); |
| } |
| } else { |
| UChar ubuffer[BUFFER_SIZE]; |
| status = U_ZERO_ERROR; |
| int32_t ret = __cficu_udat_toPattern(icudf, false, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && ret <= BUFFER_SIZE) { |
| CFStringRef newFormat = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, ret); |
| CFStringRef formatString = __CFDateFormatterCreateForcedString(df, newFormat); |
| CFIndex cnt = CFStringGetLength(formatString); |
| CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__); |
| if (cnt <= 1024) { |
| STACK_BUFFER_DECL(UChar, ubuffer, cnt); |
| const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)formatString); |
| if (NULL == ustr) { |
| CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| ustr = ubuffer; |
| } |
| UErrorCode status = U_ZERO_ERROR; |
| // __cficu_udat_applyPattern(df->_df, false, ustr, cnt, &status); |
| __cficu_udat_applyPattern(df->_df, false, ustr, cnt); |
| if (U_SUCCESS(status)) { |
| if (df->_format) CFRelease(df->_format); |
| df->_format = (CFStringRef)CFStringCreateCopy(CFGetAllocator(df), formatString); |
| } |
| } |
| CFRelease(formatString); |
| CFRelease(newFormat); |
| } |
| } |
| if (df->_defformat) CFRelease(df->_defformat); |
| df->_defformat = df->_format ? (CFStringRef)CFRetain(df->_format) : NULL; |
| |
| RESET_PROPERTY(_IsLenient, kCFDateFormatterIsLenientKey); |
| RESET_PROPERTY(_DoesRelativeDateFormatting, kCFDateFormatterDoesRelativeDateFormattingKey); |
| RESET_PROPERTY(_Calendar, kCFDateFormatterCalendarKey); |
| RESET_PROPERTY(_CalendarName, kCFDateFormatterCalendarIdentifierKey); |
| RESET_PROPERTY(_TimeZone, kCFDateFormatterTimeZoneKey); |
| RESET_PROPERTY(_TwoDigitStartDate, kCFDateFormatterTwoDigitStartDateKey); |
| RESET_PROPERTY(_DefaultDate, kCFDateFormatterDefaultDateKey); |
| RESET_PROPERTY(_GregorianStartDate, kCFDateFormatterGregorianStartDateKey); |
| RESET_PROPERTY(_AmbiguousYearStrategy, kCFDateFormatterAmbiguousYearStrategyKey); |
| RESET_PROPERTY(_UsesCharacterDirection, kCFDateFormatterUsesCharacterDirectionKey); |
| RESET_PROPERTY(_FormattingContext, kCFDateFormatterFormattingContextKey); |
| } |
| |
| static CFTypeID __kCFDateFormatterTypeID = _kCFRuntimeNotATypeID; |
| |
| static const CFRuntimeClass __CFDateFormatterClass = { |
| 0, |
| "CFDateFormatter", |
| NULL, // init |
| NULL, // copy |
| __CFDateFormatterDeallocate, |
| NULL, |
| NULL, |
| NULL, // |
| __CFDateFormatterCopyDescription |
| }; |
| |
| CFTypeID CFDateFormatterGetTypeID(void) { |
| static dispatch_once_t initOnce; |
| dispatch_once(&initOnce, ^{ __kCFDateFormatterTypeID = _CFRuntimeRegisterClass(&__CFDateFormatterClass); }); |
| return __kCFDateFormatterTypeID; |
| } |
| |
| static CFDateFormatterRef __SetUpCFDateFormatter(CFAllocatorRef allocator, CFLocaleRef locale, CFDateFormatterStyle dateStyle, CFDateFormatterStyle timeStyle, CFBooleanRef calculateISO8601) { |
| struct __CFDateFormatter *memory; |
| uint32_t size = sizeof(struct __CFDateFormatter) - sizeof(CFRuntimeBase); |
| if (allocator == NULL) allocator = __CFGetDefaultAllocator(); |
| __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); |
| if (locale) __CFGenericValidateType(locale, CFLocaleGetTypeID()); |
| memory = (struct __CFDateFormatter *)_CFRuntimeCreateInstance(allocator, CFDateFormatterGetTypeID(), size, NULL); |
| if (NULL == memory) { |
| return NULL; |
| } |
| memory->_df = NULL; |
| memory->_locale = NULL; |
| memory->_format = NULL; |
| memory->_defformat = NULL; |
| memory->_dateStyle = dateStyle; |
| memory->_timeStyle = timeStyle; |
| memory->_property._IsLenient = NULL; |
| memory->_property._DoesRelativeDateFormatting = NULL; |
| memory->_property._HasCustomFormat = NULL; |
| memory->_property._TimeZone = NULL; |
| memory->_property._Calendar = NULL; |
| memory->_property._CalendarName = NULL; |
| memory->_property._TwoDigitStartDate = NULL; |
| memory->_property._DefaultDate = NULL; |
| memory->_property._GregorianStartDate = NULL; |
| memory->_property._EraSymbols = NULL; |
| memory->_property._LongEraSymbols = NULL; |
| memory->_property._MonthSymbols = NULL; |
| memory->_property._ShortMonthSymbols = NULL; |
| memory->_property._VeryShortMonthSymbols = NULL; |
| memory->_property._StandaloneMonthSymbols = NULL; |
| memory->_property._ShortStandaloneMonthSymbols = NULL; |
| memory->_property._VeryShortStandaloneMonthSymbols = NULL; |
| memory->_property._WeekdaySymbols = NULL; |
| memory->_property._ShortWeekdaySymbols = NULL; |
| memory->_property._VeryShortWeekdaySymbols = NULL; |
| memory->_property._StandaloneWeekdaySymbols = NULL; |
| memory->_property._ShortStandaloneWeekdaySymbols = NULL; |
| memory->_property._VeryShortStandaloneWeekdaySymbols = NULL; |
| memory->_property._QuarterSymbols = NULL; |
| memory->_property._ShortQuarterSymbols = NULL; |
| memory->_property._StandaloneQuarterSymbols = NULL; |
| memory->_property._ShortStandaloneQuarterSymbols = NULL; |
| memory->_property._AMSymbol = NULL; |
| memory->_property._PMSymbol = NULL; |
| memory->_property._AmbiguousYearStrategy = NULL; |
| memory->_property._UsesCharacterDirection = NULL; |
| memory->_property._FormattingContext = NULL; |
| memory->_property._CustomEraSymbols = NULL; |
| memory->_property._CustomMonthSymbols = NULL; |
| memory->_property._CustomShortMonthSymbols = NULL; |
| memory->_property._CustomWeekdaySymbols = NULL; |
| memory->_property._CustomShortWeekdaySymbols = NULL; |
| memory->_property._CustomLongEraSymbols = NULL; |
| memory->_property._CustomVeryShortMonthSymbols = NULL; |
| memory->_property._CustomVeryShortWeekdaySymbols = NULL; |
| memory->_property._CustomStandaloneMonthSymbols = NULL; |
| memory->_property._CustomShortStandaloneMonthSymbols = NULL; |
| memory->_property._CustomVeryShortStandaloneMonthSymbols = NULL; |
| memory->_property._CustomStandaloneWeekdaySymbols = NULL; |
| memory->_property._CustomShortStandaloneWeekdaySymbols = NULL; |
| memory->_property._CustomVeryShortStandaloneWeekdaySymbols = NULL; |
| memory->_property._CustomQuarterSymbols = NULL; |
| memory->_property._CustomShortQuarterSymbols = NULL; |
| memory->_property._CustomStandaloneQuarterSymbols = NULL; |
| memory->_property._CustomShortStandaloneQuarterSymbols = NULL; |
| memory->_property._CustomDateFormat = NULL; |
| memory->_property._CustomTimeFormat = NULL; |
| memory->_property._Custom24Hour = NULL; |
| memory->_property._Custom12Hour = NULL; |
| memory->_property._CustomAMSymbol = NULL; |
| memory->_property._CustomPMSymbol = NULL; |
| memory->_property._CustomFirstWeekday = NULL; |
| memory->_property._CustomMinDaysInFirstWeek = NULL; |
| |
| switch (dateStyle) { |
| case kCFDateFormatterNoStyle: |
| case kCFDateFormatterShortStyle: |
| case kCFDateFormatterMediumStyle: |
| case kCFDateFormatterLongStyle: |
| case kCFDateFormatterFullStyle: break; |
| default: |
| CFAssert2(0, __kCFLogAssertion, "%s(): unknown date style %ld", __PRETTY_FUNCTION__, dateStyle); |
| memory->_dateStyle = kCFDateFormatterMediumStyle; |
| break; |
| } |
| switch (timeStyle) { |
| case kCFDateFormatterNoStyle: |
| case kCFDateFormatterShortStyle: |
| case kCFDateFormatterMediumStyle: |
| case kCFDateFormatterLongStyle: |
| case kCFDateFormatterFullStyle: break; |
| default: |
| CFAssert2(0, __kCFLogAssertion, "%s(): unknown time style %ld", __PRETTY_FUNCTION__, timeStyle); |
| memory->_timeStyle = kCFDateFormatterMediumStyle; |
| break; |
| } |
| |
| //Prior to Gala, CFLocaleCreateCopy() always just retained. This caused problems because CFLocaleGetValue(locale, kCFLocaleCalendarKey) would create a calendar, then set its locale to self, leading to a retain cycle |
| //Since we're not in that situation here, and this is a frequently used path, we retain as we used to |
| memory->_locale = locale ? CFRetain(locale) : (CFLocaleRef)CFRetain(CFLocaleGetSystem()); |
| |
| if (kCFBooleanTrue == calculateISO8601) { |
| CFCalendarRef iso8601Cal = CFCalendarCreateWithIdentifier(kCFAllocatorSystemDefault, kCFGregorianCalendar); |
| CFCalendarSetFirstWeekday(iso8601Cal, UCAL_MONDAY); |
| CFCalendarSetMinimumDaysInFirstWeek(iso8601Cal, 4); |
| memory->_property._Calendar = iso8601Cal; |
| } |
| |
| memory->_property._TimeZone = CFTimeZoneCopyDefault(); |
| |
| CFStringRef calident = (CFStringRef)CFLocaleGetValue(memory->_locale, kCFLocaleCalendarIdentifierKey); |
| if (calident && CFEqual(calident, kCFCalendarIdentifierGregorian)) { |
| memory->_property._TwoDigitStartDate = CFDateCreate(kCFAllocatorSystemDefault, -1609459200.0); // 1950-01-01 00:00:00 +0000 |
| } |
| |
| __ResetUDateFormat(memory, false); |
| if (!memory->_df) { |
| CFRelease(memory); |
| return NULL; |
| } |
| return (CFDateFormatterRef)memory; |
| } |
| |
| #define FORMAT_STRING_MAX_LENGTH 29 // Length of "yyyy-'W'ww-dd'T'HH:mm:ssXXXXX" |
| static CFMutableStringRef __createISO8601FormatString(CFISO8601DateFormatOptions options) { |
| CFMutableStringRef resultStr = CFStringCreateMutable(kCFAllocatorSystemDefault, FORMAT_STRING_MAX_LENGTH); |
| |
| BOOL useSpaceInsteadOfTChar = (options & kCFISO8601DateFormatWithSpaceBetweenDateAndTime) == kCFISO8601DateFormatWithSpaceBetweenDateAndTime; |
| BOOL useDashSeparator = (options & kCFISO8601DateFormatWithDashSeparatorInDate) == kCFISO8601DateFormatWithDashSeparatorInDate; |
| BOOL useColonSeparatorInTime = (options & kCFISO8601DateFormatWithColonSeparatorInTime) == kCFISO8601DateFormatWithColonSeparatorInTime; |
| BOOL useColonSeparatorInTimeZone = (options & kCFISO8601DateFormatWithColonSeparatorInTimeZone) == kCFISO8601DateFormatWithColonSeparatorInTimeZone; |
| |
| if ((options & kCFISO8601DateFormatWithInternetDateTime) == kCFISO8601DateFormatWithInternetDateTime) { |
| // Check for dashes |
| if (useDashSeparator == NO) { |
| CFStringAppendCString(resultStr, "yyyyMMdd", kCFStringEncodingUTF8); |
| } else { |
| CFStringAppendCString(resultStr, "yyyy-MM-dd", kCFStringEncodingUTF8); |
| } |
| |
| // Check for T separator |
| if (useSpaceInsteadOfTChar == NO) { |
| CFStringAppendCString(resultStr, "'T'", kCFStringEncodingUTF8); |
| } else { |
| CFStringAppendCString(resultStr, " ", kCFStringEncodingUTF8); |
| } |
| |
| // Check for colons |
| if (useColonSeparatorInTime == NO) { |
| CFStringAppendCString(resultStr, "HHmmss", kCFStringEncodingUTF8); |
| } else { |
| CFStringAppendCString(resultStr, "HH:mm:ss", kCFStringEncodingUTF8); |
| } |
| |
| // Check for time zone separator |
| if (useColonSeparatorInTimeZone == NO) { |
| CFStringAppendCString(resultStr, "XXXX", kCFStringEncodingUTF8); // Basic Format (e.g. +080500) |
| } else { |
| CFStringAppendCString(resultStr, "XXXXX", kCFStringEncodingUTF8); // Extended Format (e.g. +08:05:00) |
| } |
| |
| return resultStr; // Skip the loop below. |
| } |
| |
| unsigned int currentOption = 0, allOptions = options; |
| for (int i = 0; currentOption < allOptions; currentOption = 1 << i, i++) { |
| BOOL firstFormatSymbol = NO; |
| if (CFStringGetLength(resultStr) < 1) { |
| firstFormatSymbol = YES; |
| } |
| switch(currentOption) { |
| case kCFISO8601DateFormatWithYear: |
| if ((options & kCFISO8601DateFormatWithYear) == kCFISO8601DateFormatWithYear) { |
| BOOL includesWeekOfYear = (options & kCFISO8601DateFormatWithWeekOfYear) == kCFISO8601DateFormatWithWeekOfYear; |
| if (includesWeekOfYear == NO) { |
| CFStringAppendCString(resultStr, "yyyy", kCFStringEncodingUTF8); // Year for calendar dates |
| } else { |
| CFStringAppendCString(resultStr, "YYYY", kCFStringEncodingUTF8); // Year for week dates |
| } |
| } |
| break; |
| case kCFISO8601DateFormatWithMonth: |
| if ((options & kCFISO8601DateFormatWithMonth) == kCFISO8601DateFormatWithMonth) { |
| if ((firstFormatSymbol == NO) && useDashSeparator) { |
| CFStringAppendCString(resultStr, "-", kCFStringEncodingUTF8); |
| } |
| CFStringAppendCString(resultStr, "MM", kCFStringEncodingUTF8); |
| } |
| break; |
| case kCFISO8601DateFormatWithWeekOfYear: |
| if ((options & kCFISO8601DateFormatWithWeekOfYear) == kCFISO8601DateFormatWithWeekOfYear) { |
| if ((firstFormatSymbol == NO) && useDashSeparator) { |
| CFStringAppendCString(resultStr, "-", kCFStringEncodingUTF8); |
| } |
| CFStringAppendCString(resultStr, "'W'ww", kCFStringEncodingUTF8); |
| } |
| break; |
| case kCFISO8601DateFormatWithDay: |
| if ((options & kCFISO8601DateFormatWithDay) == kCFISO8601DateFormatWithDay) { |
| if ((firstFormatSymbol == NO) && useDashSeparator) { |
| CFStringAppendCString(resultStr, "-", kCFStringEncodingUTF8); |
| } |
| |
| BOOL includesMonth = (options & kCFISO8601DateFormatWithMonth) == kCFISO8601DateFormatWithMonth; |
| BOOL includesWeekOfYear = (options & kCFISO8601DateFormatWithWeekOfYear) == kCFISO8601DateFormatWithWeekOfYear; |
| |
| if (includesWeekOfYear == NO) { |
| if (includesMonth == NO) { |
| CFStringAppendCString(resultStr, "DDD", kCFStringEncodingUTF8); // Day of Year |
| } else { |
| CFStringAppendCString(resultStr, "dd", kCFStringEncodingUTF8); // Day of Month |
| } |
| } else { // If week is included, we want to use the day local to that week. |
| CFStringAppendCString(resultStr, "ee", kCFStringEncodingUTF8); // Local day of week |
| } |
| } |
| break; |
| case kCFISO8601DateFormatWithTime: |
| if ((options & kCFISO8601DateFormatWithTime) == kCFISO8601DateFormatWithTime) { |
| if (firstFormatSymbol == NO) { // This matters for T (or Space) character |
| if (useSpaceInsteadOfTChar == NO) { |
| CFStringAppendCString(resultStr, "'T'", kCFStringEncodingUTF8); |
| } else { |
| CFStringAppendCString(resultStr, " ", kCFStringEncodingUTF8); |
| } |
| } |
| // Check for colons |
| if (useColonSeparatorInTime == NO) { |
| CFStringAppendCString(resultStr, "HHmmss", kCFStringEncodingUTF8); |
| } else { |
| CFStringAppendCString(resultStr, "HH:mm:ss", kCFStringEncodingUTF8); |
| } |
| } |
| break; |
| case kCFISO8601DateFormatWithTimeZone: |
| if ((options & kCFISO8601DateFormatWithTimeZone) == kCFISO8601DateFormatWithTimeZone) { |
| // Check for time zone separator |
| if (useColonSeparatorInTimeZone == NO) { |
| CFStringAppendCString(resultStr, "XXXX", kCFStringEncodingUTF8); // Basic Format (e.g. +080500) |
| } else { |
| CFStringAppendCString(resultStr, "XXXXX", kCFStringEncodingUTF8); // Extended Format (e.g. +08:05:00) |
| } |
| } |
| break; |
| default: break; |
| } |
| } |
| |
| return resultStr; |
| } |
| |
| CFDateFormatterRef CFDateFormatterCreateISO8601Formatter(CFAllocatorRef allocator, CFISO8601DateFormatOptions formatOptions) { |
| CFStringRef localeStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, "en_US_POSIX", kCFStringEncodingUTF8); |
| CFLocaleRef locale = CFLocaleCreate(kCFAllocatorSystemDefault, localeStr); |
| CFDateFormatterRef ISO8601Formatter = __SetUpCFDateFormatter(allocator, locale, kCFDateFormatterNoStyle, kCFDateFormatterNoStyle, kCFBooleanTrue); // dateStyle and timeStyle are not relevant for ISO8601 |
| |
| if (formatOptions != 0) { |
| CFStringRef formatStr = __createISO8601FormatString(formatOptions); |
| if (formatStr) { |
| CFDateFormatterSetFormat(ISO8601Formatter, formatStr); |
| CFRelease(formatStr); |
| } |
| } |
| |
| CFRelease(localeStr); |
| CFRelease(locale); |
| |
| return ISO8601Formatter; |
| } |
| |
| CFDateFormatterRef CFDateFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFDateFormatterStyle dateStyle, CFDateFormatterStyle timeStyle) { |
| return __SetUpCFDateFormatter(allocator, locale, dateStyle, timeStyle, kCFBooleanFalse); |
| } |
| |
| |
| static void __substituteFormatStringFromPrefsDFRelative(CFDateFormatterRef formatter) { |
| |
| CFIndex dateLen = -1; |
| UChar dateBuffer[BUFFER_SIZE]; |
| if (kCFDateFormatterNoStyle != formatter->_dateStyle) { |
| if (formatter->_property._CustomDateFormat != NULL) { |
| dateLen = __CFMin(CFStringGetLength(formatter->_property._CustomDateFormat), BUFFER_SIZE); |
| CFStringGetCharacters(formatter->_property._CustomDateFormat, CFRangeMake(0, dateLen), (UniChar *)dateBuffer); |
| } |
| } |
| if (-1 == dateLen) { |
| UErrorCode status = U_ZERO_ERROR; |
| int32_t ret = __cficu_udat_toPatternRelativeDate(formatter->_df, dateBuffer, BUFFER_SIZE, &status); |
| if (!U_FAILURE(status)) { |
| dateLen = ret; |
| } |
| } |
| |
| CFIndex timeLen = -1; |
| UChar timeBuffer[BUFFER_SIZE]; |
| if (kCFDateFormatterNoStyle != formatter->_timeStyle) { |
| if (formatter->_property._CustomTimeFormat != NULL) { |
| timeLen = __CFMin(CFStringGetLength(formatter->_property._CustomTimeFormat), BUFFER_SIZE); |
| CFStringGetCharacters(formatter->_property._CustomTimeFormat, CFRangeMake(0, timeLen), (UniChar *)timeBuffer); |
| } |
| } |
| if (-1 == timeLen) { |
| UErrorCode status = U_ZERO_ERROR; |
| int32_t ret = __cficu_udat_toPatternRelativeTime(formatter->_df, timeBuffer, BUFFER_SIZE, &status); |
| if (!U_FAILURE(status)) { |
| timeLen = ret; |
| } |
| } |
| |
| UErrorCode status = U_ZERO_ERROR; |
| __cficu_udat_applyPatternRelative(formatter->_df, (0 <= dateLen) ? dateBuffer : NULL, (0 <= dateLen) ? dateLen : 0, (0 <= timeLen) ? timeBuffer : NULL, (0 <= timeLen) ? timeLen : 0, &status); |
| } |
| |
| static void __substituteFormatStringFromPrefsDF(CFDateFormatterRef formatter, bool doTime) { |
| CFIndex formatStyle = doTime ? formatter->_timeStyle : formatter->_dateStyle; |
| CFStringRef pref = doTime ? formatter->_property._CustomTimeFormat : formatter->_property._CustomDateFormat; |
| if (kCFDateFormatterNoStyle != formatStyle) { |
| if (NULL != pref) { |
| int32_t icustyle = UDAT_NONE; |
| switch (formatStyle) { |
| case kCFDateFormatterShortStyle: icustyle = UDAT_SHORT; break; |
| case kCFDateFormatterMediumStyle: icustyle = UDAT_MEDIUM; break; |
| case kCFDateFormatterLongStyle: icustyle = UDAT_LONG; break; |
| case kCFDateFormatterFullStyle: icustyle = UDAT_FULL; break; |
| } |
| CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); |
| char buffer[BUFFER_SIZE]; |
| const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII); |
| if (NULL == cstr) { |
| if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer; |
| } |
| UErrorCode status = U_ZERO_ERROR; |
| UDateFormat *df = __cficu_udat_open((UDateFormatStyle)(doTime ? icustyle : UDAT_NONE), (UDateFormatStyle)(doTime ? UDAT_NONE : icustyle), cstr, NULL, 0, NULL, 0, &status); |
| if (NULL != df) { |
| UChar ubuffer[BUFFER_SIZE]; |
| status = U_ZERO_ERROR; |
| int32_t date_len = __cficu_udat_toPattern(df, false, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && date_len <= BUFFER_SIZE) { |
| CFStringRef dateString = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)ubuffer, date_len); |
| status = U_ZERO_ERROR; |
| int32_t formatter_len = __cficu_udat_toPattern(formatter->_df, false, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && formatter_len <= BUFFER_SIZE) { |
| CFMutableStringRef formatString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); |
| CFStringAppendCharacters(formatString, (UniChar *)ubuffer, formatter_len); |
| // find dateString inside formatString, substitute the pref in that range |
| CFRange result; |
| if (CFStringFindWithOptions(formatString, dateString, CFRangeMake(0, formatter_len), 0, &result)) { |
| CFStringReplace(formatString, result, pref); |
| int32_t new_len = CFStringGetLength(formatString); |
| STACK_BUFFER_DECL(UChar, new_buffer, new_len); |
| const UChar *new_ustr = (UChar *)CFStringGetCharactersPtr(formatString); |
| if (NULL == new_ustr) { |
| CFStringGetCharacters(formatString, CFRangeMake(0, new_len), (UniChar *)new_buffer); |
| new_ustr = new_buffer; |
| } |
| status = U_ZERO_ERROR; |
| // __cficu_udat_applyPattern(formatter->_df, false, new_ustr, new_len, &status); |
| __cficu_udat_applyPattern(formatter->_df, false, new_ustr, new_len); |
| } |
| CFRelease(formatString); |
| } |
| CFRelease(dateString); |
| } |
| __cficu_udat_close(df); |
| } |
| } |
| } |
| } |
| |
| static void __CFDateFormatterStoreSymbolPrefs(const void *key, const void *value, void *context) { |
| if (CFGetTypeID(key) == CFStringGetTypeID() && CFGetTypeID(value) == CFArrayGetTypeID()) { |
| CFDateFormatterRef formatter = (CFDateFormatterRef)context; |
| UDateFormatSymbolType sym = (UDateFormatSymbolType)CFStringGetIntValue((CFStringRef)key); |
| CFArrayRef array = (CFArrayRef)value; |
| CFIndex idx, cnt = CFArrayGetCount(array); |
| switch (sym) { |
| case UDAT_ERAS: |
| formatter->_property._CustomEraSymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_MONTHS: |
| formatter->_property._CustomMonthSymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_SHORT_MONTHS: |
| formatter->_property._CustomShortMonthSymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_WEEKDAYS: |
| formatter->_property._CustomWeekdaySymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_SHORT_WEEKDAYS: |
| formatter->_property._CustomShortWeekdaySymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_AM_PMS: |
| { |
| for (idx = 0; idx < cnt; idx++) { |
| CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, idx); |
| if (CFGetTypeID(item) != CFStringGetTypeID()) continue; |
| if (idx == 0) { |
| formatter->_property._CustomAMSymbol = (CFStringRef)CFRetain(item); |
| } else if (idx == 1) { |
| formatter->_property._CustomPMSymbol = (CFStringRef)CFRetain(item); |
| } |
| } |
| } |
| break; |
| case UDAT_ERA_NAMES: |
| formatter->_property._CustomLongEraSymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_NARROW_MONTHS: |
| formatter->_property._CustomVeryShortMonthSymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_NARROW_WEEKDAYS: |
| formatter->_property._CustomVeryShortWeekdaySymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_STANDALONE_MONTHS: |
| formatter->_property._CustomStandaloneMonthSymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_STANDALONE_SHORT_MONTHS: |
| formatter->_property._CustomShortStandaloneMonthSymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_STANDALONE_NARROW_MONTHS: |
| formatter->_property._CustomVeryShortStandaloneMonthSymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_STANDALONE_WEEKDAYS: |
| formatter->_property._CustomStandaloneWeekdaySymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_STANDALONE_SHORT_WEEKDAYS: |
| formatter->_property._CustomShortStandaloneWeekdaySymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_STANDALONE_NARROW_WEEKDAYS: |
| formatter->_property._CustomVeryShortStandaloneWeekdaySymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_QUARTERS: |
| formatter->_property._CustomQuarterSymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_SHORT_QUARTERS: |
| formatter->_property._CustomShortQuarterSymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_STANDALONE_QUARTERS: |
| formatter->_property._CustomStandaloneQuarterSymbols = (CFArrayRef)CFRetain(array); |
| break; |
| case UDAT_STANDALONE_SHORT_QUARTERS: |
| formatter->_property._CustomShortStandaloneQuarterSymbols = (CFArrayRef)CFRetain(array); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| static CFStringRef __CFDateFormatterCreateForcedTemplate(CFLocaleRef locale, CFStringRef inString, Boolean stripAMPM) { |
| if (!inString) return NULL; |
| Boolean doForce24 = false, doForce12 = false; |
| CFDictionaryRef prefs = __CFLocaleGetPrefs(locale); |
| CFPropertyListRef pref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce24HourTime")) : NULL; |
| if (NULL != pref && CFGetTypeID(pref) == CFBooleanGetTypeID()) { |
| doForce24 = CFBooleanGetValue((CFBooleanRef)pref); |
| } |
| pref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce12HourTime")) : NULL; |
| if (NULL != pref && CFGetTypeID(pref) == CFBooleanGetTypeID()) { |
| doForce12 = CFBooleanGetValue((CFBooleanRef)pref); |
| } |
| if (doForce24) doForce12 = false; // if both are set, Force24 wins, period |
| if (!doForce24 && !doForce12) return (CFStringRef)CFRetain(inString); |
| |
| CFMutableStringRef outString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0); |
| CFIndex cnt = CFStringGetLength(inString); |
| CFIndex lastSecond = -1, lastMinute = -1, firstHour = -1; |
| Boolean isInQuote = false, hasA = false, had12Hour = false, had24Hour = false; |
| for (CFIndex idx = 0; idx < cnt; idx++) { |
| Boolean emit = true; |
| UniChar ch = CFStringGetCharacterAtIndex(inString, idx); |
| switch (ch) { |
| case '\'': isInQuote = !isInQuote; break; |
| case 'J': //fall through |
| case 'j': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); if (doForce24) ch = 'H'; else ch = 'h';} break; |
| case 'h': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had12Hour = true; if (doForce24) ch = 'H';} break; // switch 12-hour to 24-hour |
| case 'K': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had12Hour = true; if (doForce24) ch = 'k';} break; // switch 12-hour to 24-hour |
| case 'H': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had24Hour = true; if (doForce12) ch = 'h';} break; // switch 24-hour to 12-hour |
| case 'k': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had24Hour = true; if (doForce12) ch = 'K';} break; // switch 24-hour to 12-hour |
| case 'm': if (!isInQuote) lastMinute = CFStringGetLength(outString); break; |
| case 's': if (!isInQuote) lastSecond = CFStringGetLength(outString); break; |
| case 'a': if (!isInQuote) {hasA = true; if (doForce24 || stripAMPM) emit = false;} break; |
| break; |
| } |
| if (emit) CFStringAppendCharacters(outString, &ch, 1); |
| } |
| |
| return outString; |
| } |
| |
| static CFStringRef __CFDateFormatterCreateForcedString(CFDateFormatterRef formatter, CFStringRef inString) { |
| if (!inString) return NULL; |
| |
| UDateTimePatternMatchOptions options = UDATPG_MATCH_NO_OPTIONS; |
| #if __HAS_APPLE_ICU__ |
| if (formatter->_property._Custom12Hour != NULL && CFBooleanGetValue((CFBooleanRef)formatter->_property._Custom12Hour)) { |
| options = UADATPG_FORCE_12_HOUR_CYCLE; |
| } |
| if (formatter->_property._Custom24Hour != NULL && CFBooleanGetValue((CFBooleanRef)formatter->_property._Custom24Hour)) { |
| options = UADATPG_FORCE_24_HOUR_CYCLE; //force 24 hour always wins if both are specified |
| } |
| #endif |
| if (options == UDATPG_MATCH_NO_OPTIONS) return (CFStringRef)CFRetain(inString); |
| |
| static CFCharacterSetRef hourCharacters; |
| static dispatch_once_t onceToken; |
| dispatch_once(&onceToken, ^{ |
| hourCharacters = CFCharacterSetCreateWithCharactersInString(kCFAllocatorSystemDefault, CFSTR("hHkK")); |
| }); |
| |
| CFRange hourRange = CFRangeMake(kCFNotFound, 0); |
| if (!CFStringFindCharacterFromSet(inString, hourCharacters, CFRangeMake(0, CFStringGetLength(inString)), 0, &hourRange) || hourRange.location == kCFNotFound) { |
| return (CFStringRef)CFRetain(inString); |
| } |
| __block CFStringRef result = NULL; |
| __block int32_t newPatternLen = 0; |
| #if __HAS_APPLE_ICU__ |
| Boolean success = useTemplatePatternGenerator(formatter->_locale, ^(UDateTimePatternGenerator *ptg) { |
| CFIndex cnt = CFStringGetLength(inString); |
| STACK_BUFFER_DECL(UChar, ubuffer, cnt); |
| const UChar *ustr = (UChar *)CFStringGetCharactersPtr(inString); |
| if (NULL == ustr) { |
| CFStringGetCharacters(inString, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| ustr = ubuffer; |
| } |
| STACK_BUFFER_DECL(UChar, outBuffer, 256); |
| |
| UErrorCode err = U_ZERO_ERROR; |
| newPatternLen = uadatpg_remapPatternWithOptions(ptg, ustr, cnt, options, outBuffer, 256, &err); |
| if (U_SUCCESS(err)) { |
| result = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, outBuffer, newPatternLen); |
| } else if (err == U_BUFFER_OVERFLOW_ERROR) { |
| err = U_ZERO_ERROR; |
| UChar *largerBuffer = calloc(newPatternLen + 1, sizeof(UChar)); |
| newPatternLen = uadatpg_remapPatternWithOptions(ptg, ustr, cnt, options, largerBuffer, newPatternLen + 1, &err); |
| if (U_SUCCESS(err)) { |
| result = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, largerBuffer, newPatternLen); |
| } |
| free(largerBuffer); |
| } |
| }); |
| #else |
| Boolean success = false; |
| #endif |
| return success && result && newPatternLen > 0 ? result : CFRetain(inString); |
| } |
| |
| CFLocaleRef CFDateFormatterGetLocale(CFDateFormatterRef formatter) { |
| __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
| return formatter->_locale; |
| } |
| |
| CFDateFormatterStyle CFDateFormatterGetDateStyle(CFDateFormatterRef formatter) { |
| __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
| return formatter->_dateStyle; |
| } |
| |
| CFDateFormatterStyle CFDateFormatterGetTimeStyle(CFDateFormatterRef formatter) { |
| __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
| return formatter->_timeStyle; |
| } |
| |
| CFStringRef CFDateFormatterGetFormat(CFDateFormatterRef formatter) { |
| __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
| return formatter->_format; |
| } |
| |
| void CFDateFormatterSetFormat(CFDateFormatterRef formatter, CFStringRef formatString) { |
| __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
| __CFGenericValidateType(formatString, CFStringGetTypeID()); |
| formatString = __CFDateFormatterCreateForcedString(formatter, formatString); |
| CFIndex cnt = CFStringGetLength(formatString); |
| CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__); |
| if (formatter->_format != formatString && cnt <= 1024) { |
| // When going from a situation where there is no custom format already, |
| // and the "relative date formatting" property is set, we need to reset |
| // the whole UDateFormat. |
| if (formatter->_property._HasCustomFormat != kCFBooleanTrue && formatter->_property._DoesRelativeDateFormatting == kCFBooleanTrue) { |
| __ResetUDateFormat(formatter, true); |
| // the "true" results in: if you set a custom format string, you don't get relative date formatting |
| } |
| STACK_BUFFER_DECL(UChar, ubuffer, cnt); |
| const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)formatString); |
| if (NULL == ustr) { |
| CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)ubuffer); |
| ustr = ubuffer; |
| } |
| UErrorCode status = U_ZERO_ERROR; |
| // __cficu_udat_applyPattern(formatter->_df, false, ustr, cnt, &status); |
| __cficu_udat_applyPattern(formatter->_df, false, ustr, cnt); |
| if (U_SUCCESS(status)) { |
| if (formatter->_format) CFRelease(formatter->_format); |
| formatter->_format = (CFStringRef)CFStringCreateCopy(CFGetAllocator(formatter), formatString); |
| formatter->_property._HasCustomFormat = kCFBooleanTrue; |
| } |
| } |
| if (formatString) CFRelease(formatString); |
| } |
| |
| CFStringRef CFDateFormatterCreateStringWithDate(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFDateRef date) { |
| if (allocator == NULL) allocator = __CFGetDefaultAllocator(); |
| __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); |
| __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
| __CFGenericValidateType(date, CFDateGetTypeID()); |
| return CFDateFormatterCreateStringWithAbsoluteTime(allocator, formatter, CFDateGetAbsoluteTime(date)); |
| } |
| |
| CFStringRef CFDateFormatterCreateStringWithAbsoluteTime(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFAbsoluteTime at) { |
| if (allocator == NULL) allocator = __CFGetDefaultAllocator(); |
| __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); |
| __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
| UChar *ustr = NULL, ubuffer[BUFFER_SIZE + 1]; |
| UErrorCode status = U_ZERO_ERROR; |
| CFIndex used, cnt = BUFFER_SIZE; |
| UDate ud = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0 + 0.5; |
| used = __cficu_udat_format(formatter->_df, ud, ubuffer + 1, cnt, NULL, &status); |
| if (status == U_BUFFER_OVERFLOW_ERROR || cnt < used) { |
| cnt = used + 1 + 1; // leave room for RTL marker if needed |
| ustr = (UChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UChar) * cnt, 0); |
| status = U_ZERO_ERROR; |
| used = __cficu_udat_format(formatter->_df, ud, ustr + 1, cnt, NULL, &status); |
| } |
| CFStringRef string = NULL; |
| if (U_SUCCESS(status)) { |
| UniChar *bufferToUse = ustr ? (UniChar *)ustr : (UniChar *)ubuffer; |
| if (formatter->_property._UsesCharacterDirection == kCFBooleanTrue && CFLocaleGetLanguageCharacterDirection(CFLocaleGetIdentifier(formatter->_locale)) == kCFLocaleLanguageDirectionRightToLeft) { |
| // Insert Unicode RTL marker |
| bufferToUse[0] = 0x200F; |
| used++; |
| } else { |
| // Move past direction marker |
| bufferToUse++; |
| } |
| string = CFStringCreateWithCharacters(allocator, bufferToUse, used); |
| } |
| if (ustr) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ustr); |
| return string; |
| } |
| |
| #if U_ICU_VERSION_MAJOR_NUM > 55 |
| static const char * const _ICUPatternMap = "GyMdkHmsSEDFwWahKzYeugAZvcLQqVUOXxr:"; |
| |
| CFAttributedStringRef _CFDateFormatterCreateAttributedStringAndFieldsWithAbsoluteTime(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFAbsoluteTime at) { |
| if (allocator == NULL) allocator = __CFGetDefaultAllocator(); |
| __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); |
| __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
| UChar *ustr = NULL, ubuffer[BUFFER_SIZE + 1]; |
| UErrorCode status = U_ZERO_ERROR; |
| CFIndex used, cnt = BUFFER_SIZE; |
| UDate ud = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0 + 0.5; |
| UFieldPositionIterator *fpositer = ufieldpositer_open(&status); |
| used = __cficu_udat_formatForFields(formatter->_df, ud, ubuffer + 1, cnt, fpositer, &status); |
| if (status == U_BUFFER_OVERFLOW_ERROR || cnt < used) { |
| cnt = used + 1 + 1; // leave room for RTL marker if needed |
| ustr = (UChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UChar) * cnt, 0); |
| status = U_ZERO_ERROR; |
| ufieldpositer_close(fpositer); |
| fpositer = ufieldpositer_open(&status); |
| used = __cficu_udat_formatForFields(formatter->_df, ud, ustr + 1, cnt, fpositer, &status); |
| } |
| CFStringRef string = NULL; |
| CFMutableAttributedStringRef attributed = NULL; |
| if (U_SUCCESS(status)) { |
| UniChar *bufferToUse = ustr ? (UniChar *)ustr : (UniChar *)ubuffer; |
| if (formatter->_property._UsesCharacterDirection == kCFBooleanTrue && CFLocaleGetLanguageCharacterDirection(CFLocaleGetIdentifier(formatter->_locale)) == kCFLocaleLanguageDirectionRightToLeft) { |
| // Insert Unicode RTL marker |
| bufferToUse[0] = 0x200F; |
| used++; |
| } else { |
| // Move past direction marker |
| bufferToUse++; |
| } |
| string = CFStringCreateWithCharacters(allocator, bufferToUse, used); |
| |
| } |
| if (ustr) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ustr); |
| if (string) { |
| attributed = CFAttributedStringCreateMutable(allocator, CFStringGetLength(string)); |
| CFAttributedStringReplaceString(attributed, CFRangeMake(0, 0), string); |
| CFRelease(string); |
| int32_t field; |
| int32_t beginIndex; |
| int32_t endIndex; |
| size_t limit = strlen(_ICUPatternMap); |
| while ((field = ufieldpositer_next(fpositer, &beginIndex, &endIndex)) >= 0) { |
| if (field >= limit) { |
| continue; |
| } |
| CFStringRef pattern = CFStringCreateWithBytes(allocator, (UInt8 *)_ICUPatternMap+field, 1, kCFStringEncodingASCII, false); |
| CFAttributedStringSetAttribute(attributed, CFRangeMake(beginIndex, endIndex-beginIndex), kCFDateFormatterPatternCharacterKey, pattern); |
| CFRelease(pattern); |
| } |
| } |
| if (fpositer != NULL) { |
| ufieldpositer_close(fpositer); |
| } |
| return attributed; |
| } |
| #endif |
| |
| static UDate __CFDateFormatterCorrectTimeWithTarget(UCalendar *calendar, UDate at, int32_t target, Boolean isEra, UErrorCode *status) { |
| __cficu_ucal_setMillis(calendar, at, status); |
| UCalendarDateFields field = isEra ? UCAL_ERA : UCAL_YEAR; |
| __cficu_ucal_set(calendar, field, target); |
| return __cficu_ucal_getMillis(calendar, status); |
| } |
| |
| static UDate __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(UCalendar *calendar, UDate at, CFIndex period, CFIndex pastYears, CFIndex futureYears, Boolean isEra, UErrorCode *status) { |
| __cficu_ucal_setMillis(calendar, __cficu_ucal_getNow(), status); |
| int32_t currYear = __cficu_ucal_get(calendar, UCAL_YEAR, status); |
| UCalendarDateFields field = isEra ? UCAL_ERA : UCAL_YEAR; |
| int32_t currEraOrCentury = __cficu_ucal_get(calendar, field, status); |
| if (!isEra) { |
| currYear %= 100; |
| currEraOrCentury = currEraOrCentury / 100 * 100; // get century |
| } |
| |
| CFIndex futureMax = currYear + futureYears; |
| CFIndex pastMin = currYear - pastYears; |
| |
| CFRange currRange, futureRange, pastRange; |
| currRange.location = futureRange.location = pastRange.location = kCFNotFound; |
| currRange.length = futureRange.length = pastRange.length = 0; |
| if (!isEra) { |
| if (period < INT_MAX && futureMax >= period) { |
| futureRange.location = 0; |
| futureRange.length = futureMax - period + 1; |
| } |
| if (pastMin < 0) { |
| pastRange.location = period + pastMin; |
| pastRange.length = period - pastRange.location; |
| } |
| if (pastRange.location != kCFNotFound) { |
| currRange.location = 0; |
| } else { |
| currRange.location = pastMin; |
| } |
| } else { |
| if (period < INT_MAX && futureMax > period) { |
| futureRange.location = 1, |
| futureRange.length = futureMax - period; |
| } |
| if (pastMin <= 0) { |
| pastRange.location = period + pastMin; |
| pastRange.length = period - pastRange.location + 1; |
| } |
| if (pastRange.location != kCFNotFound) { |
| currRange.location = 1; |
| } else { |
| currRange.location = pastMin; |
| } |
| |
| } |
| currRange.length = period - pastRange.length - futureRange.length; |
| |
| __cficu_ucal_setMillis(calendar, at, status); |
| int32_t atYear = __cficu_ucal_get(calendar, UCAL_YEAR, status); |
| if (!isEra) { |
| atYear %= 100; |
| currEraOrCentury += atYear; |
| } |
| |
| int32_t offset = 0; // current era or century |
| if (pastRange.location != kCFNotFound && atYear >= pastRange.location && atYear - pastRange.location + 1 <= pastRange.length) { |
| offset = -1; // past era or century |
| } else if (futureRange.location != kCFNotFound && atYear >= futureRange.location && atYear - futureRange.location + 1 <= futureRange.length) { |
| offset = 1; // next era or century |
| } |
| if (!isEra) offset *= 100; |
| return __CFDateFormatterCorrectTimeWithTarget(calendar, at, currEraOrCentury+offset, isEra, status); |
| } |
| |
| #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS |
| static int32_t __CFDateFormatterGetMaxYearGivenJapaneseEra(UCalendar *calendar, int32_t era, UErrorCode *status) { |
| int32_t years = 0; |
| __cficu_ucal_clear(calendar); |
| __cficu_ucal_set(calendar, UCAL_ERA, era+1); |
| UDate target = __cficu_ucal_getMillis(calendar, status); |
| __cficu_ucal_set(calendar, UCAL_ERA, era); |
| years = __cficu_ucal_getFieldDifference(calendar, target, UCAL_YEAR, status); |
| return years+1; |
| } |
| #endif |
| |
| static Boolean __CFDateFormatterHandleAmbiguousYear(CFDateFormatterRef formatter, CFStringRef calendar_id, UDateFormat *df, UCalendar *cal, UDate *at, const UChar *ustr, CFIndex length, UErrorCode *status) { |
| Boolean success = true; |
| int64_t ambigStrat = kCFDateFormatterAmbiguousYearAssumeToNone; |
| if (formatter->_property._AmbiguousYearStrategy) { |
| CFNumberGetValue(formatter->_property._AmbiguousYearStrategy, kCFNumberSInt64Type, &ambigStrat); |
| } |
| if (calendar_id == kCFCalendarIdentifierChinese) { |
| // we default to era 1 if era is missing, however, we cannot just test if the era is 1 becuase we may get era 2 or larger if the year in the string is greater than 60 |
| // now I just assume that the year will not be greater than 600 in the string |
| if (__cficu_ucal_get(cal, UCAL_ERA, status) < 10) { |
| switch (ambigStrat) { |
| case kCFDateFormatterAmbiguousYearFailToParse: |
| success = false; |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToCurrent: { |
| __cficu_ucal_setMillis(cal, __cficu_ucal_getNow(), status); |
| int32_t currEra = __cficu_ucal_get(cal, UCAL_ERA, status); |
| *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status); |
| break; |
| } |
| case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate: |
| *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 29, 30, true, status); |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToFuture: |
| *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 0, 59, true, status); |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToPast: |
| *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 59, 0, true, status); |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture: |
| *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 10, 49, true, status); |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToLikelyPast: |
| *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 49, 10, true, status); |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToNone: |
| default: |
| break; // do nothing |
| } |
| } |
| } else if (calendar_id == kCFCalendarIdentifierJapanese) { // ??? need more work |
| #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS |
| __cficu_ucal_clear(cal); |
| __cficu_ucal_set(cal, UCAL_ERA, 1); |
| __cficu_udat_parseCalendar(df, cal, ustr, length, NULL, status); |
| UDate test = __cficu_ucal_getMillis(cal, status); |
| if (test != *at) { // missing era |
| __cficu_ucal_setMillis(cal, *at, status); |
| int32_t givenYear = __cficu_ucal_get(cal, UCAL_YEAR, status); |
| __cficu_ucal_setMillis(cal, __cficu_ucal_getNow(), status); |
| int32_t currYear = __cficu_ucal_get(cal, UCAL_YEAR, status); |
| int32_t currEra = __cficu_ucal_get(cal, UCAL_ERA, status); |
| switch (ambigStrat) { |
| case kCFDateFormatterAmbiguousYearFailToParse: |
| success = false; |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToCurrent: |
| *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status); |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToFuture: |
| if (givenYear < currYear) { // we only consider current or the future |
| success = false; |
| } else { // current era |
| *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status); |
| } |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToPast: |
| if (givenYear > currYear) { // past era |
| success = false; |
| // we find the closest era that has the given year |
| // if no era has such given year, we fail the parse |
| for (CFIndex era = currEra-1; era >= 234; era--) { // Showa era (234) is the longest era |
| int32_t years = __CFDateFormatterGetMaxYearGivenJapaneseEra(cal, era, status); |
| if (givenYear > years) { |
| continue; |
| } |
| success = true; |
| *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, era, true, status); |
| break; |
| } |
| } else { // current era |
| *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status); |
| } |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture: |
| if (givenYear < currYear - 10) { // we allow 10 years to the past |
| success = false; |
| } else { |
| *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status); |
| } |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToLikelyPast: |
| if (givenYear > currYear + 10) { |
| success = false; |
| // we find the closest era that has the given year |
| // if no era has such given year, we fail the parse |
| for (CFIndex era = currEra-1; era >= 234; era--) { // Showa era (234) is the longest era |
| int32_t years = __CFDateFormatterGetMaxYearGivenJapaneseEra(cal, era, status); |
| if (givenYear > years) { |
| continue; |
| } |
| success = true; |
| *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, era, true, status); |
| break; |
| } |
| } else { // current era |
| *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status); |
| } |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToNone: |
| default: |
| break; // do nothing |
| } |
| } |
| #else |
| success = false; |
| #endif |
| } else { // calenders other than chinese and japanese |
| int32_t parsedYear = __cficu_ucal_get(cal, UCAL_YEAR, status); |
| if (parsedYear >= 12000 && parsedYear <= 12099) { // most likely that the parsed string had a 2-digits year |
| if (formatter->_property._TwoDigitStartDate != NULL) { |
| UCalendar *tempCal = __cficu_ucal_clone(cal, status); |
| __cficu_ucal_clear(tempCal); |
| CFAbsoluteTime twoDigitAt = CFDateGetAbsoluteTime(formatter->_property._TwoDigitStartDate); |
| UDate targetUdate = (twoDigitAt + kCFAbsoluteTimeIntervalSince1970) * 1000.0; |
| __cficu_ucal_setMillis(tempCal, targetUdate, status); |
| int targetYear = __cficu_ucal_get(tempCal, UCAL_YEAR, status); |
| parsedYear -= 12000; |
| int targetYearM100 = targetYear % 100; |
| if (targetYearM100 < parsedYear) { |
| parsedYear = ((targetYear / 100) * 100) + parsedYear; |
| } else if (parsedYear < targetYearM100) { |
| parsedYear = ((targetYear / 100) * 100) + 100 + parsedYear; |
| } else { |
| __cficu_ucal_set(cal, UCAL_YEAR, targetYear); |
| UDate parseUdate = __cficu_ucal_getMillis(cal, status); |
| if (parseUdate >= targetUdate) { |
| parsedYear = targetYear; |
| } else { |
| parsedYear = targetYear + 100; |
| } |
| } |
| __cficu_ucal_close(tempCal); |
| __cficu_ucal_set(cal, UCAL_YEAR, parsedYear); |
| *at = __cficu_ucal_getMillis(cal, status); |
| } else { |
| switch (ambigStrat) { |
| case kCFDateFormatterAmbiguousYearFailToParse: |
| success = false; |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToCurrent: |
| { |
| // we can modify cal here because cal is just a temp cal from the caller |
| __cficu_ucal_setMillis(cal, __cficu_ucal_getNow(), status); |
| int32_t currYear = __cficu_ucal_get(cal, UCAL_YEAR, status); |
| *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, (currYear / 100 * 100) + parsedYear % 100, false, status); |
| } |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate: |
| *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 50, 49, false, status); |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToFuture: |
| *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 0, 99, false, status); |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToPast: |
| *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 99, 0, false, status); |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture: |
| *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 9, 90, false, status); |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToLikelyPast: |
| *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 90, 9, false, status); |
| break; |
| case kCFDateFormatterAmbiguousYearAssumeToNone: |
| default: |
| if (calendar_id == kCFCalendarIdentifierGregorian) { // historical default behavior of 1950 - 2049 |
| int32_t twoDigits = parsedYear % 100; |
| *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, ((twoDigits < 50) ? 2000 : 1900) + twoDigits, false, status); |
| } |
| break; // do nothing |
| } |
| } |
| |
| } |
| } |
| return success; |
| } |
| |
| CFDateRef CFDateFormatterCreateDateFromString(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep) { |
| if (allocator == NULL) allocator = __CFGetDefaultAllocator(); |
| __CFGenericValidateType(allocator, CFAllocatorGetTypeID()); |
| __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
| __CFGenericValidateType(string, CFStringGetTypeID()); |
| CFAbsoluteTime at; |
| if (CFDateFormatterGetAbsoluteTimeFromString(formatter, string, rangep, &at)) { |
| return CFDateCreate(allocator, at); |
| } |
| return NULL; |
| } |
| |
| Boolean CFDateFormatterGetAbsoluteTimeFromString(CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep, CFAbsoluteTime *atp) { |
| __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
| __CFGenericValidateType(string, CFStringGetTypeID()); |
| CFRange range = {0, 0}; |
| if (rangep) { |
| range = *rangep; |
| } else { |
| range.length = CFStringGetLength(string); |
| } |
| if (1024 < range.length) range.length = 1024; |
| const UChar *ustr = (UChar *)CFStringGetCharactersPtr(string); |
| STACK_BUFFER_DECL(UChar, ubuffer, (NULL == ustr) ? range.length : 1); |
| if (NULL == ustr) { |
| CFStringGetCharacters(string, range, (UniChar *)ubuffer); |
| ustr = ubuffer; |
| } else { |
| ustr += range.location; |
| } |
| UDate udate; |
| int32_t dpos = 0; |
| UErrorCode status = U_ZERO_ERROR; |
| UDateFormat *df2 = __cficu_udat_clone(formatter->_df, &status); |
| const UCalendar *ucal2 = __cficu_udat_getCalendar(df2); |
| UCalendar *cal2 = __cficu_ucal_clone(ucal2, &status); |
| CFStringRef calendar_id = (CFStringRef) CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarIdentifierKey); |
| if (calendar_id != kCFCalendarIdentifierChinese && calendar_id != kCFCalendarIdentifierJapanese) { |
| __cficu_ucal_clear(cal2); |
| // set both year, and 2DigitYearStart to year 12000 |
| __cficu_ucal_set(cal2, UCAL_YEAR, 12000); |
| __cficu_udat_set2DigitYearStart(df2, 316516204800.0 * 1000.0, &status); |
| } else if (calendar_id == kCFCalendarIdentifierChinese) { |
| __cficu_ucal_clear(cal2); |
| __cficu_ucal_set(cal2, UCAL_ERA, 1); // default to era 1 if no era info in the string for chinese |
| } else if (calendar_id == kCFCalendarIdentifierJapanese) { // default to the current era |
| __cficu_ucal_setMillis(cal2, __cficu_ucal_getNow(), &status); |
| int32_t currEra = __cficu_ucal_get(cal2, UCAL_ERA, &status); |
| __cficu_ucal_clear(cal2); |
| __cficu_ucal_set(cal2, UCAL_ERA, currEra); |
| } |
| if (formatter->_property._DefaultDate) { |
| CFAbsoluteTime at = CFDateGetAbsoluteTime(formatter->_property._DefaultDate); |
| udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0; |
| __cficu_ucal_setMillis(cal2, udate, &status); |
| } |
| __cficu_udat_parseCalendar(df2, cal2, ustr, range.length, &dpos, &status); |
| udate = __cficu_ucal_getMillis(cal2, &status); |
| if (rangep) rangep->length = dpos; |
| Boolean success = false; |
| // first status check is for parsing and the second status check is for the work done inside __CFDateFormatterHandleAmbiguousYear() |
| if (!U_FAILURE(status) && (__CFDateFormatterHandleAmbiguousYear(formatter, calendar_id, df2, cal2, &udate, ustr, range.length, &status)) && !U_FAILURE(status)) { |
| if (atp) { |
| *atp = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970; |
| } |
| success = true; |
| } |
| CFRelease(calendar_id); |
| __cficu_udat_close(df2); |
| __cficu_ucal_close(cal2); |
| return success; |
| } |
| |
| static void __CFDateFormatterSetSymbolsArray(UDateFormat *icudf, int32_t icucode, int index_base, CFTypeRef value) { |
| UErrorCode status = U_ZERO_ERROR; |
| __CFGenericValidateType(value, CFArrayGetTypeID()); |
| CFArrayRef array = (CFArrayRef)value; |
| CFIndex idx, cnt = CFArrayGetCount(array); |
| for (idx = 0; idx < cnt; idx++) { |
| CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, idx); |
| __CFGenericValidateType(item, CFStringGetTypeID()); |
| CFIndex item_cnt = CFStringGetLength(item); |
| STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); |
| UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item); |
| if (NULL == item_ustr) { |
| item_cnt = __CFMin(BUFFER_SIZE, item_cnt); |
| CFStringGetCharacters(item, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); |
| item_ustr = item_buffer; |
| } |
| status = U_ZERO_ERROR; |
| __cficu_udat_setSymbols(icudf, (UDateFormatSymbolType)icucode, idx + index_base, item_ustr, item_cnt, &status); |
| } |
| } |
| |
| static CFArrayRef __CFDateFormatterCopySymbolsArray(UDateFormat *icudf, int32_t icucode, int index_base) { |
| UErrorCode status = U_ZERO_ERROR; |
| CFIndex idx, cnt = __cficu_udat_countSymbols(icudf, (UDateFormatSymbolType)icucode); |
| if (cnt <= index_base) return CFArrayCreate(kCFAllocatorSystemDefault, NULL, 0, &kCFTypeArrayCallBacks); |
| cnt = cnt - index_base; |
| STACK_BUFFER_DECL(CFStringRef, strings, cnt); |
| for (idx = 0; idx < cnt; idx++) { |
| UChar ubuffer[BUFFER_SIZE]; |
| CFStringRef str = NULL; |
| status = U_ZERO_ERROR; |
| CFIndex ucnt = __cficu_udat_getSymbols(icudf, (UDateFormatSymbolType)icucode, idx + index_base, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, ucnt); |
| } |
| strings[idx] = !str ? (CFStringRef)CFRetain(CFSTR("<error>")) : str; |
| } |
| CFArrayRef array = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)strings, cnt, &kCFTypeArrayCallBacks); |
| while (cnt--) { |
| CFRelease(strings[cnt]); |
| } |
| return array; |
| } |
| |
| CF_PRIVATE CFLocaleRef _CFLocaleCreateCopyWithNewCalendarIdentifier(CFAllocatorRef allocator, CFLocaleRef locale, CFStringRef calendarIdentifier); |
| |
| #define SET_SYMBOLS_ARRAY(A, B, C) \ |
| if (!directToICU) { \ |
| oldProperty = formatter->_property. C; \ |
| formatter->_property. C = NULL; \ |
| } \ |
| __CFDateFormatterSetSymbolsArray(formatter->_df, A, B, value); \ |
| if (!directToICU) { \ |
| formatter->_property. C = __CFDateFormatterCopySymbolsArray(formatter->_df, A, B); \ |
| } |
| |
| static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value, Boolean directToICU) { |
| __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
| __CFGenericValidateType(key, CFStringGetTypeID()); |
| CFTypeRef oldProperty = NULL; |
| UErrorCode status = U_ZERO_ERROR; |
| |
| if (kCFDateFormatterIsLenientKey == key) { |
| if (!directToICU) { |
| oldProperty = formatter->_property. _IsLenient; |
| formatter->_property. _IsLenient = NULL; |
| } |
| __CFGenericValidateType(value, CFBooleanGetTypeID()); |
| if (!directToICU) { |
| formatter->_property. _IsLenient = value ? (CFBooleanRef)CFRetain(value) : NULL; |
| __ResetUDateFormat(formatter, false); |
| } |
| } else if (kCFDateFormatterDoesRelativeDateFormattingKey == key) { |
| if (!directToICU) { |
| oldProperty = formatter->_property. _DoesRelativeDateFormatting; |
| formatter->_property. _DoesRelativeDateFormatting = NULL; |
| } |
| __CFGenericValidateType(value, CFBooleanGetTypeID()); |
| if (!directToICU) { |
| if (kCFBooleanTrue != value) value = kCFBooleanFalse; |
| formatter->_property. _DoesRelativeDateFormatting = value ? (CFBooleanRef)CFRetain(value) : NULL; |
| __ResetUDateFormat(formatter, false); |
| } |
| } else if (kCFDateFormatterCalendarKey == key) { |
| if (!directToICU) { |
| oldProperty = formatter->_property. _Calendar; |
| formatter->_property. _Calendar = NULL; |
| } |
| __CFGenericValidateType(value, CFCalendarGetTypeID()); |
| CFLocaleRef newLocale = _CFLocaleCreateCopyWithNewCalendarIdentifier(CFGetAllocator(formatter->_locale), formatter->_locale, CFCalendarGetIdentifier((CFCalendarRef)value)); |
| |
| if (formatter->_locale) { |
| CFRelease(formatter->_locale); |
| } |
| // at this point, we should be setting the preferences if any into this new locale |
| formatter->_locale = newLocale; |
| if (!directToICU) { |
| formatter->_property. _Calendar = (CFCalendarRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarKey); |
| __ResetUDateFormat(formatter, false); |
| } |
| } else if (kCFDateFormatterCalendarIdentifierKey == key) { |
| if (!directToICU) { |
| oldProperty = formatter->_property. _CalendarName; |
| formatter->_property. _CalendarName = NULL; |
| } |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale); |
| CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeName); |
| CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components); |
| CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifierKey, value); |
| localeName = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents); |
| CFRelease(mcomponents); |
| CFRelease(components); |
| CFLocaleRef newLocale = CFLocaleCreate(CFGetAllocator(formatter->_locale), localeName); |
| // at this point, we should be setting the preferences if any into this new locale |
| CFRelease(localeName); |
| CFRelease(formatter->_locale); |
| formatter->_locale = newLocale; |
| if (!directToICU) { |
| formatter->_property. _CalendarName = (CFStringRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarIdentifierKey); |
| __ResetUDateFormat(formatter, false); |
| } |
| } else if (kCFDateFormatterTimeZoneKey == key) { |
| if (formatter->_property. _TimeZone != value) { |
| if (!directToICU) { |
| oldProperty = formatter->_property. _TimeZone; |
| formatter->_property. _TimeZone = NULL; |
| } |
| __CFGenericValidateType(value, CFTimeZoneGetTypeID()); |
| CFTimeZoneRef old = formatter->_property._TimeZone; |
| formatter->_property._TimeZone = value ? (CFTimeZoneRef)CFRetain(value) : CFTimeZoneCopyDefault(); |
| if (old) CFRelease(old); |
| if (!directToICU) { |
| old = formatter->_property._TimeZone; |
| formatter->_property. _TimeZone = (CFTimeZoneRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterTimeZoneKey); |
| __ResetUDateFormat(formatter, false); |
| if (old) CFRelease(old); |
| } |
| } |
| } else if (kCFDateFormatterDefaultFormatKey == key) { |
| // read-only attribute |
| } else if (kCFDateFormatterTwoDigitStartDateKey == key) { |
| if (!directToICU) { |
| oldProperty = formatter->_property. _TwoDigitStartDate; |
| formatter->_property. _TwoDigitStartDate = NULL; |
| } |
| __CFGenericValidateType(value, CFDateGetTypeID()); |
| if (!directToICU) { |
| formatter->_property. _TwoDigitStartDate = value ? (CFDateRef)CFRetain(value) : NULL; |
| } |
| } else if (kCFDateFormatterDefaultDateKey == key) { |
| if (!directToICU) { |
| oldProperty = formatter->_property. _DefaultDate; |
| formatter->_property. _DefaultDate = NULL; |
| } |
| __CFGenericValidateType(value, CFDateGetTypeID()); |
| if (!directToICU) { |
| formatter->_property._DefaultDate = value ? (CFDateRef)CFRetain(value) : NULL; |
| } |
| } else if (kCFDateFormatterGregorianStartDateKey == key) { |
| if (!directToICU) { |
| oldProperty = formatter->_property. _GregorianStartDate; |
| formatter->_property. _GregorianStartDate = NULL; |
| } |
| __CFGenericValidateType(value, CFDateGetTypeID()); |
| if (!directToICU) { |
| formatter->_property. _GregorianStartDate = value ? (CFDateRef)CFRetain(value) : NULL; |
| __ResetUDateFormat(formatter, false); |
| } |
| } else if (kCFDateFormatterEraSymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_ERAS, 0, _EraSymbols) |
| } else if (kCFDateFormatterLongEraSymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_ERA_NAMES, 0, _LongEraSymbols) |
| } else if (kCFDateFormatterMonthSymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_MONTHS, 0, _MonthSymbols) |
| } else if (kCFDateFormatterShortMonthSymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_SHORT_MONTHS, 0, _ShortMonthSymbols) |
| } else if (kCFDateFormatterVeryShortMonthSymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_NARROW_MONTHS, 0, _VeryShortMonthSymbols) |
| } else if (kCFDateFormatterStandaloneMonthSymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_STANDALONE_MONTHS, 0, _StandaloneMonthSymbols) |
| } else if (kCFDateFormatterShortStandaloneMonthSymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_MONTHS, 0, _ShortStandaloneMonthSymbols) |
| } else if (kCFDateFormatterVeryShortStandaloneMonthSymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_MONTHS, 0, _VeryShortStandaloneMonthSymbols) |
| } else if (kCFDateFormatterWeekdaySymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_WEEKDAYS, 1, _WeekdaySymbols) |
| } else if (kCFDateFormatterShortWeekdaySymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_SHORT_WEEKDAYS, 1, _ShortWeekdaySymbols) |
| } else if (kCFDateFormatterVeryShortWeekdaySymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_NARROW_WEEKDAYS, 1, _VeryShortWeekdaySymbols) |
| } else if (kCFDateFormatterStandaloneWeekdaySymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_STANDALONE_WEEKDAYS, 1, _StandaloneWeekdaySymbols) |
| } else if (kCFDateFormatterShortStandaloneWeekdaySymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_WEEKDAYS, 1, _ShortStandaloneWeekdaySymbols) |
| } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_WEEKDAYS, 1, _VeryShortStandaloneWeekdaySymbols) |
| } else if (kCFDateFormatterQuarterSymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_QUARTERS, 0, _QuarterSymbols) |
| } else if (kCFDateFormatterShortQuarterSymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_SHORT_QUARTERS, 0, _ShortQuarterSymbols) |
| } else if (kCFDateFormatterStandaloneQuarterSymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_STANDALONE_QUARTERS, 0, _StandaloneQuarterSymbols) |
| } else if (kCFDateFormatterShortStandaloneQuarterSymbolsKey == key) { |
| SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_QUARTERS, 0, _ShortStandaloneQuarterSymbols) |
| } else if (kCFDateFormatterAMSymbolKey == key) { |
| if (!directToICU) { |
| oldProperty = formatter->_property. _AMSymbol; |
| formatter->_property. _AMSymbol = NULL; |
| } |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| CFIndex item_cnt = CFStringGetLength((CFStringRef)value); |
| STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); |
| UChar *item_ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)value); |
| if (NULL == item_ustr) { |
| item_cnt = __CFMin(BUFFER_SIZE, item_cnt); |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); |
| item_ustr = item_buffer; |
| } |
| __cficu_udat_setSymbols(formatter->_df, UDAT_AM_PMS, 0, item_ustr, item_cnt, &status); |
| if (!directToICU) { |
| formatter->_property. _AMSymbol = value ? (CFStringRef)CFStringCreateCopy(NULL, value) : NULL; |
| } |
| } else if (kCFDateFormatterPMSymbolKey == key) { |
| if (!directToICU) { |
| oldProperty = formatter->_property. _PMSymbol; |
| formatter->_property. _PMSymbol = NULL; |
| } |
| __CFGenericValidateType(value, CFStringGetTypeID()); |
| CFIndex item_cnt = CFStringGetLength((CFStringRef)value); |
| STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt)); |
| UChar *item_ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)value); |
| if (NULL == item_ustr) { |
| item_cnt = __CFMin(BUFFER_SIZE, item_cnt); |
| CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, item_cnt), (UniChar *)item_buffer); |
| item_ustr = item_buffer; |
| } |
| __cficu_udat_setSymbols(formatter->_df, UDAT_AM_PMS, 1, item_ustr, item_cnt, &status); |
| if (!directToICU) { |
| formatter->_property. _PMSymbol = value ? (CFStringRef)CFStringCreateCopy(NULL, value) : NULL; |
| } |
| } else if (kCFDateFormatterAmbiguousYearStrategyKey == key) { |
| oldProperty = formatter->_property._AmbiguousYearStrategy; |
| formatter->_property._AmbiguousYearStrategy = NULL; |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| formatter->_property._AmbiguousYearStrategy = (CFNumberRef)CFRetain(value); |
| } else if (kCFDateFormatterUsesCharacterDirectionKey == key) { |
| __CFGenericValidateType(value, CFBooleanGetTypeID()); |
| oldProperty = formatter->_property._UsesCharacterDirection; |
| formatter->_property._UsesCharacterDirection = (CFBooleanRef)CFRetain(value); |
| } else if (CFEqual(key, kCFDateFormatterFormattingContextKey)) { |
| if (!directToICU) { |
| oldProperty = formatter->_property. _FormattingContext; |
| formatter->_property._FormattingContext = NULL; |
| } |
| __CFGenericValidateType(value, CFNumberGetTypeID()); |
| int context = 0; |
| CFNumberGetValue(value, kCFNumberIntType, &context); |
| __cficu_udat_setContext(formatter->_df, context, &status); |
| if (!directToICU) { |
| formatter->_property._FormattingContext = (CFNumberRef)CFRetain(value); |
| } |
| } else { |
| CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key); |
| } |
| if (oldProperty) CFRelease(oldProperty); |
| } |
| |
| void CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value) { |
| __CFDateFormatterSetProperty(formatter, key, value, false); |
| } |
| |
| CFTypeRef CFDateFormatterCopyProperty(CFDateFormatterRef formatter, CFStringRef key) { |
| __CFGenericValidateType(formatter, CFDateFormatterGetTypeID()); |
| __CFGenericValidateType(key, CFStringGetTypeID()); |
| UErrorCode status = U_ZERO_ERROR; |
| UChar ubuffer[BUFFER_SIZE]; |
| |
| if (kCFDateFormatterIsLenientKey == key) { |
| if (formatter->_property._IsLenient) return CFRetain(formatter->_property._IsLenient); |
| return CFRetain(__cficu_udat_isLenient(formatter->_df) ? kCFBooleanTrue : kCFBooleanFalse); |
| } else if (kCFDateFormatterDoesRelativeDateFormattingKey == key) { |
| if (formatter->_property._DoesRelativeDateFormatting) return CFRetain(formatter->_property._DoesRelativeDateFormatting); |
| return CFRetain(kCFBooleanFalse); |
| } else if (kCFDateFormatterCalendarKey == key) { |
| if (formatter->_property._Calendar) return CFRetain(formatter->_property._Calendar); |
| CFCalendarRef calendar = (CFCalendarRef)CFLocaleGetValue(formatter->_locale, kCFLocaleCalendarKey); |
| return calendar ? CFRetain(calendar) : NULL; |
| } else if (kCFDateFormatterCalendarIdentifierKey == key) { |
| if (formatter->_property._CalendarName) return CFRetain(formatter->_property._CalendarName); |
| CFStringRef ident = (CFStringRef)CFLocaleGetValue(formatter->_locale, kCFLocaleCalendarIdentifierKey); |
| return ident ? CFRetain(ident) : NULL; |
| } else if (kCFDateFormatterTimeZoneKey == key) { |
| return formatter->_property._TimeZone ? CFRetain(formatter->_property._TimeZone) : NULL; |
| } else if (kCFDateFormatterDefaultFormatKey == key) { |
| return formatter->_defformat ? CFRetain(formatter->_defformat) : NULL; |
| } else if (kCFDateFormatterTwoDigitStartDateKey == key) { |
| return formatter->_property._TwoDigitStartDate ? CFRetain(formatter->_property._TwoDigitStartDate) : NULL; |
| } else if (kCFDateFormatterDefaultDateKey == key) { |
| return formatter->_property._DefaultDate ? CFRetain(formatter->_property._DefaultDate) : NULL; |
| } else if (kCFDateFormatterGregorianStartDateKey == key) { |
| if (formatter->_property._GregorianStartDate) return CFRetain(formatter->_property._GregorianStartDate); |
| const UCalendar *cal = __cficu_udat_getCalendar(formatter->_df); |
| UDate udate = __cficu_ucal_getGregorianChange(cal, &status); |
| if (U_SUCCESS(status)) { |
| CFAbsoluteTime at = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970; |
| return CFDateCreate(CFGetAllocator(formatter), at); |
| } |
| } else if (kCFDateFormatterEraSymbolsKey == key) { |
| if (formatter->_property._EraSymbols) return CFRetain(formatter->_property._EraSymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_ERAS, 0); |
| } else if (kCFDateFormatterLongEraSymbolsKey == key) { |
| if (formatter->_property._LongEraSymbols) return CFRetain(formatter->_property._LongEraSymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_ERA_NAMES, 0); |
| } else if (kCFDateFormatterMonthSymbolsKey == key) { |
| if (formatter->_property._MonthSymbols) return CFRetain(formatter->_property._MonthSymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_MONTHS, 0); |
| } else if (kCFDateFormatterShortMonthSymbolsKey == key) { |
| if (formatter->_property._ShortMonthSymbols) return CFRetain(formatter->_property._ShortMonthSymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_SHORT_MONTHS, 0); |
| } else if (kCFDateFormatterVeryShortMonthSymbolsKey == key) { |
| if (formatter->_property._VeryShortMonthSymbols) return CFRetain(formatter->_property._VeryShortMonthSymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_NARROW_MONTHS, 0); |
| } else if (kCFDateFormatterStandaloneMonthSymbolsKey == key) { |
| if (formatter->_property._StandaloneMonthSymbols) return CFRetain(formatter->_property._StandaloneMonthSymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_STANDALONE_MONTHS, 0); |
| } else if (kCFDateFormatterShortStandaloneMonthSymbolsKey == key) { |
| if (formatter->_property._ShortStandaloneMonthSymbols) return CFRetain(formatter->_property._ShortStandaloneMonthSymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_STANDALONE_SHORT_MONTHS, 0); |
| } else if (kCFDateFormatterVeryShortStandaloneMonthSymbolsKey == key) { |
| if (formatter->_property._VeryShortStandaloneMonthSymbols) return CFRetain(formatter->_property._VeryShortStandaloneMonthSymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_STANDALONE_NARROW_MONTHS, 0); |
| } else if (kCFDateFormatterWeekdaySymbolsKey == key) { |
| if (formatter->_property._WeekdaySymbols) return CFRetain(formatter->_property._WeekdaySymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_WEEKDAYS, 1); |
| } else if (kCFDateFormatterShortWeekdaySymbolsKey == key) { |
| if (formatter->_property._ShortWeekdaySymbols) return CFRetain(formatter->_property._ShortWeekdaySymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_SHORT_WEEKDAYS, 1); |
| } else if (kCFDateFormatterVeryShortWeekdaySymbolsKey == key) { |
| if (formatter->_property._VeryShortWeekdaySymbols) return CFRetain(formatter->_property._VeryShortWeekdaySymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_NARROW_WEEKDAYS, 1); |
| } else if (kCFDateFormatterStandaloneWeekdaySymbolsKey == key) { |
| if (formatter->_property._StandaloneWeekdaySymbols) return CFRetain(formatter->_property._StandaloneWeekdaySymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_STANDALONE_WEEKDAYS, 1); |
| } else if (kCFDateFormatterShortStandaloneWeekdaySymbolsKey == key) { |
| if (formatter->_property._ShortStandaloneWeekdaySymbols) return CFRetain(formatter->_property._ShortStandaloneWeekdaySymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_STANDALONE_SHORT_WEEKDAYS, 1); |
| } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey == key) { |
| if (formatter->_property._VeryShortStandaloneWeekdaySymbols) return CFRetain(formatter->_property._VeryShortStandaloneWeekdaySymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_STANDALONE_NARROW_WEEKDAYS, 1); |
| } else if (kCFDateFormatterQuarterSymbolsKey == key) { |
| if (formatter->_property._QuarterSymbols) return CFRetain(formatter->_property._QuarterSymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_QUARTERS, 0); |
| } else if (kCFDateFormatterShortQuarterSymbolsKey == key) { |
| if (formatter->_property._ShortQuarterSymbols) return CFRetain(formatter->_property._ShortQuarterSymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_SHORT_QUARTERS, 0); |
| } else if (kCFDateFormatterStandaloneQuarterSymbolsKey == key) { |
| if (formatter->_property._StandaloneQuarterSymbols) return CFRetain(formatter->_property._StandaloneQuarterSymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_STANDALONE_QUARTERS, 0); |
| } else if (kCFDateFormatterShortStandaloneQuarterSymbolsKey == key) { |
| if (formatter->_property._ShortStandaloneQuarterSymbols) return CFRetain(formatter->_property._ShortStandaloneQuarterSymbols); |
| return __CFDateFormatterCopySymbolsArray(formatter->_df, UDAT_STANDALONE_SHORT_QUARTERS, 0); |
| } else if (kCFDateFormatterAMSymbolKey == key) { |
| if (formatter->_property._AMSymbol) return CFRetain(formatter->_property._AMSymbol); |
| CFIndex cnt = __cficu_udat_countSymbols(formatter->_df, UDAT_AM_PMS); |
| if (2 <= cnt) { |
| CFIndex ucnt = __cficu_udat_getSymbols(formatter->_df, UDAT_AM_PMS, 0, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (UniChar *)ubuffer, ucnt); |
| } |
| } |
| } else if (kCFDateFormatterPMSymbolKey == key) { |
| if (formatter->_property._PMSymbol) return CFRetain(formatter->_property._PMSymbol); |
| CFIndex cnt = __cficu_udat_countSymbols(formatter->_df, UDAT_AM_PMS); |
| if (2 <= cnt) { |
| CFIndex ucnt = __cficu_udat_getSymbols(formatter->_df, UDAT_AM_PMS, 1, ubuffer, BUFFER_SIZE, &status); |
| if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) { |
| return CFStringCreateWithCharacters(CFGetAllocator(formatter), (UniChar *)ubuffer, ucnt); |
| } |
| } |
| } else if (kCFDateFormatterAmbiguousYearStrategyKey == key) { |
| if (formatter->_property._AmbiguousYearStrategy) return CFRetain(formatter->_property._AmbiguousYearStrategy); |
| } else if (kCFDateFormatterUsesCharacterDirectionKey == key) { |
| return formatter->_property._UsesCharacterDirection ? CFRetain(formatter->_property._UsesCharacterDirection) : CFRetain(kCFBooleanFalse); |
| } else if (CFEqual(key, kCFDateFormatterFormattingContextKey)) { |
| if (formatter->_property._FormattingContext) return CFRetain(formatter->_property._FormattingContext); |
| int value = __cficu_udat_getContext(formatter->_df, UDISPCTX_TYPE_CAPITALIZATION, &status); |
| return CFNumberCreate(CFGetAllocator(formatter), kCFNumberIntType, (const void *)&value); |
| } else { |
| CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key); |
| } |
| return NULL; |
| } |
| |
| |