1 /* 2 * Copyright 2010-2011, Oliver Tappe, zooey@hirschkaefer.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ICUTimeData.h" 8 9 #include <langinfo.h> 10 #include <strings.h> 11 12 #include <unicode/dtfmtsym.h> 13 #include <unicode/gregocal.h> 14 #include <unicode/smpdtfmt.h> 15 16 #include <AutoDeleter.h> 17 18 #include "ICUMessagesData.h" 19 20 namespace BPrivate { 21 namespace Libroot { 22 23 24 ICUTimeData::ICUTimeData(pthread_key_t tlsKey, struct lc_time_t& lcTimeInfo, 25 const ICUMessagesData& messagesData) 26 : 27 inherited(tlsKey), 28 fLCTimeInfo(lcTimeInfo), 29 fDataBridge(NULL), 30 fMessagesData(messagesData) 31 { 32 for (int i = 0; i < 12; ++i) { 33 fLCTimeInfo.mon[i] = fMon[i]; 34 fLCTimeInfo.month[i] = fMonth[i]; 35 fLCTimeInfo.alt_month[i] = fAltMonth[i]; 36 } 37 for (int i = 0; i < 7; ++i) { 38 fLCTimeInfo.wday[i] = fWday[i]; 39 fLCTimeInfo.weekday[i] = fWeekday[i]; 40 } 41 fLCTimeInfo.X_fmt = fTimeFormat; 42 fLCTimeInfo.x_fmt = fDateFormat; 43 fLCTimeInfo.c_fmt = fDateTimeFormat; 44 fLCTimeInfo.am = fAm; 45 fLCTimeInfo.pm = fPm; 46 fLCTimeInfo.date_fmt = fDateTimeZoneFormat; 47 fLCTimeInfo.md_order = fMonthDayOrder; 48 fLCTimeInfo.ampm_fmt = fAmPmFormat; 49 } 50 51 52 ICUTimeData::~ICUTimeData() 53 { 54 } 55 56 57 void 58 ICUTimeData::Initialize(LocaleTimeDataBridge* dataBridge) 59 { 60 fDataBridge = dataBridge; 61 } 62 63 64 status_t 65 ICUTimeData::SetTo(const Locale& locale, const char* posixLocaleName) 66 { 67 status_t result = inherited::SetTo(locale, posixLocaleName); 68 if (result != B_OK) 69 return result; 70 71 UErrorCode icuStatus = U_ZERO_ERROR; 72 DateFormatSymbols formatSymbols(ICULocaleForStrings(), icuStatus); 73 if (!U_SUCCESS(icuStatus)) 74 return B_UNSUPPORTED; 75 76 int count = 0; 77 const UnicodeString* strings = formatSymbols.getShortMonths(count); 78 result = _SetLCTimeEntries(strings, fMon[0], sizeof(fMon[0]), count, 12); 79 80 if (result == B_OK) { 81 strings = formatSymbols.getMonths(count); 82 result = _SetLCTimeEntries(strings, fMonth[0], sizeof(fMonth[0]), count, 83 12); 84 } 85 86 if (result == B_OK) { 87 strings = formatSymbols.getShortWeekdays(count); 88 if (count == 8 && strings[0].length() == 0) { 89 // ICUs weekday arrays are 1-based 90 strings++; 91 count = 7; 92 } 93 result 94 = _SetLCTimeEntries(strings, fWday[0], sizeof(fWday[0]), count, 7); 95 } 96 97 if (result == B_OK) { 98 strings = formatSymbols.getWeekdays(count); 99 if (count == 8 && strings[0].length() == 0) { 100 // ICUs weekday arrays are 1-based 101 strings++; 102 count = 7; 103 } 104 result = _SetLCTimeEntries(strings, fWeekday[0], sizeof(fWeekday[0]), 105 count, 7); 106 } 107 108 if (result == B_OK) { 109 try { 110 DateFormat* format = DateFormat::createTimeInstance( 111 DateFormat::kDefault, fLocale); 112 result = _SetLCTimePattern(format, fTimeFormat, sizeof(fTimeFormat)); 113 delete format; 114 } catch(...) { 115 result = B_NO_MEMORY; 116 } 117 } 118 119 if (result == B_OK) { 120 try { 121 DateFormat* format = DateFormat::createDateInstance( 122 DateFormat::kDefault, fLocale); 123 result = _SetLCTimePattern(format, fDateFormat, sizeof(fDateFormat)); 124 delete format; 125 } catch(...) { 126 result = B_NO_MEMORY; 127 } 128 } 129 130 if (result == B_OK) { 131 try { 132 DateFormat* format = DateFormat::createDateTimeInstance( 133 DateFormat::kFull, DateFormat::kFull, fLocale); 134 result = _SetLCTimePattern(format, fDateTimeFormat, 135 sizeof(fDateTimeFormat)); 136 delete format; 137 } catch(...) { 138 result = B_NO_MEMORY; 139 } 140 } 141 142 if (result == B_OK) { 143 strings = formatSymbols.getAmPmStrings(count); 144 result = _SetLCTimeEntries(strings, fAm, sizeof(fAm), 1, 1); 145 if (result == B_OK) 146 result = _SetLCTimeEntries(&strings[1], fPm, sizeof(fPm), 1, 1); 147 } 148 149 if (result == B_OK) { 150 strings = formatSymbols.getMonths(count, DateFormatSymbols::STANDALONE, 151 DateFormatSymbols::WIDE); 152 result = _SetLCTimeEntries(strings, fAltMonth[0], sizeof(fAltMonth[0]), 153 count, 12); 154 } 155 156 strcpy(fAmPmFormat, fDataBridge->posixLCTimeInfo->ampm_fmt); 157 // ICU does not provide anything for this (and that makes sense, too) 158 159 return result; 160 } 161 162 163 status_t 164 ICUTimeData::SetToPosix() 165 { 166 status_t result = inherited::SetToPosix(); 167 168 if (result == B_OK) { 169 for (int i = 0; i < 12; ++i) { 170 strcpy(fMon[i], fDataBridge->posixLCTimeInfo->mon[i]); 171 strcpy(fMonth[i], fDataBridge->posixLCTimeInfo->month[i]); 172 strcpy(fAltMonth[i], fDataBridge->posixLCTimeInfo->alt_month[i]); 173 } 174 for (int i = 0; i < 7; ++i) { 175 strcpy(fWday[i], fDataBridge->posixLCTimeInfo->wday[i]); 176 strcpy(fWeekday[i], fDataBridge->posixLCTimeInfo->weekday[i]); 177 } 178 strcpy(fTimeFormat, fDataBridge->posixLCTimeInfo->X_fmt); 179 strcpy(fDateFormat, fDataBridge->posixLCTimeInfo->x_fmt); 180 strcpy(fDateTimeFormat, fDataBridge->posixLCTimeInfo->c_fmt); 181 strcpy(fAm, fDataBridge->posixLCTimeInfo->am); 182 strcpy(fPm, fDataBridge->posixLCTimeInfo->pm); 183 strcpy(fDateTimeZoneFormat, fDataBridge->posixLCTimeInfo->date_fmt); 184 strcpy(fMonthDayOrder, fDataBridge->posixLCTimeInfo->md_order); 185 strcpy(fAmPmFormat, fDataBridge->posixLCTimeInfo->ampm_fmt); 186 } 187 188 return result; 189 } 190 191 192 const char* 193 ICUTimeData::GetLanginfo(int index) 194 { 195 switch(index) { 196 case D_T_FMT: 197 return fDateTimeFormat; 198 case D_FMT: 199 return fDateFormat; 200 case T_FMT: 201 return fTimeFormat; 202 case T_FMT_AMPM: 203 return fAmPmFormat; 204 case AM_STR: 205 return fAm; 206 case PM_STR: 207 return fPm; 208 209 case DAY_1: 210 case DAY_2: 211 case DAY_3: 212 case DAY_4: 213 case DAY_5: 214 case DAY_6: 215 case DAY_7: 216 return fWeekday[index - DAY_1]; 217 218 case ABDAY_1: 219 case ABDAY_2: 220 case ABDAY_3: 221 case ABDAY_4: 222 case ABDAY_5: 223 case ABDAY_6: 224 case ABDAY_7: 225 return fWday[index - ABDAY_1]; 226 227 case MON_1: 228 case MON_2: 229 case MON_3: 230 case MON_4: 231 case MON_5: 232 case MON_6: 233 case MON_7: 234 case MON_8: 235 case MON_9: 236 case MON_10: 237 case MON_11: 238 case MON_12: 239 return fMonth[index - MON_1]; 240 241 case ABMON_1: 242 case ABMON_2: 243 case ABMON_3: 244 case ABMON_4: 245 case ABMON_5: 246 case ABMON_6: 247 case ABMON_7: 248 case ABMON_8: 249 case ABMON_9: 250 case ABMON_10: 251 case ABMON_11: 252 case ABMON_12: 253 return fMon[index - ABMON_1]; 254 255 default: 256 return ""; 257 } 258 } 259 260 261 const Locale& 262 ICUTimeData::ICULocaleForStrings() const 263 { 264 // check if the date strings should be taken from the messages-locale 265 // or from the time-locale (default) 266 UErrorCode icuStatus = U_ZERO_ERROR; 267 char stringsValue[16]; 268 fLocale.getKeywordValue("strings", stringsValue, sizeof(stringsValue), 269 icuStatus); 270 if (U_SUCCESS(icuStatus) && strcasecmp(stringsValue, "messages") == 0) 271 return fMessagesData.ICULocale(); 272 else 273 return fLocale; 274 } 275 276 277 status_t 278 ICUTimeData::_SetLCTimeEntries(const UnicodeString* strings, char* destination, 279 int entrySize, int count, int maxCount) 280 { 281 if (strings == NULL) 282 return B_ERROR; 283 284 status_t result = B_OK; 285 if (count > maxCount) 286 count = maxCount; 287 for (int32 i = 0; result == B_OK && i < count; ++i) { 288 result = _ConvertUnicodeStringToLocaleconvEntry(strings[i], destination, 289 entrySize); 290 destination += entrySize; 291 } 292 293 return result; 294 } 295 296 297 status_t 298 ICUTimeData::_SetLCTimePattern(DateFormat* format, char* destination, 299 int destinationSize) 300 { 301 SimpleDateFormat* simpleFormat = dynamic_cast<SimpleDateFormat*>(format); 302 if (!simpleFormat) 303 return B_BAD_TYPE; 304 305 // convert ICU-type pattern to posix (i.e. strftime()) format string 306 UnicodeString icuPattern; 307 simpleFormat->toPattern(icuPattern); 308 UnicodeString posixPattern; 309 if (icuPattern.length() > 0) { 310 UChar lastCharSeen = 0; 311 int lastCharCount = 1; 312 bool inSingleQuotes = false; 313 bool inDoubleQuotes = false; 314 // we loop one character past the end on purpose, which will result in a 315 // final -1 char to be processed, which in turn will let us handle the 316 // last character (via lastCharSeen) 317 for (int i = 0; i <= icuPattern.length(); ++i) { 318 UChar currChar = icuPattern.charAt(i); 319 if (lastCharSeen != 0 && currChar == lastCharSeen) { 320 lastCharCount++; 321 continue; 322 } 323 324 if (!inSingleQuotes && !inDoubleQuotes) { 325 switch (lastCharSeen) { 326 case L'a': 327 posixPattern.append(UnicodeString("%p", "")); 328 break; 329 case L'd': 330 if (lastCharCount == 2) 331 posixPattern.append(UnicodeString("%d", "")); 332 else 333 posixPattern.append(UnicodeString("%e", "")); 334 break; 335 case L'D': 336 posixPattern.append(UnicodeString("%j", "")); 337 break; 338 case L'c': 339 // fall through, to handle 'c' the same as 'e' 340 case L'e': 341 if (lastCharCount == 4) 342 posixPattern.append(UnicodeString("%A", "")); 343 else if (lastCharCount <= 2) 344 posixPattern.append(UnicodeString("%u", "")); 345 else 346 posixPattern.append(UnicodeString("%a", "")); 347 break; 348 case L'E': 349 if (lastCharCount == 4) 350 posixPattern.append(UnicodeString("%A", "")); 351 else 352 posixPattern.append(UnicodeString("%a", "")); 353 break; 354 case L'k': 355 // fall through, to handle 'k' the same as 'h' 356 case L'h': 357 if (lastCharCount == 2) 358 posixPattern.append(UnicodeString("%I", "")); 359 else 360 posixPattern.append(UnicodeString("%l", "")); 361 break; 362 case L'H': 363 if (lastCharCount == 2) 364 posixPattern.append(UnicodeString("%H", "")); 365 else 366 posixPattern.append(UnicodeString("%k", "")); 367 break; 368 case L'm': 369 posixPattern.append(UnicodeString("%M", "")); 370 break; 371 case L'L': 372 // fall through, to handle 'L' the same as 'M' 373 case L'M': 374 if (lastCharCount == 4) 375 posixPattern.append(UnicodeString("%B", "")); 376 else if (lastCharCount == 3) 377 posixPattern.append(UnicodeString("%b", "")); 378 else 379 posixPattern.append(UnicodeString("%m", "")); 380 break; 381 case L's': 382 posixPattern.append(UnicodeString("%S", "")); 383 break; 384 case L'w': 385 posixPattern.append(UnicodeString("%V", "")); 386 break; 387 case L'y': 388 if (lastCharCount == 2) 389 posixPattern.append(UnicodeString("%y", "")); 390 else 391 posixPattern.append(UnicodeString("%Y", "")); 392 break; 393 case L'Y': 394 posixPattern.append(UnicodeString("%G", "")); 395 break; 396 case L'z': 397 posixPattern.append(UnicodeString("%Z", "")); 398 break; 399 case L'Z': 400 posixPattern.append(UnicodeString("%z", "")); 401 break; 402 default: 403 if (lastCharSeen != 0) 404 posixPattern.append(lastCharSeen); 405 } 406 } else { 407 if (lastCharSeen != 0) 408 posixPattern.append(lastCharSeen); 409 } 410 411 if (currChar == L'"') { 412 inDoubleQuotes = !inDoubleQuotes; 413 lastCharSeen = 0; 414 } else if (currChar == L'\'') { 415 inSingleQuotes = !inSingleQuotes; 416 lastCharSeen = 0; 417 } else 418 lastCharSeen = currChar; 419 420 lastCharCount = 1; 421 } 422 } 423 424 return _ConvertUnicodeStringToLocaleconvEntry(posixPattern, destination, 425 destinationSize); 426 } 427 428 429 } // namespace Libroot 430 } // namespace BPrivate 431