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