/ CFDateFormatter.c
CFDateFormatter.c
   1  /*
   2   * Copyright (c) 2015 Apple Inc. All rights reserved.
   3   *
   4   * @APPLE_LICENSE_HEADER_START@
   5   *
   6   * This file contains Original Code and/or Modifications of Original Code
   7   * as defined in and that are subject to the Apple Public Source License
   8   * Version 2.0 (the 'License'). You may not use this file except in
   9   * compliance with the License. Please obtain a copy of the License at
  10   * http://www.opensource.apple.com/apsl/ and read it before using this
  11   * file.
  12   *
  13   * The Original Code and all software distributed under the License are
  14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  18   * Please see the License for the specific language governing rights and
  19   * limitations under the License.
  20   *
  21   * @APPLE_LICENSE_HEADER_END@
  22   */
  23  
  24  /*        CFDateFormatter.c
  25          Copyright (c) 2002-2014, Apple Inc. All rights reserved.
  26          Responsibility: David Smith
  27  */
  28  
  29  #define U_SHOW_INTERNAL_API 1
  30  
  31  #include <CoreFoundation/CFDateFormatter.h>
  32  #include <CoreFoundation/CFDate.h>
  33  #include <CoreFoundation/CFTimeZone.h>
  34  #include <CoreFoundation/CFCalendar.h>
  35  #include <CoreFoundation/CFNumber.h>
  36  #include "CFPriv.h"
  37  #include "CFInternal.h"
  38  #include "CFLocaleInternal.h"
  39  #include "CFICULogging.h"
  40  #include <math.h>
  41  #include <float.h>
  42  
  43  
  44  typedef CF_ENUM(CFIndex, CFDateFormatterAmbiguousYearHandling) {
  45      kCFDateFormatterAmbiguousYearFailToParse = 0, // fail the parse; the default formatter behavior
  46      kCFDateFormatterAmbiguousYearAssumeToNone = 1, // default to assuming era 1, or the year 0-99
  47      kCFDateFormatterAmbiguousYearAssumeToCurrent = 2, // default to assuming the current century or era
  48      kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate = 3,
  49      kCFDateFormatterAmbiguousYearAssumeToFuture = 4,
  50      kCFDateFormatterAmbiguousYearAssumeToPast = 5,
  51      kCFDateFormatterAmbiguousYearAssumeToLikelyFuture = 6,
  52      kCFDateFormatterAmbiguousYearAssumeToLikelyPast = 7
  53  };
  54  
  55  extern UCalendar *__CFCalendarCreateUCalendar(CFStringRef calendarID, CFStringRef localeID, CFTimeZoneRef tz);
  56  
  57  static CONST_STRING_DECL(kCFDateFormatterFormattingContextKey, "kCFDateFormatterFormattingContextKey");
  58  
  59  CF_EXPORT const CFStringRef kCFDateFormatterCalendarIdentifierKey;
  60  
  61  #undef CFReleaseIfNotNull
  62  #define CFReleaseIfNotNull(X) if (X) CFRelease(X)
  63  
  64  #define BUFFER_SIZE 768
  65  
  66  static CFStringRef __CFDateFormatterCreateForcedTemplate(CFLocaleRef locale, CFStringRef inString, Boolean stripAMPM);
  67  
  68  // If you pass in a string in tmplate, you get back NULL (failure) or a CFStringRef.
  69  // 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.
  70  
  71  CFArrayRef CFDateFormatterCreateDateFormatsFromTemplates(CFAllocatorRef allocator, CFArrayRef tmplates, CFOptionFlags options, CFLocaleRef locale) {
  72      return (CFArrayRef)CFDateFormatterCreateDateFormatFromTemplate(allocator, (CFStringRef)tmplates, options, locale);
  73  }
  74  
  75  static Boolean useTemplatePatternGenerator(CFLocaleRef locale, void(^work)(UDateTimePatternGenerator *ptg)) {
  76      static UDateTimePatternGenerator *ptg;
  77      static pthread_mutex_t ptgLock = PTHREAD_MUTEX_INITIALIZER;
  78      static const char *ptgLocaleName;
  79      CFStringRef ln = locale ? CFLocaleGetIdentifier(locale) : CFSTR("");
  80      char buffer[BUFFER_SIZE];
  81      const char *localeName = CFStringGetCStringPtr(ln, kCFStringEncodingASCII);
  82      if (NULL == localeName) {
  83          if (CFStringGetCString(ln, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) localeName = buffer;
  84      }
  85      
  86      static void (^flushCache)() = ^{
  87          __cficu_udatpg_close(ptg);
  88          ptg = NULL;
  89          free((void *)ptgLocaleName);
  90          ptgLocaleName = NULL;
  91      };
  92      pthread_mutex_lock(&ptgLock);
  93      if (ptgLocaleName && strcmp(ptgLocaleName, localeName) != 0) {
  94          flushCache();
  95      }
  96      UErrorCode status = U_ZERO_ERROR;
  97      if (!ptg) {
  98          ptg = __cficu_udatpg_open(localeName, &status);
  99          if (ptg && !U_FAILURE(status)) {
 100              ptgLocaleName = strdup(localeName);
 101          }
 102      }
 103      Boolean result = (NULL != ptg && !U_FAILURE(status));
 104      if (result && work) {
 105          work(ptg);
 106      }
 107      pthread_mutex_unlock(&ptgLock);
 108      return result;
 109  }
 110  
 111  /*
 112   1) Scan the string for an AM/PM indicator
 113   2) Back up past any spaces in front of the AM/PM indicator
 114   3) As long as the current character is whitespace, or an 'a', remove it and shift everything past it down
 115   */
 116  static void _CFDateFormatterStripAMPMIndicators(UniChar **bpat, int32_t *bpatlen, CFIndex bufferSize) {
 117  
 118      //scan
 119      for (CFIndex idx = 0; idx < *bpatlen; idx++) {
 120          if ((*bpat)[idx] == 'a') {
 121              
 122              //back up
 123              while ((*bpat)[idx - 1] == ' ') {
 124                  idx--;
 125              }
 126              
 127              //shift
 128              for (; (*bpat)[idx] == ' ' || (*bpat)[idx] == 'a'; idx++) {
 129                  for (CFIndex shiftIdx = idx; shiftIdx < *bpatlen && shiftIdx + 1 < bufferSize; shiftIdx++) {
 130                      (*bpat)[shiftIdx] = (*bpat)[shiftIdx + 1];
 131                  }
 132                  //compensate for the character we just removed
 133                  (*bpatlen)--;
 134                  idx--;
 135              }
 136          }
 137      }
 138  }
 139  
 140  CFStringRef CFDateFormatterCreateDateFormatFromTemplate(CFAllocatorRef allocator, CFStringRef tmplate, CFOptionFlags options, CFLocaleRef locale) {
 141      if (allocator) __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
 142      if (locale) __CFGenericValidateType(locale, CFLocaleGetTypeID());
 143      Boolean tmplateIsString = (CFStringGetTypeID() == CFGetTypeID(tmplate));
 144      if (!tmplateIsString) {
 145          __CFGenericValidateType(tmplate, CFArrayGetTypeID());
 146      }
 147      
 148      __block CFTypeRef result = tmplateIsString ? NULL : (CFTypeRef)CFArrayCreateMutable(allocator, 0, &kCFTypeArrayCallBacks);
 149  
 150      Boolean success = useTemplatePatternGenerator(locale, ^(UDateTimePatternGenerator *ptg) {
 151          
 152          
 153          for (CFIndex idx = 0, cnt = tmplateIsString ? 1 : CFArrayGetCount((CFArrayRef)tmplate); idx < cnt; idx++) {
 154              CFStringRef tmplateString = tmplateIsString ? (CFStringRef)tmplate : (CFStringRef)CFArrayGetValueAtIndex((CFArrayRef)tmplate, idx);
 155              CFStringRef resultString = NULL;
 156              
 157              Boolean stripAMPM = CFStringFind(tmplateString, CFSTR("J"), 0).location != kCFNotFound;
 158              tmplateString = __CFDateFormatterCreateForcedTemplate(locale ? locale : CFLocaleGetSystem(), tmplateString, stripAMPM);
 159              
 160              CFIndex jCount = 0; // the only interesting cases are 0, 1, and 2 (adjacent)
 161              UniChar adjacentJs[2] = {-1, -1};
 162              CFRange r = CFStringFind(tmplateString, CFSTR("j"), kCFCompareCaseInsensitive);
 163              if (kCFNotFound != r.location) {
 164                  adjacentJs[0] = CFStringGetCharacterAtIndex(tmplateString, r.location);
 165                  jCount++;
 166                  if ((r.location + 1 < CFStringGetLength(tmplateString)) && ('j' == CFStringGetCharacterAtIndex(tmplateString, r.location + 1) || 'J' == CFStringGetCharacterAtIndex(tmplateString, r.location + 1))) {
 167                      jCount++;
 168                      adjacentJs[1] = CFStringGetCharacterAtIndex(tmplateString, r.location + 1);
 169                  }
 170              }
 171              
 172              UChar pattern[BUFFER_SIZE] = {0}, skel[BUFFER_SIZE] = {0}, bpat[BUFFER_SIZE] = {0};
 173              CFIndex tmpltLen = CFStringGetLength(tmplateString);
 174              if (BUFFER_SIZE < tmpltLen) tmpltLen = BUFFER_SIZE;
 175              CFStringGetCharacters(tmplateString, CFRangeMake(0, tmpltLen), (UniChar *)pattern);
 176              CFRelease(tmplateString);
 177              
 178              int32_t patlen = tmpltLen;
 179              UErrorCode status = U_ZERO_ERROR;
 180              int32_t skellen = __cficu_udatpg_getSkeleton(ptg, pattern, patlen, skel, sizeof(skel) / sizeof(skel[0]), &status);
 181              if (!U_FAILURE(status)) {
 182                  if ((0 < jCount) && (skellen + jCount < (sizeof(skel) / sizeof(skel[0])))) {
 183                      
 184                      skel[skellen++] = 'j'; //adjacentJs[0];
 185                      if (1 < jCount) skel[skellen++] = 'j'; //adjacentJs[1];
 186                      //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
 187                  }
 188                  
 189                  status = U_ZERO_ERROR;
 190                  int32_t bpatlen = __cficu_udatpg_getBestPattern(ptg, skel, skellen, bpat, sizeof(bpat) / sizeof(bpat[0]), &status);
 191                  if (!U_FAILURE(status)) {
 192                      if (stripAMPM) {
 193                          UniChar *bpatptr = (UniChar *)bpat;
 194                          _CFDateFormatterStripAMPMIndicators(&bpatptr, &bpatlen, BUFFER_SIZE);
 195                      }
 196                      resultString = CFStringCreateWithCharacters(allocator, (const UniChar *)bpat, bpatlen);
 197                  }
 198              }
 199              
 200              if (tmplateIsString) {
 201                  result = (CFTypeRef)resultString;
 202              } else {
 203                  CFArrayAppendValue((CFMutableArrayRef)result, resultString ? (CFTypeRef)resultString : (CFTypeRef)kCFNull);
 204                  if (resultString) CFRelease(resultString);
 205              }
 206          }
 207      });
 208      
 209      if (!success) {
 210          if (result) CFRelease(result);
 211          result = NULL;
 212      }
 213      
 214      return (CFStringRef)result;
 215  }
 216  
 217  struct __CFDateFormatter {
 218      CFRuntimeBase _base;
 219      UDateFormat *_df;
 220      CFLocaleRef _locale;
 221      CFDateFormatterStyle _timeStyle;
 222      CFDateFormatterStyle _dateStyle;
 223      CFStringRef _format;
 224      CFStringRef _defformat;
 225      struct {
 226          CFBooleanRef _IsLenient;
 227  	CFBooleanRef _DoesRelativeDateFormatting;
 228  	CFBooleanRef _HasCustomFormat;
 229          CFTimeZoneRef _TimeZone;
 230          CFCalendarRef _Calendar;
 231          CFStringRef _CalendarName;
 232          CFDateRef _TwoDigitStartDate;
 233          CFDateRef _DefaultDate;
 234          CFDateRef _GregorianStartDate;
 235          CFArrayRef _EraSymbols;
 236          CFArrayRef _LongEraSymbols;
 237          CFArrayRef _MonthSymbols;
 238          CFArrayRef _ShortMonthSymbols;
 239          CFArrayRef _VeryShortMonthSymbols;
 240          CFArrayRef _StandaloneMonthSymbols;
 241          CFArrayRef _ShortStandaloneMonthSymbols;
 242          CFArrayRef _VeryShortStandaloneMonthSymbols;
 243          CFArrayRef _WeekdaySymbols;
 244          CFArrayRef _ShortWeekdaySymbols;
 245          CFArrayRef _VeryShortWeekdaySymbols;
 246          CFArrayRef _StandaloneWeekdaySymbols;
 247          CFArrayRef _ShortStandaloneWeekdaySymbols;
 248          CFArrayRef _VeryShortStandaloneWeekdaySymbols;
 249          CFArrayRef _QuarterSymbols;
 250          CFArrayRef _ShortQuarterSymbols;
 251          CFArrayRef _StandaloneQuarterSymbols;
 252          CFArrayRef _ShortStandaloneQuarterSymbols;
 253          CFStringRef _AMSymbol;
 254          CFStringRef _PMSymbol;
 255          CFNumberRef _AmbiguousYearStrategy;
 256          CFBooleanRef _UsesCharacterDirection;
 257          CFNumberRef _FormattingContext;
 258          
 259          // the following are from preferences
 260          CFArrayRef _CustomEraSymbols;
 261          CFArrayRef _CustomLongEraSymbols;
 262          CFArrayRef _CustomMonthSymbols;
 263          CFArrayRef _CustomShortMonthSymbols;
 264          CFArrayRef _CustomVeryShortMonthSymbols;
 265          CFArrayRef _CustomStandaloneMonthSymbols;
 266          CFArrayRef _CustomShortStandaloneMonthSymbols;
 267          CFArrayRef _CustomVeryShortStandaloneMonthSymbols;
 268          CFArrayRef _CustomWeekdaySymbols;
 269          CFArrayRef _CustomShortWeekdaySymbols;
 270          CFArrayRef _CustomVeryShortWeekdaySymbols;
 271          CFArrayRef _CustomStandaloneWeekdaySymbols;
 272          CFArrayRef _CustomShortStandaloneWeekdaySymbols;
 273          CFArrayRef _CustomVeryShortStandaloneWeekdaySymbols;
 274          CFArrayRef _CustomQuarterSymbols;
 275          CFArrayRef _CustomShortQuarterSymbols;
 276          CFArrayRef _CustomStandaloneQuarterSymbols;
 277          CFArrayRef _CustomShortStandaloneQuarterSymbols;
 278          CFStringRef _CustomDateFormat;
 279          CFStringRef _CustomTimeFormat;
 280          CFBooleanRef _Custom24Hour;
 281          CFBooleanRef _Custom12Hour;
 282          CFStringRef _CustomAMSymbol;
 283          CFStringRef _CustomPMSymbol;
 284          CFDictionaryRef _CustomFirstWeekday;
 285          CFDictionaryRef _CustomMinDaysInFirstWeek;
 286          
 287      } _property;
 288  };
 289  
 290  static CFStringRef __CFDateFormatterCopyDescription(CFTypeRef cf) {
 291      CFDateFormatterRef formatter = (CFDateFormatterRef)cf;
 292      return CFStringCreateWithFormat(CFGetAllocator(formatter), NULL, CFSTR("<CFDateFormatter %p [%p]>"), cf, CFGetAllocator(formatter));
 293  }
 294  
 295  static void __CFDateFormatterDeallocate(CFTypeRef cf) {
 296      CFDateFormatterRef formatter = (CFDateFormatterRef)cf;
 297      if (formatter->_df) __cficu_udat_close(formatter->_df);
 298      if (formatter->_locale) CFRelease(formatter->_locale);
 299      if (formatter->_format) CFRelease(formatter->_format);
 300      if (formatter->_defformat) CFRelease(formatter->_defformat);
 301      CFReleaseIfNotNull(formatter->_property._IsLenient);
 302      CFReleaseIfNotNull(formatter->_property._DoesRelativeDateFormatting);
 303      CFReleaseIfNotNull(formatter->_property._TimeZone);
 304      CFReleaseIfNotNull(formatter->_property._Calendar);
 305      CFReleaseIfNotNull(formatter->_property._CalendarName);
 306      CFReleaseIfNotNull(formatter->_property._TwoDigitStartDate);
 307      CFReleaseIfNotNull(formatter->_property._DefaultDate);
 308      CFReleaseIfNotNull(formatter->_property._GregorianStartDate);
 309      CFReleaseIfNotNull(formatter->_property._EraSymbols);
 310      CFReleaseIfNotNull(formatter->_property._LongEraSymbols);
 311      CFReleaseIfNotNull(formatter->_property._MonthSymbols);
 312      CFReleaseIfNotNull(formatter->_property._ShortMonthSymbols);
 313      CFReleaseIfNotNull(formatter->_property._VeryShortMonthSymbols);
 314      CFReleaseIfNotNull(formatter->_property._StandaloneMonthSymbols);
 315      CFReleaseIfNotNull(formatter->_property._ShortStandaloneMonthSymbols);
 316      CFReleaseIfNotNull(formatter->_property._VeryShortStandaloneMonthSymbols);
 317      CFReleaseIfNotNull(formatter->_property._WeekdaySymbols);
 318      CFReleaseIfNotNull(formatter->_property._ShortWeekdaySymbols);
 319      CFReleaseIfNotNull(formatter->_property._VeryShortWeekdaySymbols);
 320      CFReleaseIfNotNull(formatter->_property._StandaloneWeekdaySymbols);
 321      CFReleaseIfNotNull(formatter->_property._ShortStandaloneWeekdaySymbols);
 322      CFReleaseIfNotNull(formatter->_property._VeryShortStandaloneWeekdaySymbols);
 323      CFReleaseIfNotNull(formatter->_property._QuarterSymbols);
 324      CFReleaseIfNotNull(formatter->_property._ShortQuarterSymbols);
 325      CFReleaseIfNotNull(formatter->_property._StandaloneQuarterSymbols);
 326      CFReleaseIfNotNull(formatter->_property._ShortStandaloneQuarterSymbols);
 327      CFReleaseIfNotNull(formatter->_property._AMSymbol);
 328      CFReleaseIfNotNull(formatter->_property._PMSymbol);
 329      CFReleaseIfNotNull(formatter->_property._AmbiguousYearStrategy);
 330      CFReleaseIfNotNull(formatter->_property._UsesCharacterDirection);
 331      CFReleaseIfNotNull(formatter->_property._FormattingContext);
 332      CFReleaseIfNotNull(formatter->_property._CustomEraSymbols);
 333      CFReleaseIfNotNull(formatter->_property._CustomMonthSymbols);
 334      CFReleaseIfNotNull(formatter->_property._CustomShortMonthSymbols);
 335      CFReleaseIfNotNull(formatter->_property._CustomWeekdaySymbols);
 336      CFReleaseIfNotNull(formatter->_property._CustomShortWeekdaySymbols);
 337      CFReleaseIfNotNull(formatter->_property._CustomLongEraSymbols);
 338      CFReleaseIfNotNull(formatter->_property._CustomVeryShortMonthSymbols);
 339      CFReleaseIfNotNull(formatter->_property._CustomVeryShortWeekdaySymbols);
 340      CFReleaseIfNotNull(formatter->_property._CustomStandaloneMonthSymbols);
 341      CFReleaseIfNotNull(formatter->_property._CustomShortStandaloneMonthSymbols);
 342      CFReleaseIfNotNull(formatter->_property._CustomVeryShortStandaloneMonthSymbols);
 343      CFReleaseIfNotNull(formatter->_property._CustomStandaloneWeekdaySymbols);
 344      CFReleaseIfNotNull(formatter->_property._CustomShortStandaloneWeekdaySymbols);
 345      CFReleaseIfNotNull(formatter->_property._CustomVeryShortStandaloneWeekdaySymbols);
 346      CFReleaseIfNotNull(formatter->_property._CustomQuarterSymbols);
 347      CFReleaseIfNotNull(formatter->_property._CustomShortQuarterSymbols);
 348      CFReleaseIfNotNull(formatter->_property._CustomShortStandaloneQuarterSymbols);
 349      CFReleaseIfNotNull(formatter->_property._CustomDateFormat);
 350      CFReleaseIfNotNull(formatter->_property._CustomTimeFormat);
 351      CFReleaseIfNotNull(formatter->_property._Custom24Hour);
 352      CFReleaseIfNotNull(formatter->_property._Custom12Hour);
 353      CFReleaseIfNotNull(formatter->_property._CustomAMSymbol);
 354      CFReleaseIfNotNull(formatter->_property._CustomPMSymbol);
 355      CFReleaseIfNotNull(formatter->_property._CustomFirstWeekday);
 356      CFReleaseIfNotNull(formatter->_property._CustomMinDaysInFirstWeek);
 357  }
 358  
 359  static CFStringRef __CFDateFormatterCreateForcedString(CFDateFormatterRef formatter, CFStringRef inString);
 360  
 361  static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value, Boolean directToICU);
 362  static void __CFDateFormatterStoreSymbolPrefs(const void *key, const void *value, void *context);
 363  extern CFDictionaryRef __CFLocaleGetPrefs(CFLocaleRef locale);
 364  static void __substituteFormatStringFromPrefsDFRelative(CFDateFormatterRef formatter);
 365  static void __substituteFormatStringFromPrefsDF(CFDateFormatterRef formatter, bool doTime);
 366  static void __CFDateFormatterSetSymbolsArray(UDateFormat *icudf, int32_t icucode, int index_base, CFTypeRef value);
 367      
 368  static void __ReadCustomUDateFormatProperty(CFDateFormatterRef formatter) {
 369      CFDictionaryRef prefs = __CFLocaleGetPrefs(formatter->_locale);
 370      CFPropertyListRef metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUDateTimeSymbols")) : NULL;
 371      if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
 372          CFDictionaryApplyFunction((CFDictionaryRef)metapref, __CFDateFormatterStoreSymbolPrefs, formatter);
 373      }
 374      metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleFirstWeekday")) : NULL;
 375      if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
 376          formatter->_property._CustomFirstWeekday = (CFDictionaryRef)CFRetain(metapref);
 377      }
 378      metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleMinDaysInFirstWeek")) : NULL;
 379      if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
 380          formatter->_property._CustomMinDaysInFirstWeek = (CFDictionaryRef)CFRetain(metapref);
 381      }
 382      metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce24HourTime")) : NULL;
 383      if (NULL != metapref && CFGetTypeID(metapref) == CFBooleanGetTypeID()) {
 384          formatter->_property._Custom24Hour = (CFBooleanRef)CFRetain(metapref);
 385      }
 386      metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce12HourTime")) : NULL;
 387      if (NULL != metapref && CFGetTypeID(metapref) == CFBooleanGetTypeID()) {
 388          formatter->_property._Custom12Hour = (CFBooleanRef)CFRetain(metapref);
 389      }
 390      metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUDateFormatStrings")) : NULL;
 391      if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
 392          CFStringRef key;
 393          switch (formatter->_dateStyle) {
 394              case kCFDateFormatterShortStyle: key = CFSTR("1"); break;
 395              case kCFDateFormatterMediumStyle: key = CFSTR("2"); break;
 396              case kCFDateFormatterLongStyle: key = CFSTR("3"); break;
 397              case kCFDateFormatterFullStyle: key = CFSTR("4"); break;
 398              default: key = CFSTR("0"); break;
 399          }
 400          CFStringRef dateFormat = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key);
 401          if (NULL != dateFormat && CFGetTypeID(dateFormat) == CFStringGetTypeID()) {
 402              formatter->_property._CustomDateFormat = (CFStringRef)CFRetain(dateFormat);
 403          }
 404      }
 405      metapref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUTimeFormatStrings")) : NULL;
 406      if (NULL != metapref && CFGetTypeID(metapref) == CFDictionaryGetTypeID()) {
 407          CFStringRef key;
 408          switch (formatter->_timeStyle) {
 409              case kCFDateFormatterShortStyle: key = CFSTR("1"); break;
 410              case kCFDateFormatterMediumStyle: key = CFSTR("2"); break;
 411              case kCFDateFormatterLongStyle: key = CFSTR("3"); break;
 412              case kCFDateFormatterFullStyle: key = CFSTR("4"); break;
 413              default: key = CFSTR("0"); break;
 414          }
 415          CFStringRef timeFormat = (CFStringRef)CFDictionaryGetValue((CFDictionaryRef)metapref, key);
 416          if (NULL != timeFormat && CFGetTypeID(timeFormat) == CFStringGetTypeID()) {
 417              formatter->_property._CustomTimeFormat = (CFStringRef)CFRetain(timeFormat);
 418          }
 419      }
 420  }
 421      
 422  static void __ApplyUDateFormatSymbol(CFDateFormatterRef formatter) {
 423      UDateFormatSymbolType types[18] = {UDAT_ERAS,
 424                                       UDAT_ERA_NAMES,
 425                                       UDAT_MONTHS,
 426                                       UDAT_SHORT_MONTHS,
 427                                       UDAT_NARROW_MONTHS,
 428                                       UDAT_STANDALONE_MONTHS,
 429                                       UDAT_STANDALONE_SHORT_MONTHS,
 430                                       UDAT_STANDALONE_NARROW_MONTHS,
 431                                       UDAT_WEEKDAYS,
 432                                       UDAT_SHORT_WEEKDAYS,
 433                                       UDAT_NARROW_WEEKDAYS,
 434                                       UDAT_STANDALONE_WEEKDAYS,
 435                                       UDAT_STANDALONE_SHORT_WEEKDAYS,
 436                                       UDAT_STANDALONE_NARROW_WEEKDAYS,
 437                                       UDAT_QUARTERS,
 438                                       UDAT_SHORT_QUARTERS,
 439                                       UDAT_STANDALONE_QUARTERS,
 440                                       UDAT_STANDALONE_SHORT_QUARTERS};
 441      int offsets[18] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0};
 442      CFArrayRef symbols[18] = {formatter->_property._EraSymbols,
 443                                formatter->_property._LongEraSymbols,
 444                                formatter->_property._MonthSymbols,
 445                                formatter->_property._ShortMonthSymbols,
 446                                formatter->_property._VeryShortMonthSymbols,
 447                                formatter->_property._StandaloneMonthSymbols,
 448                                formatter->_property._ShortStandaloneMonthSymbols,
 449                                formatter->_property._VeryShortStandaloneMonthSymbols,
 450                                formatter->_property._WeekdaySymbols,
 451                                formatter->_property._ShortWeekdaySymbols,
 452                                formatter->_property._VeryShortWeekdaySymbols,
 453                                formatter->_property._StandaloneWeekdaySymbols,
 454                                formatter->_property._ShortStandaloneWeekdaySymbols,
 455                                formatter->_property._VeryShortStandaloneWeekdaySymbols,
 456                                formatter->_property._QuarterSymbols,
 457                                formatter->_property._ShortQuarterSymbols,
 458                                formatter->_property._StandaloneQuarterSymbols,
 459                                formatter->_property._ShortStandaloneQuarterSymbols
 460      };
 461      CFArrayRef customSymbols[18] = {formatter->_property._CustomEraSymbols,
 462          formatter->_property._CustomLongEraSymbols,
 463          formatter->_property._CustomMonthSymbols,
 464          formatter->_property._CustomShortMonthSymbols,
 465          formatter->_property._CustomVeryShortMonthSymbols,
 466          formatter->_property._CustomStandaloneMonthSymbols,
 467          formatter->_property._CustomShortStandaloneMonthSymbols,
 468          formatter->_property._CustomVeryShortStandaloneMonthSymbols,
 469          formatter->_property._CustomWeekdaySymbols,
 470          formatter->_property._CustomShortWeekdaySymbols,
 471          formatter->_property._CustomVeryShortWeekdaySymbols,
 472          formatter->_property._CustomStandaloneWeekdaySymbols,
 473          formatter->_property._CustomShortStandaloneWeekdaySymbols,
 474          formatter->_property._CustomVeryShortStandaloneWeekdaySymbols,
 475          formatter->_property._CustomQuarterSymbols,
 476          formatter->_property._CustomShortQuarterSymbols,
 477          formatter->_property._CustomStandaloneQuarterSymbols,
 478          formatter->_property._CustomShortStandaloneQuarterSymbols
 479      };
 480      
 481      for (CFIndex i = 0; i < 18; i++) {
 482          if (symbols[i] != NULL) {
 483              __CFDateFormatterSetSymbolsArray(formatter->_df, types[i], offsets[i], symbols[i]);
 484          } else if (customSymbols[i] != NULL) {
 485              __CFDateFormatterSetSymbolsArray(formatter->_df, types[i], offsets[i], customSymbols[i]);
 486          }
 487      }
 488      
 489      CFStringRef ampm[2];
 490      ampm[0] = NULL;
 491      ampm[1] = NULL;
 492      
 493      if (formatter->_property._AMSymbol != NULL) {
 494          ampm[0] = formatter->_property._AMSymbol;
 495      } else if (formatter->_property._CustomAMSymbol != NULL) {
 496          ampm[0] = formatter->_property._CustomAMSymbol;
 497      }
 498      if (formatter->_property._PMSymbol != NULL) {
 499          ampm[1] = formatter->_property._PMSymbol;
 500      } else if (formatter->_property._CustomPMSymbol != NULL) {
 501          ampm[1] = formatter->_property._CustomPMSymbol;
 502      }
 503      for (CFIndex i = 0; i < 2; i++) {
 504          CFStringRef sym = ampm[i];
 505          if (sym != NULL) {
 506              CFIndex item_cnt = CFStringGetLength(sym);
 507              STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt));
 508              UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(sym);
 509              if (NULL == item_ustr) {
 510                  item_cnt = __CFMin(BUFFER_SIZE, item_cnt);
 511                  CFStringGetCharacters(sym, CFRangeMake(0, item_cnt), (UniChar *)item_buffer);
 512                  item_ustr = item_buffer;
 513              }
 514              UErrorCode status = U_ZERO_ERROR;
 515              __cficu_udat_setSymbols(formatter->_df, UDAT_AM_PMS, i, item_ustr, item_cnt, &status);
 516          }
 517      }
 518  }
 519      
 520  static void __SetCalendarProperties(CFDateFormatterRef df) {
 521      CFStringRef calName = df->_property._CalendarName ? (df->_property._CalendarName) : NULL;
 522      if (!calName) {
 523          calName = (CFStringRef)CFLocaleGetValue(df->_locale, kCFLocaleCalendarIdentifierKey);
 524      }
 525      UErrorCode status = U_ZERO_ERROR;
 526      const UCalendar *cal = __cficu_udat_getCalendar(df->_df);
 527      UCalendar *new_cal = NULL;
 528      
 529      if (df->_property._Calendar != NULL || df->_property._CalendarName != NULL) {
 530          UCalendar *caltmp = __CFCalendarCreateUCalendar(NULL, CFLocaleGetIdentifier(df->_locale), df->_property._TimeZone);
 531          if (caltmp) {
 532              new_cal = caltmp;
 533          }
 534      }
 535      if (new_cal == NULL) {
 536          new_cal = __cficu_ucal_clone(cal, &status);
 537      }
 538      
 539      if (df->_property._IsLenient != NULL) {
 540          status = U_ZERO_ERROR;
 541          CFBooleanRef value = df->_property._IsLenient;
 542          __cficu_ucal_setAttribute(new_cal, UCAL_LENIENT, (kCFBooleanTrue == value));
 543      }
 544      if (df->_property._TimeZone != NULL) {
 545          status = U_ZERO_ERROR;
 546          UChar ubuffer[BUFFER_SIZE];
 547          CFStringRef tznam = CFTimeZoneGetName(df->_property._TimeZone);
 548          CFIndex ucnt = CFStringGetLength(tznam);
 549          if (BUFFER_SIZE < ucnt) ucnt = BUFFER_SIZE;
 550          CFStringGetCharacters(tznam, CFRangeMake(0, ucnt), (UniChar *)ubuffer);
 551          __cficu_ucal_setTimeZone(new_cal, ubuffer, ucnt, &status);
 552      }
 553      if (df->_property._GregorianStartDate != NULL) {
 554          status = U_ZERO_ERROR;
 555          CFAbsoluteTime at = CFDateGetAbsoluteTime((CFDateRef)df->_property._GregorianStartDate);
 556          UDate udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
 557          __cficu_ucal_setGregorianChange(new_cal, udate, &status);
 558      } else if (calName && CFEqual(calName, kCFCalendarIdentifierGregorian)) {
 559          status = U_ZERO_ERROR;
 560          UDate udate = __cficu_ucal_getGregorianChange(cal, &status);
 561          CFAbsoluteTime at = U_SUCCESS(status) ? (udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970) : -13197600000.0; // Oct 15, 1582
 562          udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
 563          status = U_ZERO_ERROR;
 564          __cficu_ucal_setGregorianChange(new_cal, udate, &status);
 565      }
 566      if (df->_property._Calendar != NULL) {
 567          __cficu_ucal_setAttribute(new_cal, UCAL_FIRST_DAY_OF_WEEK, CFCalendarGetFirstWeekday((CFCalendarRef)df->_property._Calendar));
 568      } else if (df->_property._CustomFirstWeekday != NULL) {
 569          CFNumberRef firstWeekday = (CFNumberRef)CFDictionaryGetValue(df->_property._CustomFirstWeekday, calName);
 570          if (NULL != firstWeekday && CFGetTypeID(firstWeekday) == CFNumberGetTypeID()) {
 571              CFIndex wkdy;
 572              if (CFNumberGetValue((CFNumberRef)firstWeekday, kCFNumberCFIndexType, &wkdy)) {
 573                  status = U_ZERO_ERROR;
 574                  __cficu_ucal_setAttribute(new_cal, UCAL_FIRST_DAY_OF_WEEK, wkdy);
 575              }
 576          }
 577      }
 578      
 579      if (df->_property._Calendar != NULL) {
 580          __cficu_ucal_setAttribute(new_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, CFCalendarGetMinimumDaysInFirstWeek((CFCalendarRef)df->_property._Calendar));
 581      } else if (df->_property._CustomMinDaysInFirstWeek != NULL) {
 582          CFNumberRef minDays = (CFNumberRef)CFDictionaryGetValue(df->_property._CustomMinDaysInFirstWeek, calName);
 583          if (NULL != minDays && CFGetTypeID(minDays) == CFNumberGetTypeID()) {
 584              CFIndex mwd;
 585              if (CFNumberGetValue((CFNumberRef)minDays, kCFNumberCFIndexType, &mwd)) {
 586                  status = U_ZERO_ERROR;
 587                  __cficu_ucal_setAttribute(new_cal, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, mwd);
 588              }
 589          }
 590      }
 591      __cficu_udat_setCalendar(df->_df, new_cal);
 592      __cficu_ucal_close(new_cal);
 593  }
 594      
 595  #define RESET_PROPERTY(C, K) \
 596      if (df->_property. C) __CFDateFormatterSetProperty(df, K, df->_property. C, true);
 597  
 598  static void __ResetUDateFormat(CFDateFormatterRef df, Boolean goingToHaveCustomFormat) {
 599      if (df->_df) __cficu_udat_close(df->_df);
 600      df->_df = NULL;
 601  
 602      // uses _timeStyle, _dateStyle, _locale, _property._TimeZone; sets _df, _format, _defformat
 603      char loc_buffer[BUFFER_SIZE];
 604      loc_buffer[0] = 0;
 605      CFStringRef tmpLocName = df->_locale ? CFLocaleGetIdentifier(df->_locale) : CFSTR("");
 606      CFStringGetCString(tmpLocName, loc_buffer, BUFFER_SIZE, kCFStringEncodingASCII);
 607  
 608      UChar tz_buffer[BUFFER_SIZE];
 609      tz_buffer[0] = 0;
 610      CFStringRef tmpTZName = df->_property._TimeZone ? CFTimeZoneGetName(df->_property._TimeZone) : CFSTR("GMT");
 611      CFStringGetCharacters(tmpTZName, CFRangeMake(0, CFStringGetLength(tmpTZName)), (UniChar *)tz_buffer);
 612  
 613      int32_t udstyle = 0, utstyle = 0; // effectively this makes UDAT_FULL the default for unknown dateStyle/timeStyle values
 614      switch (df->_dateStyle) {
 615      case kCFDateFormatterNoStyle: udstyle = UDAT_NONE; break;
 616      case kCFDateFormatterShortStyle: udstyle = UDAT_SHORT; break;
 617      case kCFDateFormatterMediumStyle: udstyle = UDAT_MEDIUM; break;
 618      case kCFDateFormatterLongStyle: udstyle = UDAT_LONG; break;
 619      case kCFDateFormatterFullStyle: udstyle = UDAT_FULL; break;
 620      }
 621      switch (df->_timeStyle) {
 622      case kCFDateFormatterNoStyle: utstyle = UDAT_NONE; break;
 623      case kCFDateFormatterShortStyle: utstyle = UDAT_SHORT; break;
 624      case kCFDateFormatterMediumStyle: utstyle = UDAT_MEDIUM; break;
 625      case kCFDateFormatterLongStyle: utstyle = UDAT_LONG; break;
 626      case kCFDateFormatterFullStyle: utstyle = UDAT_FULL; break;
 627      }
 628      Boolean wantRelative = (NULL != df->_property._DoesRelativeDateFormatting && df->_property._DoesRelativeDateFormatting == kCFBooleanTrue);
 629      Boolean hasFormat = (NULL != df->_property._HasCustomFormat && df->_property._HasCustomFormat == kCFBooleanTrue) || goingToHaveCustomFormat;
 630      if (wantRelative && !hasFormat && kCFDateFormatterNoStyle != df->_dateStyle) {
 631  	udstyle |= UDAT_RELATIVE;
 632      }
 633  
 634      UErrorCode status = U_ZERO_ERROR;
 635      UDateFormat *icudf = __cficu_udat_open((UDateFormatStyle)utstyle, (UDateFormatStyle)udstyle, loc_buffer, tz_buffer, CFStringGetLength(tmpTZName), NULL, 0, &status);
 636  
 637      if (NULL == icudf || U_FAILURE(status)) {
 638          return;
 639      }
 640      
 641      // <rdar://problem/15420462> "Yesterday" and "Today" now appear in lower case
 642      // 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
 643      if (wantRelative) {
 644          __cficu_udat_setContext(icudf, UDISPCTX_CAPITALIZATION_FOR_UI_LIST_OR_MENU, &status);
 645      }
 646      
 647      if (df->_property._IsLenient != NULL) {
 648          __cficu_udat_setLenient(icudf, (kCFBooleanTrue == df->_property._IsLenient));
 649      } else {
 650          __cficu_udat_setLenient(icudf, 0);
 651      }
 652      if (kCFDateFormatterNoStyle == df->_dateStyle && kCFDateFormatterNoStyle == df->_timeStyle) {
 653          if (wantRelative && !hasFormat && kCFDateFormatterNoStyle != df->_dateStyle) {
 654              UErrorCode s = U_ZERO_ERROR;
 655              __cficu_udat_applyPatternRelative(icudf, NULL, 0, NULL, 0, &s);
 656          } else {
 657              __cficu_udat_applyPattern(icudf, false, NULL, 0);
 658          }
 659      }
 660      if (!wantRelative && df->_property._HasCustomFormat == kCFBooleanTrue) {
 661          CFIndex cnt = CFStringGetLength(df->_format);
 662          STACK_BUFFER_DECL(UChar, ubuffer, cnt);
 663          const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)df->_format);
 664          if (NULL == ustr) {
 665              CFStringGetCharacters(df->_format, CFRangeMake(0, cnt), (UniChar *)ubuffer);
 666              ustr = ubuffer;
 667          }
 668          __cficu_udat_applyPattern(icudf, false, ustr, cnt);
 669      }
 670      
 671      CFStringRef calident = (CFStringRef)CFLocaleGetValue(df->_locale, kCFLocaleCalendarIdentifierKey);
 672      if (calident && CFEqual(calident, kCFCalendarIdentifierGregorian)) {
 673          status = U_ZERO_ERROR;
 674          __cficu_udat_set2DigitYearStart(icudf, -631152000000.0, &status); // 1950-01-01 00:00:00 GMT
 675      }
 676      df->_df = icudf;
 677      
 678      __ReadCustomUDateFormatProperty(df);
 679   
 680      __SetCalendarProperties(df);
 681      
 682      if (wantRelative && !hasFormat && kCFDateFormatterNoStyle != df->_dateStyle) {
 683          __substituteFormatStringFromPrefsDFRelative(df);
 684      } else {
 685          __substituteFormatStringFromPrefsDF(df, false);
 686          __substituteFormatStringFromPrefsDF(df, true);
 687      }
 688  
 689      __ApplyUDateFormatSymbol(df);
 690      
 691  
 692      if (wantRelative && !hasFormat && kCFDateFormatterNoStyle != df->_dateStyle) {
 693          UChar dateBuffer[BUFFER_SIZE];
 694          UChar timeBuffer[BUFFER_SIZE];
 695          status = U_ZERO_ERROR;
 696          CFIndex dateLen = __cficu_udat_toPatternRelativeDate(icudf, dateBuffer, BUFFER_SIZE, &status);
 697          CFIndex timeLen = (utstyle != UDAT_NONE) ? __cficu_udat_toPatternRelativeTime(icudf, timeBuffer, BUFFER_SIZE, &status) : 0;
 698          if (U_SUCCESS(status) && dateLen <= BUFFER_SIZE && timeLen <= BUFFER_SIZE) {
 699              // We assume that the 12/24-hour forcing preferences only affect the Time component
 700              CFStringRef newFormat = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)timeBuffer, timeLen);
 701              CFStringRef formatString = __CFDateFormatterCreateForcedString(df, newFormat);
 702              CFIndex cnt = CFStringGetLength(formatString);
 703              CFAssert1(cnt <= BUFFER_SIZE, __kCFLogAssertion, "%s(): time format string too long", __PRETTY_FUNCTION__);
 704              if (cnt <= BUFFER_SIZE) {
 705                  CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)timeBuffer);
 706                  timeLen = cnt;
 707                  status = U_ZERO_ERROR;
 708                  __cficu_udat_applyPatternRelative(icudf, dateBuffer, dateLen, timeBuffer, timeLen, &status);
 709                  // ignore error and proceed anyway, what else can be done?
 710  
 711                  UChar ubuffer[BUFFER_SIZE];
 712                  status = U_ZERO_ERROR;
 713                  int32_t ret = __cficu_udat_toPattern(icudf, false, ubuffer, BUFFER_SIZE, &status); // read out current pattern
 714                  if (U_SUCCESS(status) && ret <= BUFFER_SIZE) {
 715                      if (df->_format) CFRelease(df->_format);
 716                      df->_format = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, ret);
 717                  }
 718              }
 719              CFRelease(formatString);
 720              CFRelease(newFormat);
 721          }
 722      } else {
 723          UChar ubuffer[BUFFER_SIZE];
 724          status = U_ZERO_ERROR;
 725          int32_t ret = __cficu_udat_toPattern(icudf, false, ubuffer, BUFFER_SIZE, &status);
 726          if (U_SUCCESS(status) && ret <= BUFFER_SIZE) {
 727              CFStringRef newFormat = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, ret);
 728              CFStringRef formatString = __CFDateFormatterCreateForcedString(df, newFormat);
 729              CFIndex cnt = CFStringGetLength(formatString);
 730              CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__);
 731              if (df->_format != formatString && cnt <= 1024) {
 732                  STACK_BUFFER_DECL(UChar, ubuffer, cnt);
 733                  const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)formatString);
 734                  if (NULL == ustr) {
 735                      CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)ubuffer);
 736                      ustr = ubuffer;
 737                  }
 738                  UErrorCode status = U_ZERO_ERROR;
 739  //            __cficu_udat_applyPattern(df->_df, false, ustr, cnt, &status);
 740                  __cficu_udat_applyPattern(df->_df, false, ustr, cnt);
 741                  if (U_SUCCESS(status)) {
 742                      if (df->_format) CFRelease(df->_format);
 743                      df->_format = (CFStringRef)CFStringCreateCopy(CFGetAllocator(df), formatString);
 744                  }
 745              }
 746              CFRelease(formatString);
 747              CFRelease(newFormat);
 748          }
 749      }
 750      if (df->_defformat) CFRelease(df->_defformat);
 751      df->_defformat = df->_format ? (CFStringRef)CFRetain(df->_format) : NULL;
 752  
 753      RESET_PROPERTY(_IsLenient, kCFDateFormatterIsLenientKey);
 754      RESET_PROPERTY(_DoesRelativeDateFormatting, kCFDateFormatterDoesRelativeDateFormattingKey);
 755      RESET_PROPERTY(_Calendar, kCFDateFormatterCalendarKey);
 756      RESET_PROPERTY(_CalendarName, kCFDateFormatterCalendarIdentifierKey);
 757      RESET_PROPERTY(_TimeZone, kCFDateFormatterTimeZoneKey);
 758      RESET_PROPERTY(_TwoDigitStartDate, kCFDateFormatterTwoDigitStartDateKey);
 759      RESET_PROPERTY(_DefaultDate, kCFDateFormatterDefaultDateKey);
 760      RESET_PROPERTY(_GregorianStartDate, kCFDateFormatterGregorianStartDateKey);
 761      RESET_PROPERTY(_AmbiguousYearStrategy, kCFDateFormatterAmbiguousYearStrategyKey);
 762      RESET_PROPERTY(_UsesCharacterDirection, kCFDateFormatterUsesCharacterDirectionKey);
 763      RESET_PROPERTY(_FormattingContext, kCFDateFormatterFormattingContextKey);
 764  }
 765  
 766  static CFTypeID __kCFDateFormatterTypeID = _kCFRuntimeNotATypeID;
 767  
 768  static const CFRuntimeClass __CFDateFormatterClass = {
 769      0,
 770      "CFDateFormatter",
 771      NULL,        // init
 772      NULL,        // copy
 773      __CFDateFormatterDeallocate,
 774      NULL,
 775      NULL,
 776      NULL,        //
 777      __CFDateFormatterCopyDescription
 778  };
 779  
 780  CFTypeID CFDateFormatterGetTypeID(void) {
 781      static dispatch_once_t initOnce;
 782      dispatch_once(&initOnce, ^{ __kCFDateFormatterTypeID = _CFRuntimeRegisterClass(&__CFDateFormatterClass); });
 783      return __kCFDateFormatterTypeID;
 784  }
 785  
 786  CFDateFormatterRef CFDateFormatterCreate(CFAllocatorRef allocator, CFLocaleRef locale, CFDateFormatterStyle dateStyle, CFDateFormatterStyle timeStyle) {
 787      struct __CFDateFormatter *memory;
 788      uint32_t size = sizeof(struct __CFDateFormatter) - sizeof(CFRuntimeBase);
 789      if (allocator == NULL) allocator = __CFGetDefaultAllocator();
 790      __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
 791      if (locale) __CFGenericValidateType(locale, CFLocaleGetTypeID());
 792      memory = (struct __CFDateFormatter *)_CFRuntimeCreateInstance(allocator, CFDateFormatterGetTypeID(), size, NULL);
 793      if (NULL == memory) {
 794          return NULL;
 795      }
 796      memory->_df = NULL;
 797      memory->_locale = NULL;
 798      memory->_format = NULL;
 799      memory->_defformat = NULL;
 800      memory->_dateStyle = dateStyle;
 801      memory->_timeStyle = timeStyle;
 802      memory->_property._IsLenient = NULL;
 803      memory->_property._DoesRelativeDateFormatting = NULL;
 804      memory->_property._HasCustomFormat = NULL;
 805      memory->_property._TimeZone = NULL;
 806      memory->_property._Calendar = NULL;
 807      memory->_property._CalendarName = NULL;
 808      memory->_property._TwoDigitStartDate = NULL;
 809      memory->_property._DefaultDate = NULL;
 810      memory->_property._GregorianStartDate = NULL;
 811      memory->_property._EraSymbols = NULL;
 812      memory->_property._LongEraSymbols = NULL;
 813      memory->_property._MonthSymbols = NULL;
 814      memory->_property._ShortMonthSymbols = NULL;
 815      memory->_property._VeryShortMonthSymbols = NULL;
 816      memory->_property._StandaloneMonthSymbols = NULL;
 817      memory->_property._ShortStandaloneMonthSymbols = NULL;
 818      memory->_property._VeryShortStandaloneMonthSymbols = NULL;
 819      memory->_property._WeekdaySymbols = NULL;
 820      memory->_property._ShortWeekdaySymbols = NULL;
 821      memory->_property._VeryShortWeekdaySymbols = NULL;
 822      memory->_property._StandaloneWeekdaySymbols = NULL;
 823      memory->_property._ShortStandaloneWeekdaySymbols = NULL;
 824      memory->_property._VeryShortStandaloneWeekdaySymbols = NULL;
 825      memory->_property._QuarterSymbols = NULL;
 826      memory->_property._ShortQuarterSymbols = NULL;
 827      memory->_property._StandaloneQuarterSymbols = NULL;
 828      memory->_property._ShortStandaloneQuarterSymbols = NULL;
 829      memory->_property._AMSymbol = NULL;
 830      memory->_property._PMSymbol = NULL;
 831      memory->_property._AmbiguousYearStrategy = NULL;
 832      memory->_property._UsesCharacterDirection = NULL;
 833      memory->_property._FormattingContext = NULL;
 834      memory->_property._CustomEraSymbols = NULL;
 835      memory->_property._CustomMonthSymbols = NULL;
 836      memory->_property._CustomShortMonthSymbols = NULL;
 837      memory->_property._CustomWeekdaySymbols = NULL;
 838      memory->_property._CustomShortWeekdaySymbols = NULL;
 839      memory->_property._CustomLongEraSymbols = NULL;
 840      memory->_property._CustomVeryShortMonthSymbols = NULL;
 841      memory->_property._CustomVeryShortWeekdaySymbols = NULL;
 842      memory->_property._CustomStandaloneMonthSymbols = NULL;
 843      memory->_property._CustomShortStandaloneMonthSymbols = NULL;
 844      memory->_property._CustomVeryShortStandaloneMonthSymbols = NULL;
 845      memory->_property._CustomStandaloneWeekdaySymbols = NULL;
 846      memory->_property._CustomShortStandaloneWeekdaySymbols = NULL;
 847      memory->_property._CustomVeryShortStandaloneWeekdaySymbols = NULL;
 848      memory->_property._CustomQuarterSymbols = NULL;
 849      memory->_property._CustomShortQuarterSymbols = NULL;
 850      memory->_property._CustomStandaloneQuarterSymbols = NULL;
 851      memory->_property._CustomShortStandaloneQuarterSymbols = NULL;
 852      memory->_property._CustomDateFormat = NULL;
 853      memory->_property._CustomTimeFormat = NULL;
 854      memory->_property._Custom24Hour = NULL;
 855      memory->_property._Custom12Hour = NULL;
 856      memory->_property._CustomAMSymbol = NULL;
 857      memory->_property._CustomPMSymbol = NULL;
 858      memory->_property._CustomFirstWeekday = NULL;
 859      memory->_property._CustomMinDaysInFirstWeek = NULL;
 860  
 861      switch (dateStyle) {
 862      case kCFDateFormatterNoStyle:
 863      case kCFDateFormatterShortStyle:
 864      case kCFDateFormatterMediumStyle:
 865      case kCFDateFormatterLongStyle:
 866      case kCFDateFormatterFullStyle: break;
 867      default:
 868          CFAssert2(0, __kCFLogAssertion, "%s(): unknown date style %d", __PRETTY_FUNCTION__, dateStyle);
 869          memory->_dateStyle = kCFDateFormatterMediumStyle;
 870          break;
 871      }
 872      switch (timeStyle) {
 873      case kCFDateFormatterNoStyle:
 874      case kCFDateFormatterShortStyle:
 875      case kCFDateFormatterMediumStyle:
 876      case kCFDateFormatterLongStyle:
 877      case kCFDateFormatterFullStyle: break;
 878      default:
 879          CFAssert2(0, __kCFLogAssertion, "%s(): unknown time style %d", __PRETTY_FUNCTION__, timeStyle);
 880          memory->_timeStyle = kCFDateFormatterMediumStyle;
 881          break;
 882      }
 883  
 884      memory->_locale = locale ? CFLocaleCreateCopy(allocator, locale) : (CFLocaleRef)CFRetain(CFLocaleGetSystem());
 885      memory->_property._TimeZone = CFTimeZoneCopyDefault();
 886      
 887      CFStringRef calident = (CFStringRef)CFLocaleGetValue(memory->_locale, kCFLocaleCalendarIdentifierKey);
 888      if (calident && CFEqual(calident, kCFCalendarIdentifierGregorian)) {
 889          memory->_property._TwoDigitStartDate = CFDateCreate(kCFAllocatorSystemDefault, -1609459200.0); // 1950-01-01 00:00:00 +0000
 890      }
 891      
 892      __ResetUDateFormat(memory, false);
 893      if (!memory->_df) {
 894          CFRelease(memory);
 895  	return NULL;
 896      }
 897      return (CFDateFormatterRef)memory;
 898  }
 899  
 900  static void __substituteFormatStringFromPrefsDFRelative(CFDateFormatterRef formatter) {
 901  
 902      CFIndex dateLen = -1;
 903      UChar dateBuffer[BUFFER_SIZE];
 904      if (kCFDateFormatterNoStyle != formatter->_dateStyle) {
 905          if (formatter->_property._CustomDateFormat != NULL) {
 906              dateLen = __CFMin(CFStringGetLength(formatter->_property._CustomDateFormat), BUFFER_SIZE);
 907              CFStringGetCharacters(formatter->_property._CustomDateFormat, CFRangeMake(0, dateLen), (UniChar *)dateBuffer);
 908          }
 909      }
 910      if (-1 == dateLen) {
 911          UErrorCode status = U_ZERO_ERROR;
 912          int32_t ret = __cficu_udat_toPatternRelativeDate(formatter->_df, dateBuffer, BUFFER_SIZE, &status);
 913          if (!U_FAILURE(status)) {
 914              dateLen = ret;
 915          }
 916      }
 917  
 918      CFIndex timeLen = -1;
 919      UChar timeBuffer[BUFFER_SIZE];
 920      if (kCFDateFormatterNoStyle != formatter->_timeStyle) {
 921          if (formatter->_property._CustomTimeFormat != NULL) {
 922              timeLen = __CFMin(CFStringGetLength(formatter->_property._CustomTimeFormat), BUFFER_SIZE);
 923              CFStringGetCharacters(formatter->_property._CustomTimeFormat, CFRangeMake(0, timeLen), (UniChar *)timeBuffer);
 924          }
 925      }
 926      if (-1 == timeLen) {
 927          UErrorCode status = U_ZERO_ERROR;
 928          int32_t ret = __cficu_udat_toPatternRelativeTime(formatter->_df, timeBuffer, BUFFER_SIZE, &status);
 929          if (!U_FAILURE(status)) {
 930              timeLen = ret;
 931          }
 932      }
 933  
 934      UErrorCode status = U_ZERO_ERROR;
 935      __cficu_udat_applyPatternRelative(formatter->_df, (0 <= dateLen) ? dateBuffer : NULL, (0 <= dateLen) ? dateLen : 0, (0 <= timeLen) ? timeBuffer : NULL, (0 <= timeLen) ? timeLen : 0, &status);
 936  }
 937  
 938  static void __substituteFormatStringFromPrefsDF(CFDateFormatterRef formatter, bool doTime) {
 939      CFIndex formatStyle = doTime ? formatter->_timeStyle : formatter->_dateStyle;
 940      CFStringRef pref = doTime ? formatter->_property._CustomTimeFormat : formatter->_property._CustomDateFormat;
 941      if (kCFDateFormatterNoStyle != formatStyle) {
 942          if (NULL != pref) {
 943              int32_t icustyle = UDAT_NONE;
 944              switch (formatStyle) {
 945              case kCFDateFormatterShortStyle: icustyle = UDAT_SHORT; break;
 946              case kCFDateFormatterMediumStyle: icustyle = UDAT_MEDIUM; break;
 947              case kCFDateFormatterLongStyle: icustyle = UDAT_LONG; break;
 948              case kCFDateFormatterFullStyle: icustyle = UDAT_FULL; break;
 949              }
 950              CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale);
 951              char buffer[BUFFER_SIZE];
 952              const char *cstr = CFStringGetCStringPtr(localeName, kCFStringEncodingASCII);
 953              if (NULL == cstr) {
 954                  if (CFStringGetCString(localeName, buffer, BUFFER_SIZE, kCFStringEncodingASCII)) cstr = buffer;
 955              }
 956              UErrorCode status = U_ZERO_ERROR;
 957              UDateFormat *df = __cficu_udat_open((UDateFormatStyle)(doTime ? icustyle : UDAT_NONE), (UDateFormatStyle)(doTime ? UDAT_NONE : icustyle), cstr, NULL, 0, NULL, 0, &status);
 958              if (NULL != df) {
 959                  UChar ubuffer[BUFFER_SIZE];
 960                  status = U_ZERO_ERROR;
 961                  int32_t date_len = __cficu_udat_toPattern(df, false, ubuffer, BUFFER_SIZE, &status);
 962                  if (U_SUCCESS(status) && date_len <= BUFFER_SIZE) {
 963                      CFStringRef dateString = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (UniChar *)ubuffer, date_len);
 964                      status = U_ZERO_ERROR;
 965                      int32_t formatter_len = __cficu_udat_toPattern(formatter->_df, false, ubuffer, BUFFER_SIZE, &status);
 966                      if (U_SUCCESS(status) && formatter_len <= BUFFER_SIZE) {
 967                          CFMutableStringRef formatString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
 968                          CFStringAppendCharacters(formatString, (UniChar *)ubuffer, formatter_len);
 969                          // find dateString inside formatString, substitute the pref in that range
 970                          CFRange result;
 971                          if (CFStringFindWithOptions(formatString, dateString, CFRangeMake(0, formatter_len), 0, &result)) {
 972                              CFStringReplace(formatString, result, pref);
 973                              int32_t new_len = CFStringGetLength(formatString);
 974                              STACK_BUFFER_DECL(UChar, new_buffer, new_len);
 975                              const UChar *new_ustr = (UChar *)CFStringGetCharactersPtr(formatString);
 976                              if (NULL == new_ustr) {
 977                                  CFStringGetCharacters(formatString, CFRangeMake(0, new_len), (UniChar *)new_buffer);
 978                                  new_ustr = new_buffer;
 979                                  }
 980                              status = U_ZERO_ERROR;
 981  //                            __cficu_udat_applyPattern(formatter->_df, false, new_ustr, new_len, &status);
 982                              __cficu_udat_applyPattern(formatter->_df, false, new_ustr, new_len);
 983                          }
 984                          CFRelease(formatString);
 985                      }
 986                      CFRelease(dateString);
 987                  }
 988                  __cficu_udat_close(df);
 989              }
 990          }
 991      }
 992  }
 993  
 994  static void __CFDateFormatterStoreSymbolPrefs(const void *key, const void *value, void *context) {
 995      if (CFGetTypeID(key) == CFStringGetTypeID() && CFGetTypeID(value) == CFArrayGetTypeID()) {
 996          CFDateFormatterRef formatter = (CFDateFormatterRef)context;
 997          UDateFormatSymbolType sym = (UDateFormatSymbolType)CFStringGetIntValue((CFStringRef)key);
 998          CFArrayRef array = (CFArrayRef)value;
 999          CFIndex idx, cnt = CFArrayGetCount(array);
1000          switch (sym) {
1001              case UDAT_ERAS:
1002                  formatter->_property._CustomEraSymbols = (CFArrayRef)CFRetain(array);
1003                  break;
1004              case UDAT_MONTHS:
1005                  formatter->_property._CustomMonthSymbols = (CFArrayRef)CFRetain(array);
1006                  break;
1007              case UDAT_SHORT_MONTHS:
1008                  formatter->_property._CustomShortMonthSymbols = (CFArrayRef)CFRetain(array);
1009                  break;
1010              case UDAT_WEEKDAYS:
1011                  formatter->_property._CustomWeekdaySymbols = (CFArrayRef)CFRetain(array);
1012                  break;
1013              case UDAT_SHORT_WEEKDAYS:
1014                  formatter->_property._CustomShortWeekdaySymbols = (CFArrayRef)CFRetain(array);
1015                  break;
1016              case UDAT_AM_PMS:
1017                  {
1018                      for (idx = 0; idx < cnt; idx++) {
1019                          CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, idx);
1020                          if (CFGetTypeID(item) != CFStringGetTypeID()) continue;
1021                          if (idx == 0) {
1022                              formatter->_property._CustomAMSymbol = (CFStringRef)CFRetain(item);
1023                          } else if (idx == 1) {
1024                              formatter->_property._CustomPMSymbol = (CFStringRef)CFRetain(item);
1025                          }
1026                      }
1027                  }
1028                  break;
1029              case UDAT_ERA_NAMES:
1030                  formatter->_property._CustomLongEraSymbols = (CFArrayRef)CFRetain(array);
1031                  break;
1032              case UDAT_NARROW_MONTHS:
1033                  formatter->_property._CustomVeryShortMonthSymbols = (CFArrayRef)CFRetain(array);
1034                  break;
1035              case UDAT_NARROW_WEEKDAYS:
1036                  formatter->_property._CustomVeryShortWeekdaySymbols = (CFArrayRef)CFRetain(array);
1037                  break;
1038              case UDAT_STANDALONE_MONTHS:
1039                  formatter->_property._CustomStandaloneMonthSymbols = (CFArrayRef)CFRetain(array);
1040                  break;
1041              case UDAT_STANDALONE_SHORT_MONTHS:
1042                  formatter->_property._CustomShortStandaloneMonthSymbols = (CFArrayRef)CFRetain(array);
1043                  break;
1044              case UDAT_STANDALONE_NARROW_MONTHS:
1045                  formatter->_property._CustomVeryShortStandaloneMonthSymbols = (CFArrayRef)CFRetain(array);
1046                  break;
1047              case UDAT_STANDALONE_WEEKDAYS:
1048                  formatter->_property._CustomStandaloneWeekdaySymbols = (CFArrayRef)CFRetain(array);
1049                  break;
1050              case UDAT_STANDALONE_SHORT_WEEKDAYS:
1051                  formatter->_property._CustomShortStandaloneWeekdaySymbols = (CFArrayRef)CFRetain(array);
1052                  break;
1053              case UDAT_STANDALONE_NARROW_WEEKDAYS:
1054                  formatter->_property._CustomVeryShortStandaloneWeekdaySymbols = (CFArrayRef)CFRetain(array);
1055                  break;
1056              case UDAT_QUARTERS:
1057                  formatter->_property._CustomQuarterSymbols = (CFArrayRef)CFRetain(array);
1058                  break;
1059              case UDAT_SHORT_QUARTERS:
1060                  formatter->_property._CustomShortQuarterSymbols = (CFArrayRef)CFRetain(array);
1061                  break;
1062              case UDAT_STANDALONE_QUARTERS:
1063                  formatter->_property._CustomStandaloneQuarterSymbols = (CFArrayRef)CFRetain(array);
1064                  break;
1065              case UDAT_STANDALONE_SHORT_QUARTERS:
1066                  formatter->_property._CustomShortStandaloneQuarterSymbols = (CFArrayRef)CFRetain(array);
1067                  break;
1068              default:
1069                  break;
1070          }
1071      }
1072  }
1073  
1074  static CFStringRef __CFDateFormatterCreateForcedTemplate(CFLocaleRef locale, CFStringRef inString, Boolean stripAMPM) {
1075      if (!inString) return NULL;
1076      Boolean doForce24 = false, doForce12 = false;
1077      CFDictionaryRef prefs = __CFLocaleGetPrefs(locale);
1078      CFPropertyListRef pref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce24HourTime")) : NULL;
1079      if (NULL != pref && CFGetTypeID(pref) == CFBooleanGetTypeID()) {
1080          doForce24 = CFBooleanGetValue((CFBooleanRef)pref);
1081      }
1082      pref = prefs ? CFDictionaryGetValue(prefs, CFSTR("AppleICUForce12HourTime")) : NULL;
1083      if (NULL != pref && CFGetTypeID(pref) == CFBooleanGetTypeID()) {
1084          doForce12 = CFBooleanGetValue((CFBooleanRef)pref);
1085      }
1086      if (doForce24) doForce12 = false; // if both are set, Force24 wins, period
1087      if (!doForce24 && !doForce12) return (CFStringRef)CFRetain(inString);
1088      
1089      CFMutableStringRef outString = CFStringCreateMutable(kCFAllocatorSystemDefault, 0);
1090      CFIndex cnt = CFStringGetLength(inString);
1091      CFIndex lastSecond = -1, lastMinute = -1, firstHour = -1;
1092      Boolean isInQuote = false, hasA = false, had12Hour = false, had24Hour = false;
1093      for (CFIndex idx = 0; idx < cnt; idx++) {
1094          Boolean emit = true;
1095          UniChar ch = CFStringGetCharacterAtIndex(inString, idx);
1096          switch (ch) {
1097              case '\'': isInQuote = !isInQuote; break;
1098              case 'J': //fall through
1099              case 'j': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); if (doForce24) ch = 'H'; else ch = 'h';} break;
1100              case 'h': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had12Hour = true; if (doForce24) ch = 'H';} break; // switch 12-hour to 24-hour
1101              case 'K': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had12Hour = true; if (doForce24) ch = 'k';} break; // switch 12-hour to 24-hour
1102              case 'H': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had24Hour = true; if (doForce12) ch = 'h';} break; // switch 24-hour to 12-hour
1103              case 'k': if (!isInQuote) {if (-1 == firstHour) firstHour = CFStringGetLength(outString); had24Hour = true; if (doForce12) ch = 'K';} break; // switch 24-hour to 12-hour
1104              case 'm': if (!isInQuote) lastMinute = CFStringGetLength(outString); break;
1105              case 's': if (!isInQuote) lastSecond = CFStringGetLength(outString); break;
1106              case 'a': if (!isInQuote) {hasA = true; if (doForce24 || stripAMPM) emit = false;} break;
1107                  break;
1108          }
1109          if (emit) CFStringAppendCharacters(outString, &ch, 1);
1110      }
1111      
1112      return outString;
1113  }
1114  
1115  static CFStringRef __CFDateFormatterCreateForcedString(CFDateFormatterRef formatter, CFStringRef inString) {
1116      if (!inString) return NULL;
1117      
1118      UDateTimePatternMatchOptions options = UDATPG_MATCH_NO_OPTIONS;
1119      
1120      if (formatter->_property._Custom12Hour != NULL && CFBooleanGetValue((CFBooleanRef)formatter->_property._Custom12Hour)) {
1121          options = UADATPG_FORCE_12_HOUR_CYCLE;
1122      }
1123      if (formatter->_property._Custom24Hour != NULL && CFBooleanGetValue((CFBooleanRef)formatter->_property._Custom24Hour)) {
1124          options = UADATPG_FORCE_24_HOUR_CYCLE; //force 24 hour always wins if both are specified
1125      }
1126      if (options == UDATPG_MATCH_NO_OPTIONS) return (CFStringRef)CFRetain(inString);
1127      
1128      static CFCharacterSetRef hourCharacters;
1129      static dispatch_once_t onceToken;
1130      dispatch_once(&onceToken, ^{
1131          hourCharacters = CFCharacterSetCreateWithCharactersInString(kCFAllocatorSystemDefault, CFSTR("hHkK"));
1132      });
1133      
1134      CFRange hourRange = CFRangeMake(kCFNotFound, 0);
1135      if (!CFStringFindCharacterFromSet(inString, hourCharacters, CFRangeMake(0, CFStringGetLength(inString)), 0, &hourRange) || hourRange.location == kCFNotFound) {
1136          return (CFStringRef)CFRetain(inString);
1137      }
1138      __block CFStringRef result = NULL;
1139      __block int32_t newPatternLen = 0;
1140      Boolean success = useTemplatePatternGenerator(formatter->_locale, ^(UDateTimePatternGenerator *ptg) {
1141          CFIndex cnt = CFStringGetLength(inString);
1142          STACK_BUFFER_DECL(UChar, ubuffer, cnt);
1143          const UChar *ustr = (UChar *)CFStringGetCharactersPtr(inString);
1144          if (NULL == ustr) {
1145              CFStringGetCharacters(inString, CFRangeMake(0, cnt), (UniChar *)ubuffer);
1146              ustr = ubuffer;
1147          }
1148          STACK_BUFFER_DECL(UChar, outBuffer, 256);
1149          
1150          UErrorCode err = U_ZERO_ERROR;
1151          newPatternLen = uadatpg_remapPatternWithOptions(ptg, ustr, cnt, options, outBuffer, 256, &err);
1152          if (U_SUCCESS(err)) {
1153              result = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, outBuffer, newPatternLen);
1154          } else if (err == U_BUFFER_OVERFLOW_ERROR) {
1155              err = U_ZERO_ERROR;
1156              UChar *largerBuffer = calloc(newPatternLen + 1, sizeof(UChar));
1157              newPatternLen = uadatpg_remapPatternWithOptions(ptg, ustr, cnt, options, largerBuffer, newPatternLen + 1, &err);
1158              if (U_SUCCESS(err)) {
1159                  result = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, largerBuffer, newPatternLen);
1160              }
1161              free(largerBuffer);
1162          }
1163      });
1164      return success && result && newPatternLen > 0 ? result : CFRetain(inString);
1165  }
1166  
1167  CFLocaleRef CFDateFormatterGetLocale(CFDateFormatterRef formatter) {
1168      __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1169      return formatter->_locale;
1170  }
1171  
1172  CFDateFormatterStyle CFDateFormatterGetDateStyle(CFDateFormatterRef formatter) {
1173      __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1174      return formatter->_dateStyle;
1175  }
1176  
1177  CFDateFormatterStyle CFDateFormatterGetTimeStyle(CFDateFormatterRef formatter) {
1178      __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1179      return formatter->_timeStyle;
1180  }
1181  
1182  CFStringRef CFDateFormatterGetFormat(CFDateFormatterRef formatter) {
1183      __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1184      return formatter->_format;
1185  }
1186  
1187  void CFDateFormatterSetFormat(CFDateFormatterRef formatter, CFStringRef formatString) {
1188      __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1189      __CFGenericValidateType(formatString, CFStringGetTypeID());
1190      formatString = __CFDateFormatterCreateForcedString(formatter, formatString);
1191      CFIndex cnt = CFStringGetLength(formatString);
1192      CFAssert1(cnt <= 1024, __kCFLogAssertion, "%s(): format string too long", __PRETTY_FUNCTION__);
1193      if (formatter->_format != formatString && cnt <= 1024) {
1194          // When going from a situation where there is no custom format already,
1195          // and the "relative date formatting" property is set, we need to reset
1196          // the whole UDateFormat.
1197          if (formatter->_property._HasCustomFormat != kCFBooleanTrue && formatter->_property._DoesRelativeDateFormatting == kCFBooleanTrue) {
1198              __ResetUDateFormat(formatter, true);
1199              // the "true" results in: if you set a custom format string, you don't get relative date formatting
1200          }
1201          STACK_BUFFER_DECL(UChar, ubuffer, cnt);
1202          const UChar *ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)formatString);
1203          if (NULL == ustr) {
1204              CFStringGetCharacters(formatString, CFRangeMake(0, cnt), (UniChar *)ubuffer);
1205              ustr = ubuffer;
1206          }
1207          UErrorCode status = U_ZERO_ERROR;
1208  //        __cficu_udat_applyPattern(formatter->_df, false, ustr, cnt, &status);
1209          __cficu_udat_applyPattern(formatter->_df, false, ustr, cnt);
1210          if (U_SUCCESS(status)) {
1211              if (formatter->_format) CFRelease(formatter->_format);
1212              formatter->_format = (CFStringRef)CFStringCreateCopy(CFGetAllocator(formatter), formatString);
1213              formatter->_property._HasCustomFormat = kCFBooleanTrue;
1214          }
1215      }
1216      if (formatString) CFRelease(formatString);
1217  }
1218  
1219  CFStringRef CFDateFormatterCreateStringWithDate(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFDateRef date) {
1220      if (allocator == NULL) allocator = __CFGetDefaultAllocator();
1221      __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
1222      __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1223      __CFGenericValidateType(date, CFDateGetTypeID());
1224      return CFDateFormatterCreateStringWithAbsoluteTime(allocator, formatter, CFDateGetAbsoluteTime(date));
1225  }
1226  
1227  CFStringRef CFDateFormatterCreateStringWithAbsoluteTime(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFAbsoluteTime at) {
1228      if (allocator == NULL) allocator = __CFGetDefaultAllocator();
1229      __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
1230      __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1231      UChar *ustr = NULL, ubuffer[BUFFER_SIZE + 1];
1232      UErrorCode status = U_ZERO_ERROR;
1233      CFIndex used, cnt = BUFFER_SIZE;
1234      UDate ud = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0 + 0.5;
1235      used = __cficu_udat_format(formatter->_df, ud, ubuffer + 1, cnt, NULL, &status);
1236      if (status == U_BUFFER_OVERFLOW_ERROR || cnt < used) {
1237          cnt = used + 1 + 1; // leave room for RTL marker if needed
1238          ustr = (UChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UChar) * cnt, 0);
1239          status = U_ZERO_ERROR;
1240          used = __cficu_udat_format(formatter->_df, ud, ustr + 1, cnt, NULL, &status);
1241      }
1242      CFStringRef string = NULL;
1243      if (U_SUCCESS(status)) {
1244          UniChar *bufferToUse = ustr ? (UniChar *)ustr : (UniChar *)ubuffer;
1245          if (formatter->_property._UsesCharacterDirection == kCFBooleanTrue && CFLocaleGetLanguageCharacterDirection(CFLocaleGetIdentifier(formatter->_locale)) == kCFLocaleLanguageDirectionRightToLeft) {
1246              // Insert Unicode RTL marker
1247              bufferToUse[0] = 0x200F;
1248              used++;
1249          } else {
1250              // Move past direction marker
1251              bufferToUse++;
1252          }
1253          string = CFStringCreateWithCharacters(allocator, bufferToUse, used);
1254      }
1255      if (ustr) CFAllocatorDeallocate(kCFAllocatorSystemDefault, ustr);
1256      return string;
1257  }
1258  
1259  static UDate __CFDateFormatterCorrectTimeWithTarget(UCalendar *calendar, UDate at, int32_t target, Boolean isEra, UErrorCode *status) {
1260      __cficu_ucal_setMillis(calendar, at, status);
1261      UCalendarDateFields field = isEra ? UCAL_ERA : UCAL_YEAR;
1262      __cficu_ucal_set(calendar, field, target);
1263      return __cficu_ucal_getMillis(calendar, status);
1264  }
1265  
1266  static UDate __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(UCalendar *calendar, UDate at, CFIndex period, CFIndex pastYears, CFIndex futureYears, Boolean isEra, UErrorCode *status) {
1267      __cficu_ucal_setMillis(calendar, __cficu_ucal_getNow(), status);
1268      int32_t currYear = __cficu_ucal_get(calendar, UCAL_YEAR, status);
1269      UCalendarDateFields field = isEra ? UCAL_ERA : UCAL_YEAR;
1270      int32_t currEraOrCentury = __cficu_ucal_get(calendar, field, status);
1271      if (!isEra) {
1272          currYear %= 100;
1273          currEraOrCentury = currEraOrCentury / 100 * 100; // get century
1274      }
1275  
1276      CFIndex futureMax = currYear + futureYears;
1277      CFIndex pastMin = currYear - pastYears;
1278  
1279      CFRange currRange, futureRange, pastRange;
1280      currRange.location = futureRange.location = pastRange.location = kCFNotFound;
1281      currRange.length = futureRange.length = pastRange.length = 0;
1282      if (!isEra) {
1283          if (period < INT_MAX && futureMax >= period) {
1284              futureRange.location = 0;
1285              futureRange.length = futureMax - period + 1;
1286          }
1287          if (pastMin < 0) {
1288              pastRange.location = period + pastMin;
1289              pastRange.length = period - pastRange.location;
1290          }
1291          if (pastRange.location != kCFNotFound) {
1292              currRange.location = 0;
1293          } else {
1294              currRange.location = pastMin;
1295          }
1296      } else {
1297          if (period < INT_MAX && futureMax > period) {
1298              futureRange.location = 1,
1299              futureRange.length = futureMax - period;
1300          }
1301          if (pastMin <= 0) {
1302              pastRange.location = period + pastMin;
1303              pastRange.length = period - pastRange.location + 1;
1304          }
1305          if (pastRange.location != kCFNotFound) {
1306              currRange.location = 1;
1307          } else {
1308              currRange.location = pastMin;
1309          }
1310  
1311      }
1312      currRange.length = period - pastRange.length - futureRange.length;
1313  
1314      __cficu_ucal_setMillis(calendar, at, status);
1315      int32_t atYear = __cficu_ucal_get(calendar, UCAL_YEAR, status);
1316      if (!isEra) {
1317          atYear %= 100;
1318          currEraOrCentury += atYear;
1319      }
1320  
1321      int32_t offset = 0; // current era or century
1322      if (pastRange.location != kCFNotFound && atYear >= pastRange.location && atYear - pastRange.location + 1 <= pastRange.length) {
1323          offset = -1; // past era or century
1324      } else if (futureRange.location != kCFNotFound && atYear >= futureRange.location && atYear - futureRange.location + 1 <= futureRange.length) {
1325          offset = 1; // next era or century
1326      }
1327      if (!isEra) offset *= 100;
1328      return __CFDateFormatterCorrectTimeWithTarget(calendar, at, currEraOrCentury+offset, isEra, status);
1329  }
1330  
1331  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
1332  static int32_t __CFDateFormatterGetMaxYearGivenJapaneseEra(UCalendar *calendar, int32_t era, UErrorCode *status) {
1333      int32_t years = 0;
1334      __cficu_ucal_clear(calendar);
1335      __cficu_ucal_set(calendar, UCAL_ERA, era+1);
1336      UDate target = __cficu_ucal_getMillis(calendar, status);
1337      __cficu_ucal_set(calendar, UCAL_ERA, era);
1338      years = __cficu_ucal_getFieldDifference(calendar, target, UCAL_YEAR, status);
1339      return years+1;
1340  }
1341  #endif
1342  
1343  static Boolean __CFDateFormatterHandleAmbiguousYear(CFDateFormatterRef formatter, CFStringRef calendar_id, UDateFormat *df, UCalendar *cal, UDate *at, const UChar *ustr, CFIndex length, UErrorCode *status) {
1344      Boolean success = true;
1345      int64_t ambigStrat = kCFDateFormatterAmbiguousYearAssumeToNone;
1346      if (formatter->_property._AmbiguousYearStrategy) {
1347          CFNumberGetValue(formatter->_property._AmbiguousYearStrategy, kCFNumberSInt64Type, &ambigStrat);
1348      }
1349      if (calendar_id == kCFCalendarIdentifierChinese) {
1350          // 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
1351          // now I just assume that the year will not be greater than 600 in the string
1352          if (__cficu_ucal_get(cal, UCAL_ERA, status) < 10) {
1353              switch (ambigStrat) {
1354                  case kCFDateFormatterAmbiguousYearFailToParse:
1355                      success = false;
1356                      break;
1357                  case kCFDateFormatterAmbiguousYearAssumeToCurrent: {
1358                      __cficu_ucal_setMillis(cal, __cficu_ucal_getNow(), status);
1359                      int32_t currEra = __cficu_ucal_get(cal, UCAL_ERA, status);
1360                      *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1361                      break;
1362                      }
1363                  case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate:
1364                      *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 29, 30, true, status);
1365                      break;
1366                  case kCFDateFormatterAmbiguousYearAssumeToFuture:
1367                      *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 0, 59, true, status);
1368                      break;
1369                  case kCFDateFormatterAmbiguousYearAssumeToPast:
1370                      *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 59, 0, true, status);
1371                      break;
1372                  case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture:
1373                      *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 10, 49, true, status);
1374                      break;
1375                  case kCFDateFormatterAmbiguousYearAssumeToLikelyPast:
1376                      *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 60, 49, 10, true, status);
1377                      break;
1378                  case kCFDateFormatterAmbiguousYearAssumeToNone:
1379                  default:
1380                      break; // do nothing
1381              }
1382          }
1383      } else if (calendar_id == kCFCalendarIdentifierJapanese) { // ??? need more work
1384  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
1385          __cficu_ucal_clear(cal);
1386          __cficu_ucal_set(cal, UCAL_ERA, 1);
1387          __cficu_udat_parseCalendar(df, cal, ustr, length, NULL, status);
1388          UDate test = __cficu_ucal_getMillis(cal, status);
1389          if (test != *at) { // missing era
1390              __cficu_ucal_setMillis(cal, *at, status);
1391              int32_t givenYear = __cficu_ucal_get(cal, UCAL_YEAR, status);
1392              __cficu_ucal_setMillis(cal, __cficu_ucal_getNow(), status);
1393              int32_t currYear = __cficu_ucal_get(cal, UCAL_YEAR, status);
1394              int32_t currEra = __cficu_ucal_get(cal, UCAL_ERA, status);
1395              switch (ambigStrat) {
1396                  case kCFDateFormatterAmbiguousYearFailToParse:
1397                      success = false;
1398                      break;
1399                  case kCFDateFormatterAmbiguousYearAssumeToCurrent:
1400                      *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1401                      break;
1402                  case kCFDateFormatterAmbiguousYearAssumeToFuture:
1403                      if (givenYear < currYear) { // we only consider current or the future
1404                          success = false;
1405                      } else { // current era
1406                          *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1407                      }
1408                      break;
1409                  case kCFDateFormatterAmbiguousYearAssumeToPast:
1410                      if (givenYear > currYear) { // past era
1411                          success = false;
1412                          // we find the closest era that has the given year
1413                          // if no era has such given year, we fail the parse
1414                          for (CFIndex era = currEra-1; era >= 234; era--) { // Showa era (234) is the longest era
1415                              int32_t years = __CFDateFormatterGetMaxYearGivenJapaneseEra(cal, era, status);
1416                              if (givenYear > years) {
1417                                  continue;
1418                              }
1419                              success = true;
1420                              *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, era, true, status);
1421                              break;
1422                          }
1423                      } else { // current era
1424                          *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1425                      }
1426                      break;
1427                  case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture:
1428                      if (givenYear < currYear - 10) { // we allow 10 years to the past
1429                          success = false;
1430                      } else {
1431                          *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1432                      }
1433                      break;
1434                  case kCFDateFormatterAmbiguousYearAssumeToLikelyPast:
1435                      if (givenYear > currYear + 10) {
1436                          success = false;
1437                          // we find the closest era that has the given year
1438                          // if no era has such given year, we fail the parse
1439                          for (CFIndex era = currEra-1; era >= 234; era--) { // Showa era (234) is the longest era
1440                              int32_t years = __CFDateFormatterGetMaxYearGivenJapaneseEra(cal, era, status);
1441                              if (givenYear > years) {
1442                                  continue;
1443                              }
1444                              success = true;
1445                              *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, era, true, status);
1446                              break;
1447                          }
1448                      } else { // current era
1449                          *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, currEra, true, status);
1450                      }
1451                      break;
1452                  case kCFDateFormatterAmbiguousYearAssumeToNone:
1453                  default:
1454                      break; // do nothing
1455              }
1456          }
1457  #else
1458          success = false;
1459  #endif
1460      } else { // calenders other than chinese and japanese
1461          int32_t parsedYear = __cficu_ucal_get(cal, UCAL_YEAR, status);
1462          if (parsedYear >= 12000 && parsedYear <= 12099) { // most likely that the parsed string had a 2-digits year
1463              if (formatter->_property._TwoDigitStartDate != NULL) {
1464                  UCalendar *tempCal = __cficu_ucal_clone(cal, status);
1465                  __cficu_ucal_clear(tempCal);
1466                  CFAbsoluteTime twoDigitAt = CFDateGetAbsoluteTime(formatter->_property._TwoDigitStartDate);
1467                  UDate targetUdate = (twoDigitAt + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
1468                  __cficu_ucal_setMillis(tempCal, targetUdate, status);
1469                  int targetYear = __cficu_ucal_get(tempCal, UCAL_YEAR, status);
1470                  parsedYear -= 12000;
1471                  int targetYearM100 = targetYear % 100;
1472                  if (targetYearM100 < parsedYear) {
1473                      parsedYear = ((targetYear / 100) * 100) + parsedYear;
1474                  } else if (parsedYear < targetYearM100) {
1475                      parsedYear = ((targetYear / 100) * 100) + 100 + parsedYear;
1476                  } else {
1477                      __cficu_ucal_set(cal, UCAL_YEAR, targetYear);
1478                      UDate parseUdate = __cficu_ucal_getMillis(cal, status);
1479                      if (parseUdate >= targetUdate) {
1480                          parsedYear = targetYear;
1481                      } else {
1482                          parsedYear = targetYear + 100;
1483                      }
1484                  }
1485                  __cficu_ucal_close(tempCal);
1486                  __cficu_ucal_set(cal, UCAL_YEAR, parsedYear);
1487                  *at = __cficu_ucal_getMillis(cal, status);
1488              } else {
1489                  switch (ambigStrat) {
1490                      case kCFDateFormatterAmbiguousYearFailToParse:
1491                          success = false;
1492                          break;
1493                      case kCFDateFormatterAmbiguousYearAssumeToCurrent:
1494                      {
1495                          // we can modify cal here because cal is just a temp cal from the caller
1496                          __cficu_ucal_setMillis(cal, __cficu_ucal_getNow(), status);
1497                          int32_t currYear = __cficu_ucal_get(cal, UCAL_YEAR, status);
1498                          *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, (currYear / 100 * 100) + parsedYear % 100, false, status);
1499                      }
1500                          break;
1501                      case kCFDateFormatterAmbiguousYearAssumeToCenteredAroundCurrentDate:
1502                          *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 50, 49, false, status);
1503                          break;
1504                      case kCFDateFormatterAmbiguousYearAssumeToFuture:
1505                          *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 0, 99, false, status);
1506                          break;
1507                      case kCFDateFormatterAmbiguousYearAssumeToPast:
1508                          *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 99, 0, false, status);
1509                          break;
1510                      case kCFDateFormatterAmbiguousYearAssumeToLikelyFuture:
1511                          *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 9, 90, false, status);
1512                          break;
1513                      case kCFDateFormatterAmbiguousYearAssumeToLikelyPast:
1514                          *at = __CFDateFormatterCorrectTimeToARangeAroundCurrentDate(cal, *at, 100, 90, 9, false, status);
1515                          break;
1516                      case kCFDateFormatterAmbiguousYearAssumeToNone:
1517                      default:
1518                          if (calendar_id == kCFCalendarIdentifierGregorian) { // historical default behavior of 1950 - 2049
1519                              int32_t twoDigits = parsedYear % 100;
1520                              *at = __CFDateFormatterCorrectTimeWithTarget(cal, *at, ((twoDigits < 50) ? 2000 : 1900) + twoDigits, false, status);
1521                          }
1522                          break; // do nothing
1523                  }
1524              }
1525  
1526          }
1527      }
1528      return success;
1529  }
1530  
1531  CFDateRef CFDateFormatterCreateDateFromString(CFAllocatorRef allocator, CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep) {
1532      if (allocator == NULL) allocator = __CFGetDefaultAllocator();
1533      __CFGenericValidateType(allocator, CFAllocatorGetTypeID());
1534      __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1535      __CFGenericValidateType(string, CFStringGetTypeID());
1536      CFAbsoluteTime at;
1537      if (CFDateFormatterGetAbsoluteTimeFromString(formatter, string, rangep, &at)) {
1538          return CFDateCreate(allocator, at);
1539      }
1540      return NULL;
1541  }
1542  
1543  Boolean CFDateFormatterGetAbsoluteTimeFromString(CFDateFormatterRef formatter, CFStringRef string, CFRange *rangep, CFAbsoluteTime *atp) {
1544      __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1545      __CFGenericValidateType(string, CFStringGetTypeID());
1546      CFRange range = {0, 0};
1547      if (rangep) {
1548         range = *rangep;
1549      } else {
1550          range.length = CFStringGetLength(string);
1551      }
1552      if (1024 < range.length) range.length = 1024;
1553      const UChar *ustr = (UChar *)CFStringGetCharactersPtr(string);
1554      STACK_BUFFER_DECL(UChar, ubuffer, (NULL == ustr) ? range.length : 1);
1555      if (NULL == ustr) {
1556          CFStringGetCharacters(string, range, (UniChar *)ubuffer);
1557          ustr = ubuffer;
1558      } else {
1559          ustr += range.location;
1560      }
1561      UDate udate;
1562      int32_t dpos = 0;
1563      UErrorCode status = U_ZERO_ERROR;
1564      UDateFormat *df2 = __cficu_udat_clone(formatter->_df, &status);
1565      const UCalendar *ucal2 = __cficu_udat_getCalendar(df2);
1566      UCalendar *cal2 = __cficu_ucal_clone(ucal2, &status);
1567      CFStringRef calendar_id = (CFStringRef) CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarIdentifierKey);
1568      if (calendar_id != kCFCalendarIdentifierChinese && calendar_id != kCFCalendarIdentifierJapanese) {
1569          __cficu_ucal_clear(cal2);
1570          // set both year, and 2DigitYearStart to year 12000
1571          __cficu_ucal_set(cal2, UCAL_YEAR, 12000);
1572          __cficu_udat_set2DigitYearStart(df2, 316516204800.0 * 1000.0, &status);
1573      } else if (calendar_id == kCFCalendarIdentifierChinese) {
1574          __cficu_ucal_clear(cal2);
1575          __cficu_ucal_set(cal2, UCAL_ERA, 1); // default to era 1 if no era info in the string for chinese
1576      } else if (calendar_id == kCFCalendarIdentifierJapanese) { // default to the current era
1577          __cficu_ucal_setMillis(cal2, __cficu_ucal_getNow(), &status);
1578          int32_t currEra = __cficu_ucal_get(cal2, UCAL_ERA, &status);
1579          __cficu_ucal_clear(cal2);
1580          __cficu_ucal_set(cal2, UCAL_ERA, currEra);
1581      }
1582      if (formatter->_property._DefaultDate) {
1583          CFAbsoluteTime at = CFDateGetAbsoluteTime(formatter->_property._DefaultDate);
1584          udate = (at + kCFAbsoluteTimeIntervalSince1970) * 1000.0;
1585          __cficu_ucal_setMillis(cal2, udate, &status);
1586      }
1587      __cficu_udat_parseCalendar(df2, cal2, ustr, range.length, &dpos, &status);
1588      udate = __cficu_ucal_getMillis(cal2, &status);
1589      if (rangep) rangep->length = dpos;
1590      Boolean success = false;
1591      // first status check is for parsing and the second status check is for the work done inside __CFDateFormatterHandleAmbiguousYear()
1592      if (!U_FAILURE(status) && (__CFDateFormatterHandleAmbiguousYear(formatter, calendar_id, df2, cal2, &udate, ustr, range.length, &status)) && !U_FAILURE(status)) {
1593          if (atp) {
1594              *atp = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970;
1595          }
1596          success = true;
1597      }
1598      CFRelease(calendar_id);
1599      __cficu_udat_close(df2);
1600      __cficu_ucal_close(cal2);
1601      return success;
1602  }
1603  
1604  static void __CFDateFormatterSetSymbolsArray(UDateFormat *icudf, int32_t icucode, int index_base, CFTypeRef value) {
1605      UErrorCode status = U_ZERO_ERROR;
1606      __CFGenericValidateType(value, CFArrayGetTypeID());
1607      CFArrayRef array = (CFArrayRef)value;
1608      CFIndex idx, cnt = CFArrayGetCount(array);
1609      for (idx = 0; idx < cnt; idx++) {
1610  	CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(array, idx);
1611  	__CFGenericValidateType(item, CFStringGetTypeID());
1612  	CFIndex item_cnt = CFStringGetLength(item);
1613  	STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt));
1614  	UChar *item_ustr = (UChar *)CFStringGetCharactersPtr(item);
1615  	if (NULL == item_ustr) {
1616  	    item_cnt = __CFMin(BUFFER_SIZE, item_cnt);
1617  	    CFStringGetCharacters(item, CFRangeMake(0, item_cnt), (UniChar *)item_buffer);
1618  	    item_ustr = item_buffer;
1619  	}
1620  	status = U_ZERO_ERROR;
1621  	__cficu_udat_setSymbols(icudf, (UDateFormatSymbolType)icucode, idx + index_base, item_ustr, item_cnt, &status);
1622      }
1623  }
1624  
1625  static CFArrayRef __CFDateFormatterGetSymbolsArray(UDateFormat *icudf, int32_t icucode, int index_base) {
1626      UErrorCode status = U_ZERO_ERROR;
1627      CFIndex idx, cnt = __cficu_udat_countSymbols(icudf, (UDateFormatSymbolType)icucode);
1628      if (cnt <= index_base) return CFArrayCreate(kCFAllocatorSystemDefault, NULL, 0, &kCFTypeArrayCallBacks);
1629      cnt = cnt - index_base;
1630      STACK_BUFFER_DECL(CFStringRef, strings, cnt);
1631      for (idx = 0; idx < cnt; idx++) {
1632          UChar ubuffer[BUFFER_SIZE];
1633  	CFStringRef str = NULL;
1634  	status = U_ZERO_ERROR;
1635  	CFIndex ucnt = __cficu_udat_getSymbols(icudf, (UDateFormatSymbolType)icucode, idx + index_base, ubuffer, BUFFER_SIZE, &status);
1636  	if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1637  	    str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)ubuffer, ucnt);
1638  	}
1639  	strings[idx] = !str ? (CFStringRef)CFRetain(CFSTR("<error>")) : str;
1640      }
1641      CFArrayRef array = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)strings, cnt, &kCFTypeArrayCallBacks);
1642      while (cnt--) {
1643  	CFRelease(strings[cnt]);
1644      }
1645      return array;
1646  }
1647  
1648  #define SET_SYMBOLS_ARRAY(A, B, C) \
1649  	if (!directToICU) { \
1650  	    oldProperty = formatter->_property. C; \
1651  	    formatter->_property. C = NULL; \
1652  	} \
1653          __CFDateFormatterSetSymbolsArray(formatter->_df, A, B, value); \
1654  	if (!directToICU) { \
1655  	    formatter->_property. C = __CFDateFormatterGetSymbolsArray(formatter->_df, A, B); \
1656  	}
1657  
1658  static void __CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value, Boolean directToICU) {
1659      __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1660      __CFGenericValidateType(key, CFStringGetTypeID());
1661      CFTypeRef oldProperty = NULL;
1662      UErrorCode status = U_ZERO_ERROR;
1663  
1664      if (kCFDateFormatterIsLenientKey == key) {
1665  	if (!directToICU) {
1666  	    oldProperty = formatter->_property. _IsLenient;
1667              formatter->_property. _IsLenient = NULL;
1668  	}
1669          __CFGenericValidateType(value, CFBooleanGetTypeID());
1670          if (!directToICU) {
1671              formatter->_property. _IsLenient = value ? (CFBooleanRef)CFRetain(value) : NULL;
1672              __ResetUDateFormat(formatter, false);
1673          }
1674      } else if (kCFDateFormatterDoesRelativeDateFormattingKey == key) {
1675  	if (!directToICU) {
1676  	    oldProperty = formatter->_property. _DoesRelativeDateFormatting;
1677              formatter->_property. _DoesRelativeDateFormatting = NULL;
1678  	}
1679          __CFGenericValidateType(value, CFBooleanGetTypeID());
1680  	if (!directToICU) {
1681  	    if (kCFBooleanTrue != value) value = kCFBooleanFalse;
1682              formatter->_property. _DoesRelativeDateFormatting = value ? (CFBooleanRef)CFRetain(value) : NULL;
1683  	    __ResetUDateFormat(formatter, false);
1684  	}
1685      } else if (kCFDateFormatterCalendarKey == key) {
1686  	if (!directToICU) {
1687  	    oldProperty = formatter->_property. _Calendar;
1688              formatter->_property. _Calendar = NULL;
1689  	}
1690          __CFGenericValidateType(value, CFCalendarGetTypeID());
1691          CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale);
1692          CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeName);
1693          CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components);
1694          CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifierKey, CFCalendarGetIdentifier((CFCalendarRef)value));
1695          localeName = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents);
1696          CFRelease(mcomponents);
1697          CFRelease(components);
1698          CFLocaleRef newLocale = CFLocaleCreate(CFGetAllocator(formatter->_locale), localeName);
1699          // at this point, we should be setting the preferences if any into this new locale
1700          CFRelease(localeName);
1701          CFRelease(formatter->_locale);
1702          formatter->_locale = newLocale;
1703          if (!directToICU) {
1704              formatter->_property. _Calendar = (CFCalendarRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarKey);
1705              __ResetUDateFormat(formatter, false);
1706          }
1707      } else if (kCFDateFormatterCalendarIdentifierKey == key) {
1708  	if (!directToICU) {
1709  	    oldProperty = formatter->_property. _CalendarName;
1710              formatter->_property. _CalendarName = NULL;
1711  	}
1712          __CFGenericValidateType(value, CFStringGetTypeID());
1713          CFStringRef localeName = CFLocaleGetIdentifier(formatter->_locale);
1714          CFDictionaryRef components = CFLocaleCreateComponentsFromLocaleIdentifier(kCFAllocatorSystemDefault, localeName);
1715          CFMutableDictionaryRef mcomponents = CFDictionaryCreateMutableCopy(kCFAllocatorSystemDefault, 0, components);
1716          CFDictionarySetValue(mcomponents, kCFLocaleCalendarIdentifierKey, value);
1717          localeName = CFLocaleCreateLocaleIdentifierFromComponents(kCFAllocatorSystemDefault, mcomponents);
1718          CFRelease(mcomponents);
1719          CFRelease(components);
1720          CFLocaleRef newLocale = CFLocaleCreate(CFGetAllocator(formatter->_locale), localeName);
1721          // at this point, we should be setting the preferences if any into this new locale
1722          CFRelease(localeName);
1723          CFRelease(formatter->_locale);
1724          formatter->_locale = newLocale;
1725          if (!directToICU) {
1726              formatter->_property. _CalendarName = (CFStringRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterCalendarIdentifierKey);
1727              __ResetUDateFormat(formatter, false);
1728          }
1729      } else if (kCFDateFormatterTimeZoneKey == key) {
1730  	if (formatter->_property. _TimeZone != value) {
1731  	    if (!directToICU) {
1732  		oldProperty = formatter->_property. _TimeZone;
1733  		formatter->_property. _TimeZone = NULL;
1734  	    }
1735  	    __CFGenericValidateType(value, CFTimeZoneGetTypeID());
1736  	    CFTimeZoneRef old = formatter->_property._TimeZone;
1737  	    formatter->_property._TimeZone = value ? (CFTimeZoneRef)CFRetain(value) : CFTimeZoneCopyDefault();
1738  	    if (old) CFRelease(old);
1739          if (!directToICU) {
1740              old = formatter->_property._TimeZone;
1741              formatter->_property. _TimeZone = (CFTimeZoneRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterTimeZoneKey);
1742              __ResetUDateFormat(formatter, false);
1743              if (old) CFRelease(old);
1744          }
1745  	}
1746      } else if (kCFDateFormatterDefaultFormatKey == key) {
1747          // read-only attribute
1748      } else if (kCFDateFormatterTwoDigitStartDateKey == key) {
1749  	if (!directToICU) {
1750  	    oldProperty = formatter->_property. _TwoDigitStartDate;
1751              formatter->_property. _TwoDigitStartDate = NULL;
1752  	}
1753          __CFGenericValidateType(value, CFDateGetTypeID());
1754  	if (!directToICU) {
1755              formatter->_property. _TwoDigitStartDate = value ? (CFDateRef)CFRetain(value) : NULL;
1756  	}
1757      } else if (kCFDateFormatterDefaultDateKey == key) {
1758  	if (!directToICU) {
1759  	    oldProperty = formatter->_property. _DefaultDate;
1760              formatter->_property. _DefaultDate = NULL;
1761  	}
1762          __CFGenericValidateType(value, CFDateGetTypeID());
1763  	if (!directToICU) {
1764              formatter->_property._DefaultDate = value ? (CFDateRef)CFRetain(value) : NULL;
1765  	}
1766      } else if (kCFDateFormatterGregorianStartDateKey == key) {
1767  	if (!directToICU) {
1768  	    oldProperty = formatter->_property. _GregorianStartDate;
1769              formatter->_property. _GregorianStartDate = NULL;
1770  	}
1771          __CFGenericValidateType(value, CFDateGetTypeID());
1772          if (!directToICU) {
1773              formatter->_property. _GregorianStartDate = value ? (CFDateRef)CFRetain(value) : NULL;
1774              __ResetUDateFormat(formatter, false);
1775          }
1776      } else if (kCFDateFormatterEraSymbolsKey == key) {
1777         SET_SYMBOLS_ARRAY(UDAT_ERAS, 0, _EraSymbols)
1778      } else if (kCFDateFormatterLongEraSymbolsKey == key) {
1779          SET_SYMBOLS_ARRAY(UDAT_ERA_NAMES, 0, _LongEraSymbols)
1780      } else if (kCFDateFormatterMonthSymbolsKey == key) {
1781          SET_SYMBOLS_ARRAY(UDAT_MONTHS, 0, _MonthSymbols)
1782      } else if (kCFDateFormatterShortMonthSymbolsKey == key) {
1783          SET_SYMBOLS_ARRAY(UDAT_SHORT_MONTHS, 0, _ShortMonthSymbols)
1784      } else if (kCFDateFormatterVeryShortMonthSymbolsKey == key) {
1785          SET_SYMBOLS_ARRAY(UDAT_NARROW_MONTHS, 0, _VeryShortMonthSymbols)
1786      } else if (kCFDateFormatterStandaloneMonthSymbolsKey == key) {
1787          SET_SYMBOLS_ARRAY(UDAT_STANDALONE_MONTHS, 0, _StandaloneMonthSymbols)
1788      } else if (kCFDateFormatterShortStandaloneMonthSymbolsKey == key) {
1789          SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_MONTHS, 0, _ShortStandaloneMonthSymbols)
1790      } else if (kCFDateFormatterVeryShortStandaloneMonthSymbolsKey == key) {
1791          SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_MONTHS, 0, _VeryShortStandaloneMonthSymbols)
1792      } else if (kCFDateFormatterWeekdaySymbolsKey == key) {
1793          SET_SYMBOLS_ARRAY(UDAT_WEEKDAYS, 1, _WeekdaySymbols)
1794      } else if (kCFDateFormatterShortWeekdaySymbolsKey == key) {
1795          SET_SYMBOLS_ARRAY(UDAT_SHORT_WEEKDAYS, 1, _ShortWeekdaySymbols)
1796      } else if (kCFDateFormatterVeryShortWeekdaySymbolsKey == key) {
1797          SET_SYMBOLS_ARRAY(UDAT_NARROW_WEEKDAYS, 1, _VeryShortWeekdaySymbols)
1798      } else if (kCFDateFormatterStandaloneWeekdaySymbolsKey == key) {
1799          SET_SYMBOLS_ARRAY(UDAT_STANDALONE_WEEKDAYS, 1, _StandaloneWeekdaySymbols)
1800      } else if (kCFDateFormatterShortStandaloneWeekdaySymbolsKey == key) {
1801          SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_WEEKDAYS, 1, _ShortStandaloneWeekdaySymbols)
1802      } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey == key) {
1803          SET_SYMBOLS_ARRAY(UDAT_STANDALONE_NARROW_WEEKDAYS, 1, _VeryShortStandaloneWeekdaySymbols)
1804      } else if (kCFDateFormatterQuarterSymbolsKey == key) {
1805          SET_SYMBOLS_ARRAY(UDAT_QUARTERS, 0, _QuarterSymbols)
1806      } else if (kCFDateFormatterShortQuarterSymbolsKey == key) {
1807          SET_SYMBOLS_ARRAY(UDAT_SHORT_QUARTERS, 0, _ShortQuarterSymbols)
1808      } else if (kCFDateFormatterStandaloneQuarterSymbolsKey == key) {
1809          SET_SYMBOLS_ARRAY(UDAT_STANDALONE_QUARTERS, 0, _StandaloneQuarterSymbols)
1810      } else if (kCFDateFormatterShortStandaloneQuarterSymbolsKey == key) {
1811          SET_SYMBOLS_ARRAY(UDAT_STANDALONE_SHORT_QUARTERS, 0, _ShortStandaloneQuarterSymbols)
1812      } else if (kCFDateFormatterAMSymbolKey == key) {
1813  	if (!directToICU) {
1814  	    oldProperty = formatter->_property. _AMSymbol;
1815              formatter->_property. _AMSymbol = NULL;
1816  	}
1817          __CFGenericValidateType(value, CFStringGetTypeID());
1818          CFIndex item_cnt = CFStringGetLength((CFStringRef)value);
1819          STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt));
1820          UChar *item_ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)value);
1821          if (NULL == item_ustr) {
1822              item_cnt = __CFMin(BUFFER_SIZE, item_cnt);
1823              CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, item_cnt), (UniChar *)item_buffer);
1824              item_ustr = item_buffer;
1825          }
1826          __cficu_udat_setSymbols(formatter->_df, UDAT_AM_PMS, 0, item_ustr, item_cnt, &status);
1827  	if (!directToICU) {
1828              formatter->_property. _AMSymbol = value ? (CFStringRef)CFStringCreateCopy(NULL, value) : NULL;
1829  	}
1830      } else if (kCFDateFormatterPMSymbolKey == key) {
1831  	if (!directToICU) {
1832  	    oldProperty = formatter->_property. _PMSymbol;
1833              formatter->_property. _PMSymbol = NULL;
1834  	}
1835          __CFGenericValidateType(value, CFStringGetTypeID());
1836          CFIndex item_cnt = CFStringGetLength((CFStringRef)value);
1837          STACK_BUFFER_DECL(UChar, item_buffer, __CFMin(BUFFER_SIZE, item_cnt));
1838          UChar *item_ustr = (UChar *)CFStringGetCharactersPtr((CFStringRef)value);
1839          if (NULL == item_ustr) {
1840              item_cnt = __CFMin(BUFFER_SIZE, item_cnt);
1841              CFStringGetCharacters((CFStringRef)value, CFRangeMake(0, item_cnt), (UniChar *)item_buffer);
1842              item_ustr = item_buffer;
1843          }
1844          __cficu_udat_setSymbols(formatter->_df, UDAT_AM_PMS, 1, item_ustr, item_cnt, &status);
1845  	if (!directToICU) {
1846              formatter->_property. _PMSymbol = value ? (CFStringRef)CFStringCreateCopy(NULL, value) : NULL;
1847  	}
1848      } else if (kCFDateFormatterAmbiguousYearStrategyKey == key) {
1849          oldProperty = formatter->_property._AmbiguousYearStrategy;
1850          formatter->_property._AmbiguousYearStrategy = NULL;
1851          __CFGenericValidateType(value, CFNumberGetTypeID());
1852          formatter->_property._AmbiguousYearStrategy = (CFNumberRef)CFRetain(value);
1853      } else if (kCFDateFormatterUsesCharacterDirectionKey == key) {
1854          __CFGenericValidateType(value, CFBooleanGetTypeID());
1855          oldProperty = formatter->_property._UsesCharacterDirection;
1856          formatter->_property._UsesCharacterDirection = (CFBooleanRef)CFRetain(value);
1857      } else if (CFEqual(key, kCFDateFormatterFormattingContextKey)) {
1858          if (!directToICU) {
1859              oldProperty = formatter->_property. _FormattingContext;
1860              formatter->_property._FormattingContext = NULL;
1861          }
1862          __CFGenericValidateType(value, CFNumberGetTypeID());
1863          int context = 0;
1864          CFNumberGetValue(value, kCFNumberIntType, &context);
1865          __cficu_udat_setContext(formatter->_df, context, &status);
1866          if (!directToICU) {
1867              formatter->_property._FormattingContext = (CFNumberRef)CFRetain(value);
1868          }
1869      } else {
1870          CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key);
1871      }
1872      if (oldProperty) CFRelease(oldProperty);
1873  }
1874  
1875  void CFDateFormatterSetProperty(CFDateFormatterRef formatter, CFStringRef key, CFTypeRef value) {
1876      __CFDateFormatterSetProperty(formatter, key, value, false);
1877  }
1878  
1879  CFTypeRef CFDateFormatterCopyProperty(CFDateFormatterRef formatter, CFStringRef key) {
1880      __CFGenericValidateType(formatter, CFDateFormatterGetTypeID());
1881      __CFGenericValidateType(key, CFStringGetTypeID());
1882      UErrorCode status = U_ZERO_ERROR;
1883      UChar ubuffer[BUFFER_SIZE];
1884  
1885      if (kCFDateFormatterIsLenientKey == key) {
1886  	if (formatter->_property._IsLenient) return CFRetain(formatter->_property._IsLenient);
1887          return CFRetain(__cficu_udat_isLenient(formatter->_df) ? kCFBooleanTrue : kCFBooleanFalse);
1888      } else if (kCFDateFormatterDoesRelativeDateFormattingKey == key) {
1889  	if (formatter->_property._DoesRelativeDateFormatting) return CFRetain(formatter->_property._DoesRelativeDateFormatting);
1890          return CFRetain(kCFBooleanFalse);
1891      } else if (kCFDateFormatterCalendarKey == key) {
1892  	if (formatter->_property._Calendar) return CFRetain(formatter->_property._Calendar);
1893          CFCalendarRef calendar = (CFCalendarRef)CFLocaleGetValue(formatter->_locale, kCFLocaleCalendarKey);
1894          return calendar ? CFRetain(calendar) : NULL;
1895      } else if (kCFDateFormatterCalendarIdentifierKey == key) {
1896  	if (formatter->_property._CalendarName) return CFRetain(formatter->_property._CalendarName);
1897          CFStringRef ident = (CFStringRef)CFLocaleGetValue(formatter->_locale, kCFLocaleCalendarIdentifierKey);
1898          return ident ? CFRetain(ident) : NULL;
1899      } else if (kCFDateFormatterTimeZoneKey == key) {
1900          return formatter->_property._TimeZone ? CFRetain(formatter->_property._TimeZone) : NULL;
1901      } else if (kCFDateFormatterDefaultFormatKey == key) {
1902          return formatter->_defformat ? CFRetain(formatter->_defformat) : NULL;
1903      } else if (kCFDateFormatterTwoDigitStartDateKey == key) {
1904          return formatter->_property._TwoDigitStartDate ? CFRetain(formatter->_property._TwoDigitStartDate) : NULL;
1905      } else if (kCFDateFormatterDefaultDateKey == key) {
1906          return formatter->_property._DefaultDate ? CFRetain(formatter->_property._DefaultDate) : NULL;
1907      } else if (kCFDateFormatterGregorianStartDateKey == key) {
1908  	if (formatter->_property._GregorianStartDate) return CFRetain(formatter->_property._GregorianStartDate);
1909          const UCalendar *cal = __cficu_udat_getCalendar(formatter->_df);
1910          UDate udate = __cficu_ucal_getGregorianChange(cal, &status);
1911          if (U_SUCCESS(status)) {
1912              CFAbsoluteTime at = (double)udate / 1000.0 - kCFAbsoluteTimeIntervalSince1970;
1913              return CFDateCreate(CFGetAllocator(formatter), at);
1914          }
1915      } else if (kCFDateFormatterEraSymbolsKey == key) {
1916  	if (formatter->_property._EraSymbols) return CFRetain(formatter->_property._EraSymbols);
1917          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_ERAS, 0);
1918      } else if (kCFDateFormatterLongEraSymbolsKey == key) {
1919  	if (formatter->_property._LongEraSymbols) return CFRetain(formatter->_property._LongEraSymbols);
1920          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_ERA_NAMES, 0);
1921      } else if (kCFDateFormatterMonthSymbolsKey == key) {
1922  	if (formatter->_property._MonthSymbols) return CFRetain(formatter->_property._MonthSymbols);
1923          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_MONTHS, 0);
1924      } else if (kCFDateFormatterShortMonthSymbolsKey == key) {
1925  	if (formatter->_property._ShortMonthSymbols) return CFRetain(formatter->_property._ShortMonthSymbols);
1926          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_SHORT_MONTHS, 0);
1927      } else if (kCFDateFormatterVeryShortMonthSymbolsKey == key) {
1928  	if (formatter->_property._VeryShortMonthSymbols) return CFRetain(formatter->_property._VeryShortMonthSymbols);
1929          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_NARROW_MONTHS, 0);
1930      } else if (kCFDateFormatterStandaloneMonthSymbolsKey == key) {
1931  	if (formatter->_property._StandaloneMonthSymbols) return CFRetain(formatter->_property._StandaloneMonthSymbols);
1932          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_MONTHS, 0);
1933      } else if (kCFDateFormatterShortStandaloneMonthSymbolsKey == key) {
1934  	if (formatter->_property._ShortStandaloneMonthSymbols) return CFRetain(formatter->_property._ShortStandaloneMonthSymbols);
1935          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_SHORT_MONTHS, 0);
1936      } else if (kCFDateFormatterVeryShortStandaloneMonthSymbolsKey == key) {
1937  	if (formatter->_property._VeryShortStandaloneMonthSymbols) return CFRetain(formatter->_property._VeryShortStandaloneMonthSymbols);
1938          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_NARROW_MONTHS, 0);
1939      } else if (kCFDateFormatterWeekdaySymbolsKey == key) {
1940  	if (formatter->_property._WeekdaySymbols) return CFRetain(formatter->_property._WeekdaySymbols);
1941          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_WEEKDAYS, 1);
1942      } else if (kCFDateFormatterShortWeekdaySymbolsKey == key) {
1943  	if (formatter->_property._ShortWeekdaySymbols) return CFRetain(formatter->_property._ShortWeekdaySymbols);
1944          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_SHORT_WEEKDAYS, 1);
1945      } else if (kCFDateFormatterVeryShortWeekdaySymbolsKey == key) {
1946  	if (formatter->_property._VeryShortWeekdaySymbols) return CFRetain(formatter->_property._VeryShortWeekdaySymbols);
1947          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_NARROW_WEEKDAYS, 1);
1948      } else if (kCFDateFormatterStandaloneWeekdaySymbolsKey == key) {
1949  	if (formatter->_property._StandaloneWeekdaySymbols) return CFRetain(formatter->_property._StandaloneWeekdaySymbols);
1950          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_WEEKDAYS, 1);
1951      } else if (kCFDateFormatterShortStandaloneWeekdaySymbolsKey == key) {
1952  	if (formatter->_property._ShortStandaloneWeekdaySymbols) return CFRetain(formatter->_property._ShortStandaloneWeekdaySymbols);
1953          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_SHORT_WEEKDAYS, 1);
1954      } else if (kCFDateFormatterVeryShortStandaloneWeekdaySymbolsKey == key) {
1955  	if (formatter->_property._VeryShortStandaloneWeekdaySymbols) return CFRetain(formatter->_property._VeryShortStandaloneWeekdaySymbols);
1956          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_NARROW_WEEKDAYS, 1);
1957      } else if (kCFDateFormatterQuarterSymbolsKey == key) {
1958  	if (formatter->_property._QuarterSymbols) return CFRetain(formatter->_property._QuarterSymbols);
1959          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_QUARTERS, 0);
1960      } else if (kCFDateFormatterShortQuarterSymbolsKey == key) {
1961  	if (formatter->_property._ShortQuarterSymbols) return CFRetain(formatter->_property._ShortQuarterSymbols);
1962          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_SHORT_QUARTERS, 0);
1963      } else if (kCFDateFormatterStandaloneQuarterSymbolsKey == key) {
1964  	if (formatter->_property._StandaloneQuarterSymbols) return CFRetain(formatter->_property._StandaloneQuarterSymbols);
1965          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_QUARTERS, 0);
1966      } else if (kCFDateFormatterShortStandaloneQuarterSymbolsKey == key) {
1967  	if (formatter->_property._ShortStandaloneQuarterSymbols) return CFRetain(formatter->_property._ShortStandaloneQuarterSymbols);
1968          return __CFDateFormatterGetSymbolsArray(formatter->_df, UDAT_STANDALONE_SHORT_QUARTERS, 0);
1969      } else if (kCFDateFormatterAMSymbolKey == key) {
1970  	if (formatter->_property._AMSymbol) return CFRetain(formatter->_property._AMSymbol);
1971          CFIndex cnt = __cficu_udat_countSymbols(formatter->_df, UDAT_AM_PMS);
1972          if (2 <= cnt) {
1973              CFIndex ucnt = __cficu_udat_getSymbols(formatter->_df, UDAT_AM_PMS, 0, ubuffer, BUFFER_SIZE, &status);
1974              if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1975                  return CFStringCreateWithCharacters(CFGetAllocator(formatter), (UniChar *)ubuffer, ucnt);
1976              }
1977          }
1978      } else if (kCFDateFormatterPMSymbolKey == key) {
1979  	if (formatter->_property._PMSymbol) return CFRetain(formatter->_property._PMSymbol);
1980          CFIndex cnt = __cficu_udat_countSymbols(formatter->_df, UDAT_AM_PMS);
1981          if (2 <= cnt) {
1982              CFIndex ucnt = __cficu_udat_getSymbols(formatter->_df, UDAT_AM_PMS, 1, ubuffer, BUFFER_SIZE, &status);
1983              if (U_SUCCESS(status) && cnt <= BUFFER_SIZE) {
1984                  return CFStringCreateWithCharacters(CFGetAllocator(formatter), (UniChar *)ubuffer, ucnt);
1985              }
1986          }
1987      } else if (kCFDateFormatterAmbiguousYearStrategyKey == key) {
1988          if (formatter->_property._AmbiguousYearStrategy) return CFRetain(formatter->_property._AmbiguousYearStrategy);
1989      } else if (kCFDateFormatterUsesCharacterDirectionKey == key) {
1990          return formatter->_property._UsesCharacterDirection ? CFRetain(formatter->_property._UsesCharacterDirection) : CFRetain(kCFBooleanFalse);
1991      } else if (CFEqual(key, kCFDateFormatterFormattingContextKey)) {
1992          if (formatter->_property._FormattingContext) return CFRetain(formatter->_property._FormattingContext);
1993          int value = __cficu_udat_getContext(formatter->_df, UDISPCTX_TYPE_CAPITALIZATION, &status);
1994          return CFNumberCreate(CFGetAllocator(formatter), kCFNumberIntType, (const void *)&value);
1995      } else {
1996          CFAssert3(0, __kCFLogAssertion, "%s(): unknown key %p (%@)", __PRETTY_FUNCTION__, key, key);
1997      }
1998      return NULL;
1999  }
2000  
2001