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