|
| 1 | +// |
| 2 | +// Copyright (c) Microsoft. All rights reserved. |
| 3 | +// Licensed under the MIT license. See LICENSE file in the project root for full license information. |
| 4 | +// |
| 5 | + |
| 6 | +#include <assert.h> |
| 7 | + |
| 8 | +#include "locale.hpp" |
| 9 | + |
| 10 | +#include <unicode/dtfmtsym.h> |
| 11 | +#include <unicode/dtptngen.h> |
| 12 | +#include <unicode/locdspnm.h> |
| 13 | + |
| 14 | +/* |
| 15 | +* These values should be kept in sync with System.Globalization.CalendarId |
| 16 | +*/ |
| 17 | +enum CalendarId : int32_t |
| 18 | +{ |
| 19 | + UNINITIALIZED_VALUE = 0, |
| 20 | + GREGORIAN = 1, // Gregorian (localized) calendar |
| 21 | + GREGORIAN_US = 2, // Gregorian (U.S.) calendar |
| 22 | + JAPAN = 3, // Japanese Emperor Era calendar |
| 23 | + /* SSS_WARNINGS_OFF */ |
| 24 | + TAIWAN = 4, // Taiwan Era calendar /* SSS_WARNINGS_ON */ |
| 25 | + KOREA = 5, // Korean Tangun Era calendar |
| 26 | + HIJRI = 6, // Hijri (Arabic Lunar) calendar |
| 27 | + THAI = 7, // Thai calendar |
| 28 | + HEBREW = 8, // Hebrew (Lunar) calendar |
| 29 | + GREGORIAN_ME_FRENCH = 9, // Gregorian Middle East French calendar |
| 30 | + GREGORIAN_ARABIC = 10, // Gregorian Arabic calendar |
| 31 | + GREGORIAN_XLIT_ENGLISH = 11, // Gregorian Transliterated English calendar |
| 32 | + GREGORIAN_XLIT_FRENCH = 12, |
| 33 | + // Note that all calendars after this point are MANAGED ONLY for now. |
| 34 | + JULIAN = 13, |
| 35 | + JAPANESELUNISOLAR = 14, |
| 36 | + CHINESELUNISOLAR = 15, |
| 37 | + SAKA = 16, // reserved to match Office but not implemented in our code |
| 38 | + LUNAR_ETO_CHN = 17, // reserved to match Office but not implemented in our code |
| 39 | + LUNAR_ETO_KOR = 18, // reserved to match Office but not implemented in our code |
| 40 | + LUNAR_ETO_ROKUYOU = 19, // reserved to match Office but not implemented in our code |
| 41 | + KOREANLUNISOLAR = 20, |
| 42 | + TAIWANLUNISOLAR = 21, |
| 43 | + PERSIAN = 22, |
| 44 | + UMALQURA = 23, |
| 45 | + LAST_CALENDAR = 23 // Last calendar ID |
| 46 | +}; |
| 47 | + |
| 48 | +/* |
| 49 | +* These values should be kept in sync with System.Globalization.CalendarDataType |
| 50 | +*/ |
| 51 | +enum CalendarDataType : int32_t |
| 52 | +{ |
| 53 | + Uninitialized = 0, |
| 54 | + NativeName = 1, |
| 55 | + MonthDay = 2, |
| 56 | + ShortDates = 3, |
| 57 | + LongDates = 4, |
| 58 | + YearMonths = 5, |
| 59 | + DayNames = 6, |
| 60 | + AbbrevDayNames = 7, |
| 61 | + MonthNames = 8, |
| 62 | + AbbrevMonthNames = 9, |
| 63 | + SuperShortDayNames = 10, |
| 64 | + MonthGenitiveNames = 11, |
| 65 | + AbbrevMonthGenitiveNames = 12, |
| 66 | + EraNames = 13, |
| 67 | + AbbrevEraNames = 14, |
| 68 | +}; |
| 69 | + |
| 70 | +/* |
| 71 | +* These values should be kept in sync with System.Globalization.CalendarDataResult |
| 72 | +*/ |
| 73 | +enum CalendarDataResult : int32_t |
| 74 | +{ |
| 75 | + Success = 0, |
| 76 | + UnknownError = 1, |
| 77 | + InsufficentBuffer = 2, |
| 78 | +}; |
| 79 | + |
| 80 | +// the function pointer definition for the callback used in EnumCalendarInfo |
| 81 | +typedef void(*EnumCalendarInfoCallback)(const UChar*, const void*); |
| 82 | + |
| 83 | +/* |
| 84 | +Function: |
| 85 | +GetCalendarDataResult |
| 86 | +
|
| 87 | +Converts a UErrorCode to a CalendarDataResult. |
| 88 | +*/ |
| 89 | +CalendarDataResult GetCalendarDataResult(UErrorCode err) |
| 90 | +{ |
| 91 | + if (U_SUCCESS(err)) |
| 92 | + { |
| 93 | + return Success; |
| 94 | + } |
| 95 | + |
| 96 | + if (err == U_BUFFER_OVERFLOW_ERROR) |
| 97 | + { |
| 98 | + return InsufficentBuffer; |
| 99 | + } |
| 100 | + |
| 101 | + return UnknownError; |
| 102 | +} |
| 103 | + |
| 104 | +/* |
| 105 | +Function: |
| 106 | +GetCalendarName |
| 107 | +
|
| 108 | +Gets the associated ICU calendar name for the CalendarId. |
| 109 | +*/ |
| 110 | +const char* GetCalendarName(CalendarId calendarId) |
| 111 | +{ |
| 112 | + switch (calendarId) |
| 113 | + { |
| 114 | + case JAPAN: |
| 115 | + return "japanese"; |
| 116 | + case THAI: |
| 117 | + return "buddhist"; |
| 118 | + case HEBREW: |
| 119 | + return "hebrew"; |
| 120 | + case CHINESELUNISOLAR: |
| 121 | + case KOREANLUNISOLAR: |
| 122 | + case JAPANESELUNISOLAR: |
| 123 | + case TAIWANLUNISOLAR: |
| 124 | + return "chinese"; |
| 125 | + case PERSIAN: |
| 126 | + return "persian"; |
| 127 | + case HIJRI: |
| 128 | + case UMALQURA: |
| 129 | + return "islamic"; |
| 130 | + case GREGORIAN: |
| 131 | + case GREGORIAN_US: |
| 132 | + case GREGORIAN_ARABIC: |
| 133 | + case GREGORIAN_ME_FRENCH: |
| 134 | + case GREGORIAN_XLIT_ENGLISH: |
| 135 | + case GREGORIAN_XLIT_FRENCH: |
| 136 | + case KOREA: |
| 137 | + case JULIAN: |
| 138 | + case LUNAR_ETO_CHN: |
| 139 | + case LUNAR_ETO_KOR: |
| 140 | + case LUNAR_ETO_ROKUYOU: |
| 141 | + case SAKA: |
| 142 | + case TAIWAN: |
| 143 | + default: |
| 144 | + return "gregorian"; |
| 145 | + } |
| 146 | +} |
| 147 | + |
| 148 | +/* |
| 149 | +Function: |
| 150 | +GetMonthDayPattern |
| 151 | +
|
| 152 | +Gets the Month-Day DateTime pattern for the specified locale. |
| 153 | +*/ |
| 154 | +CalendarDataResult GetMonthDayPattern(Locale& locale, UChar* sMonthDay, int32_t stringCapacity) |
| 155 | +{ |
| 156 | + UErrorCode err = U_ZERO_ERROR; |
| 157 | + LocalPointer<DateTimePatternGenerator> generator(DateTimePatternGenerator::createInstance(locale, err)); |
| 158 | + if (U_FAILURE(err)) |
| 159 | + return GetCalendarDataResult(err); |
| 160 | + |
| 161 | + UnicodeString monthDayPattern = generator->getBestPattern(UnicodeString("MMMMd"), err); |
| 162 | + if (U_FAILURE(err)) |
| 163 | + return GetCalendarDataResult(err); |
| 164 | + |
| 165 | + monthDayPattern.extract(sMonthDay, stringCapacity, err); |
| 166 | + |
| 167 | + return GetCalendarDataResult(err); |
| 168 | +} |
| 169 | + |
| 170 | +/* |
| 171 | +Function: |
| 172 | +GetNativeCalendarName |
| 173 | +
|
| 174 | +Gets the native calendar name. |
| 175 | +*/ |
| 176 | +CalendarDataResult GetNativeCalendarName(Locale& locale, CalendarId calendarId, UChar* nativeName, int32_t stringCapacity) |
| 177 | +{ |
| 178 | + LocalPointer<LocaleDisplayNames> displayNames(LocaleDisplayNames::createInstance(locale)); |
| 179 | + |
| 180 | + UnicodeString calendarName; |
| 181 | + displayNames->keyValueDisplayName("calendar", GetCalendarName(calendarId), calendarName); |
| 182 | + |
| 183 | + UErrorCode err = U_ZERO_ERROR; |
| 184 | + calendarName.extract(nativeName, stringCapacity, err); |
| 185 | + |
| 186 | + return GetCalendarDataResult(err); |
| 187 | +} |
| 188 | + |
| 189 | +/* |
| 190 | +Function: |
| 191 | +GetCalendarInfo |
| 192 | +
|
| 193 | +Gets a single string of calendar information by filling the result parameter with the requested value. |
| 194 | +*/ |
| 195 | +extern "C" CalendarDataResult GetCalendarInfo(const UChar* localeName, CalendarId calendarId, CalendarDataType dataType, UChar* result, int32_t resultCapacity) |
| 196 | +{ |
| 197 | + Locale locale = GetLocale(localeName); |
| 198 | + if (locale.isBogus()) |
| 199 | + return UnknownError; |
| 200 | + |
| 201 | + switch (dataType) |
| 202 | + { |
| 203 | + case NativeName: |
| 204 | + return GetNativeCalendarName(locale, calendarId, result, resultCapacity); |
| 205 | + case MonthDay: |
| 206 | + return GetMonthDayPattern(locale, result, resultCapacity); |
| 207 | + default: |
| 208 | + assert(false); |
| 209 | + return UnknownError; |
| 210 | + } |
| 211 | +} |
| 212 | + |
| 213 | +/* |
| 214 | +Function: |
| 215 | +InvokeCallbackForDateTimePattern |
| 216 | +
|
| 217 | +Gets the DateTime pattern for the specified skeleton and invokes the callback with the retrieved value. |
| 218 | +*/ |
| 219 | +bool InvokeCallbackForDateTimePattern(Locale& locale, const char* patternSkeleton, EnumCalendarInfoCallback callback, const void* context) |
| 220 | +{ |
| 221 | + UErrorCode err = U_ZERO_ERROR; |
| 222 | + LocalPointer<DateTimePatternGenerator> generator(DateTimePatternGenerator::createInstance(locale, err)); |
| 223 | + if (U_FAILURE(err)) |
| 224 | + return false; |
| 225 | + |
| 226 | + UnicodeString pattern = generator->getBestPattern(UnicodeString(patternSkeleton), err); |
| 227 | + if (U_SUCCESS(err)) |
| 228 | + { |
| 229 | + callback(pattern.getTerminatedBuffer(), context); |
| 230 | + return true; |
| 231 | + } |
| 232 | + |
| 233 | + return false; |
| 234 | +} |
| 235 | + |
| 236 | +/* |
| 237 | +Function: |
| 238 | +EnumCalendarArray |
| 239 | +
|
| 240 | +Enumerates an array of strings and invokes the callback for each value. |
| 241 | +*/ |
| 242 | +bool EnumCalendarArray(const UnicodeString* srcArray, int32_t srcArrayCount, EnumCalendarInfoCallback callback, const void* context) |
| 243 | +{ |
| 244 | + for (int i = 0; i < srcArrayCount; i++) |
| 245 | + { |
| 246 | + UnicodeString src = srcArray[i]; |
| 247 | + callback(src.getTerminatedBuffer(), context); |
| 248 | + } |
| 249 | + |
| 250 | + return true; |
| 251 | +} |
| 252 | + |
| 253 | +/* |
| 254 | +Function: |
| 255 | +EnumWeekdays |
| 256 | +
|
| 257 | +Enumerates all the weekday names of the specified context and width, invoking the callback function |
| 258 | +for each weekday name. |
| 259 | +*/ |
| 260 | +bool EnumWeekdays( |
| 261 | + Locale& locale, |
| 262 | + CalendarId calendarId, |
| 263 | + DateFormatSymbols::DtContextType dtContext, |
| 264 | + DateFormatSymbols::DtWidthType dtWidth, |
| 265 | + EnumCalendarInfoCallback callback, |
| 266 | + const void* context) |
| 267 | +{ |
| 268 | + UErrorCode err = U_ZERO_ERROR; |
| 269 | + DateFormatSymbols dateFormatSymbols(locale, GetCalendarName(calendarId), err); |
| 270 | + if (U_FAILURE(err)) |
| 271 | + return false; |
| 272 | + |
| 273 | + int32_t daysCount; |
| 274 | + const UnicodeString* dayNames = dateFormatSymbols.getWeekdays(daysCount, dtContext, dtWidth); |
| 275 | + |
| 276 | + // ICU returns an empty string for the first/zeroth element in the weekdays array. |
| 277 | + // So skip the first element. |
| 278 | + dayNames++; |
| 279 | + daysCount--; |
| 280 | + |
| 281 | + return EnumCalendarArray(dayNames, daysCount, callback, context); |
| 282 | +} |
| 283 | + |
| 284 | +/* |
| 285 | +Function: |
| 286 | +EnumMonths |
| 287 | +
|
| 288 | +Enumerates all the month names of the specified context and width, invoking the callback function |
| 289 | +for each month name. |
| 290 | +*/ |
| 291 | +bool EnumMonths( |
| 292 | + Locale& locale, |
| 293 | + CalendarId calendarId, |
| 294 | + DateFormatSymbols::DtContextType dtContext, |
| 295 | + DateFormatSymbols::DtWidthType dtWidth, |
| 296 | + EnumCalendarInfoCallback callback, |
| 297 | + const void* context) |
| 298 | +{ |
| 299 | + UErrorCode err = U_ZERO_ERROR; |
| 300 | + DateFormatSymbols dateFormatSymbols(locale, GetCalendarName(calendarId), err); |
| 301 | + if (U_FAILURE(err)) |
| 302 | + return false; |
| 303 | + |
| 304 | + int32_t monthsCount; |
| 305 | + const UnicodeString* monthNames = dateFormatSymbols.getMonths(monthsCount, dtContext, dtWidth); |
| 306 | + return EnumCalendarArray(monthNames, monthsCount, callback, context); |
| 307 | +} |
| 308 | + |
| 309 | +/* |
| 310 | +Function: |
| 311 | +EnumEraNames |
| 312 | +
|
| 313 | +Enumerates all the era names of the specified locale and calendar, invoking the callback function |
| 314 | +for each era name. |
| 315 | +*/ |
| 316 | +bool EnumEraNames(Locale& locale, CalendarId calendarId, EnumCalendarInfoCallback callback, const void* context) |
| 317 | +{ |
| 318 | + UErrorCode err = U_ZERO_ERROR; |
| 319 | + DateFormatSymbols dateFormatSymbols(locale, GetCalendarName(calendarId), err); |
| 320 | + if (U_FAILURE(err)) |
| 321 | + return false; |
| 322 | + |
| 323 | + int32_t eraNameCount; |
| 324 | + const UnicodeString* eraNames = dateFormatSymbols.getEras(eraNameCount); |
| 325 | + return EnumCalendarArray(eraNames, eraNameCount, callback, context); |
| 326 | +} |
| 327 | + |
| 328 | +/* |
| 329 | +Function: |
| 330 | +EnumCalendarInfo |
| 331 | +
|
| 332 | +Retrieves a collection of calendar string data specified by the locale, calendar, and data type. |
| 333 | +Allows for a collection of calendar string data to be retrieved by invoking |
| 334 | +the callback for each value in the collection. |
| 335 | +The context parameter is passed through to the callback along with each string. |
| 336 | +*/ |
| 337 | +extern "C" int32_t EnumCalendarInfo( |
| 338 | + EnumCalendarInfoCallback callback, |
| 339 | + const UChar* localeName, |
| 340 | + CalendarId calendarId, |
| 341 | + CalendarDataType dataType, |
| 342 | + const void* context) |
| 343 | +{ |
| 344 | + Locale locale = GetLocale(localeName); |
| 345 | + if (locale.isBogus()) |
| 346 | + return false; |
| 347 | + |
| 348 | + switch (dataType) |
| 349 | + { |
| 350 | + case ShortDates: |
| 351 | + return InvokeCallbackForDateTimePattern(locale, "Mdyyyy", callback, context); |
| 352 | + case LongDates: |
| 353 | + // TODO: need to replace the "EEEE"s with "dddd"s for .net |
| 354 | + // Also, "LLLL"s to "MMMM"s |
| 355 | + return InvokeCallbackForDateTimePattern(locale, "eeeeMMMMddyyyy", callback, context); |
| 356 | + case YearMonths: |
| 357 | + return InvokeCallbackForDateTimePattern(locale, "yyyyMMMM", callback, context); |
| 358 | + case DayNames: |
| 359 | + return EnumWeekdays(locale, calendarId, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE, callback, context); |
| 360 | + case AbbrevDayNames: |
| 361 | + return EnumWeekdays(locale, calendarId, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED, callback, context); |
| 362 | + case MonthNames: |
| 363 | + return EnumMonths(locale, calendarId, DateFormatSymbols::STANDALONE, DateFormatSymbols::WIDE, callback, context); |
| 364 | + case AbbrevMonthNames: |
| 365 | + return EnumMonths(locale, calendarId, DateFormatSymbols::STANDALONE, DateFormatSymbols::ABBREVIATED, callback, context); |
| 366 | + case SuperShortDayNames: |
| 367 | + return EnumWeekdays(locale, calendarId, DateFormatSymbols::STANDALONE, DateFormatSymbols::SHORT, callback, context); |
| 368 | + case MonthGenitiveNames: |
| 369 | + return EnumMonths(locale, calendarId, DateFormatSymbols::FORMAT, DateFormatSymbols::WIDE, callback, context); |
| 370 | + case AbbrevMonthGenitiveNames: |
| 371 | + return EnumMonths(locale, calendarId, DateFormatSymbols::FORMAT, DateFormatSymbols::ABBREVIATED, callback, context); |
| 372 | + case EraNames: |
| 373 | + case AbbrevEraNames: |
| 374 | + // NOTE: On Windows, the EraName is "A.D." and AbbrevEraName is "AD". |
| 375 | + // But ICU/CLDR only supports "Anno Domini", "AD", and "A". |
| 376 | + // So returning getEras (i.e. "AD") for both EraNames and AbbrevEraNames. |
| 377 | + return EnumEraNames(locale, calendarId, callback, context); |
| 378 | + default: |
| 379 | + assert(false); |
| 380 | + return false; |
| 381 | + } |
| 382 | +} |
0 commit comments