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