1 /* 2 * Copyright 2010-2014, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Oliver Tappe <zooey@hirschkaefer.de> 7 * Adrien Desutugues <pulkomandy@pulkomandy.tk> 8 */ 9 10 #include <unicode/uversion.h> 11 #include <DateFormat.h> 12 13 #include <AutoDeleter.h> 14 #include <Autolock.h> 15 #include <FormattingConventionsPrivate.h> 16 #include <LanguagePrivate.h> 17 #include <Locale.h> 18 #include <LocaleRoster.h> 19 #include <TimeZone.h> 20 21 #include <ICUWrapper.h> 22 23 #include <unicode/datefmt.h> 24 #include <unicode/dtfmtsym.h> 25 #include <unicode/smpdtfmt.h> 26 27 #include <vector> 28 29 30 static const DateFormatSymbols::DtWidthType kDateFormatStyleToWidth[] = { 31 DateFormatSymbols::WIDE, 32 DateFormatSymbols::ABBREVIATED, 33 DateFormatSymbols::SHORT, 34 DateFormatSymbols::NARROW, 35 }; 36 37 38 BDateFormat::BDateFormat(const BLocale* locale) 39 : BFormat(locale) 40 { 41 } 42 43 44 BDateFormat::BDateFormat(const BLanguage& language, 45 const BFormattingConventions& conventions) 46 : BFormat(language, conventions) 47 { 48 } 49 50 51 BDateFormat::BDateFormat(const BDateFormat &other) 52 : BFormat(other) 53 { 54 } 55 56 57 BDateFormat::~BDateFormat() 58 { 59 } 60 61 62 status_t 63 BDateFormat::GetDateFormat(BDateFormatStyle style, 64 BString& outFormat) const 65 { 66 return fConventions.GetDateFormat(style, outFormat); 67 } 68 69 70 void 71 BDateFormat::SetDateFormat(BDateFormatStyle style, 72 const BString& format) 73 { 74 fConventions.SetExplicitDateFormat(style, format); 75 } 76 77 78 ssize_t 79 BDateFormat::Format(char* string, const size_t maxSize, const time_t time, 80 const BDateFormatStyle style) const 81 { 82 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style)); 83 if (dateFormatter.Get() == NULL) 84 return B_NO_MEMORY; 85 86 UnicodeString icuString; 87 dateFormatter->format((UDate)time * 1000, icuString); 88 89 CheckedArrayByteSink stringConverter(string, maxSize); 90 icuString.toUTF8(stringConverter); 91 92 if (stringConverter.Overflowed()) 93 return B_BAD_VALUE; 94 95 return stringConverter.NumberOfBytesWritten(); 96 } 97 98 99 status_t 100 BDateFormat::Format(BString& string, const time_t time, 101 const BDateFormatStyle style, const BTimeZone* timeZone) const 102 { 103 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style)); 104 if (dateFormatter.Get() == NULL) 105 return B_NO_MEMORY; 106 107 if (timeZone != NULL) { 108 ObjectDeleter<TimeZone> icuTimeZone( 109 TimeZone::createTimeZone(timeZone->ID().String())); 110 if (icuTimeZone.Get() == NULL) 111 return B_NO_MEMORY; 112 dateFormatter->setTimeZone(*icuTimeZone.Get()); 113 } 114 115 UnicodeString icuString; 116 dateFormatter->format((UDate)time * 1000, icuString); 117 118 string.Truncate(0); 119 BStringByteSink stringConverter(&string); 120 icuString.toUTF8(stringConverter); 121 122 return B_OK; 123 } 124 125 126 status_t 127 BDateFormat::Format(BString& string, const BDate& time, 128 const BDateFormatStyle style, const BTimeZone* timeZone) const 129 { 130 if (!time.IsValid()) 131 return B_BAD_DATA; 132 133 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style)); 134 if (dateFormatter.Get() == NULL) 135 return B_NO_MEMORY; 136 137 UErrorCode err = U_ZERO_ERROR; 138 ObjectDeleter<Calendar> calendar(Calendar::createInstance(err)); 139 if (!U_SUCCESS(err)) 140 return B_NO_MEMORY; 141 142 if (timeZone != NULL) { 143 ObjectDeleter<TimeZone> icuTimeZone( 144 TimeZone::createTimeZone(timeZone->ID().String())); 145 if (icuTimeZone.Get() == NULL) 146 return B_NO_MEMORY; 147 dateFormatter->setTimeZone(*icuTimeZone.Get()); 148 calendar->setTimeZone(*icuTimeZone.Get()); 149 } 150 151 // Note ICU calendar uses months in range 0..11, while we use the more 152 // natural 1..12 in BDate. 153 calendar->set(time.Year(), time.Month() - 1, time.Day()); 154 155 UnicodeString icuString; 156 FieldPosition p; 157 dateFormatter->format(*calendar.Get(), icuString, p); 158 159 string.Truncate(0); 160 BStringByteSink stringConverter(&string); 161 icuString.toUTF8(stringConverter); 162 163 return B_OK; 164 } 165 166 167 status_t 168 BDateFormat::Format(BString& string, int*& fieldPositions, int& fieldCount, 169 const time_t time, const BDateFormatStyle style) const 170 { 171 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style)); 172 if (dateFormatter.Get() == NULL) 173 return B_NO_MEMORY; 174 175 fieldPositions = NULL; 176 UErrorCode error = U_ZERO_ERROR; 177 icu::FieldPositionIterator positionIterator; 178 UnicodeString icuString; 179 dateFormatter->format((UDate)time * 1000, icuString, &positionIterator, 180 error); 181 182 if (error != U_ZERO_ERROR) 183 return B_BAD_VALUE; 184 185 icu::FieldPosition field; 186 std::vector<int> fieldPosStorage; 187 fieldCount = 0; 188 while (positionIterator.next(field)) { 189 fieldPosStorage.push_back(field.getBeginIndex()); 190 fieldPosStorage.push_back(field.getEndIndex()); 191 fieldCount += 2; 192 } 193 194 fieldPositions = (int*) malloc(fieldCount * sizeof(int)); 195 196 for (int i = 0 ; i < fieldCount ; i++ ) 197 fieldPositions[i] = fieldPosStorage[i]; 198 199 string.Truncate(0); 200 BStringByteSink stringConverter(&string); 201 202 icuString.toUTF8(stringConverter); 203 204 return B_OK; 205 } 206 207 208 status_t 209 BDateFormat::GetFields(BDateElement*& fields, int& fieldCount, 210 BDateFormatStyle style) const 211 { 212 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style)); 213 if (dateFormatter.Get() == NULL) 214 return B_NO_MEMORY; 215 216 fields = NULL; 217 UErrorCode error = U_ZERO_ERROR; 218 icu::FieldPositionIterator positionIterator; 219 UnicodeString icuString; 220 time_t now; 221 dateFormatter->format((UDate)time(&now) * 1000, icuString, 222 &positionIterator, error); 223 224 if (U_FAILURE(error)) 225 return B_BAD_VALUE; 226 227 icu::FieldPosition field; 228 std::vector<int> fieldPosStorage; 229 fieldCount = 0; 230 while (positionIterator.next(field)) { 231 fieldPosStorage.push_back(field.getField()); 232 fieldCount ++; 233 } 234 235 fields = (BDateElement*) malloc(fieldCount * sizeof(BDateElement)); 236 237 for (int i = 0 ; i < fieldCount ; i++ ) { 238 switch (fieldPosStorage[i]) { 239 case UDAT_YEAR_FIELD: 240 fields[i] = B_DATE_ELEMENT_YEAR; 241 break; 242 case UDAT_MONTH_FIELD: 243 fields[i] = B_DATE_ELEMENT_MONTH; 244 break; 245 case UDAT_DATE_FIELD: 246 fields[i] = B_DATE_ELEMENT_DAY; 247 break; 248 case UDAT_DAY_OF_WEEK_FIELD: 249 fields[i] = B_DATE_ELEMENT_WEEKDAY; 250 break; 251 default: 252 fields[i] = B_DATE_ELEMENT_INVALID; 253 break; 254 } 255 } 256 257 return B_OK; 258 } 259 260 261 status_t 262 BDateFormat::GetStartOfWeek(BWeekday* startOfWeek) const 263 { 264 if (startOfWeek == NULL) 265 return B_BAD_VALUE; 266 267 UErrorCode err = U_ZERO_ERROR; 268 ObjectDeleter<Calendar> calendar = Calendar::createInstance( 269 *BFormattingConventions::Private(&fConventions).ICULocale(), err); 270 271 if (U_FAILURE(err)) 272 return B_ERROR; 273 274 UCalendarDaysOfWeek icuWeekStart = calendar->getFirstDayOfWeek(err); 275 if (U_FAILURE(err)) 276 return B_ERROR; 277 278 switch (icuWeekStart) { 279 case UCAL_SUNDAY: 280 *startOfWeek = B_WEEKDAY_SUNDAY; 281 break; 282 case UCAL_MONDAY: 283 *startOfWeek = B_WEEKDAY_MONDAY; 284 break; 285 case UCAL_TUESDAY: 286 *startOfWeek = B_WEEKDAY_TUESDAY; 287 break; 288 case UCAL_WEDNESDAY: 289 *startOfWeek = B_WEEKDAY_WEDNESDAY; 290 break; 291 case UCAL_THURSDAY: 292 *startOfWeek = B_WEEKDAY_THURSDAY; 293 break; 294 case UCAL_FRIDAY: 295 *startOfWeek = B_WEEKDAY_FRIDAY; 296 break; 297 case UCAL_SATURDAY: 298 *startOfWeek = B_WEEKDAY_SATURDAY; 299 break; 300 default: 301 return B_ERROR; 302 } 303 304 return B_OK; 305 } 306 307 308 status_t 309 BDateFormat::GetMonthName(int month, BString& outName, 310 const BDateFormatStyle style) const 311 { 312 if (style < 0 || style >= B_DATE_FORMAT_STYLE_COUNT) 313 return B_BAD_VALUE; 314 315 DateFormat* format = _CreateDateFormatter(B_LONG_DATE_FORMAT); 316 317 SimpleDateFormat* simpleFormat = dynamic_cast<SimpleDateFormat*>(format); 318 if (simpleFormat == NULL) { 319 delete format; 320 return B_ERROR; 321 } 322 323 const DateFormatSymbols* symbols = simpleFormat->getDateFormatSymbols(); 324 325 int32_t count; 326 const UnicodeString* names = symbols->getMonths(count, 327 DateFormatSymbols::STANDALONE, kDateFormatStyleToWidth[style]); 328 329 if (month > count || month <= 0) { 330 delete simpleFormat; 331 return B_BAD_DATA; 332 } 333 334 BStringByteSink stringConverter(&outName); 335 names[month - 1].toUTF8(stringConverter); 336 337 delete simpleFormat; 338 return B_OK; 339 } 340 341 342 status_t 343 BDateFormat::GetDayName(int day, BString& outName, 344 const BDateFormatStyle style) const 345 { 346 if (style < 0 || style >= B_DATE_FORMAT_STYLE_COUNT) 347 return B_BAD_VALUE; 348 349 DateFormat* format = _CreateDateFormatter(B_LONG_DATE_FORMAT); 350 351 SimpleDateFormat* simpleFormat = dynamic_cast<SimpleDateFormat*>(format); 352 if (simpleFormat == NULL) { 353 delete format; 354 return B_ERROR; 355 } 356 357 const DateFormatSymbols* symbols = simpleFormat->getDateFormatSymbols(); 358 359 int32_t count; 360 const UnicodeString* names = symbols->getWeekdays(count, 361 DateFormatSymbols::STANDALONE, kDateFormatStyleToWidth[style]); 362 363 if (day >= count || day <= 0) { 364 delete simpleFormat; 365 return B_BAD_DATA; 366 } 367 368 BStringByteSink stringConverter(&outName); 369 names[_ConvertDayNumberToICU(day)].toUTF8(stringConverter); 370 371 delete simpleFormat; 372 return B_OK; 373 } 374 375 376 status_t 377 BDateFormat::Parse(BString source, BDateFormatStyle style, BDate& output) 378 { 379 // FIXME currently this parses a date in any timezone (or the local one if 380 // none is specified) to a BDate in UTC. This may not be a good idea, we 381 // may want to parse to a "local" date instead. But BDate should be made 382 // timezone aware so things like BDate::Difference can work for dates in 383 // different timezones. 384 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style)); 385 if (dateFormatter.Get() == NULL) 386 return B_NO_MEMORY; 387 388 ParsePosition p(0); 389 UDate date = dateFormatter->parse(UnicodeString::fromUTF8(source.String()), 390 p); 391 392 output.SetDate(1970, 1, 1); 393 output.AddDays(date / U_MILLIS_PER_DAY + 0.5); 394 395 return B_OK; 396 } 397 398 399 DateFormat* 400 BDateFormat::_CreateDateFormatter(const BDateFormatStyle style) const 401 { 402 Locale* icuLocale 403 = fConventions.UseStringsFromPreferredLanguage() 404 ? BLanguage::Private(&fLanguage).ICULocale() 405 : BFormattingConventions::Private(&fConventions).ICULocale(); 406 407 icu::DateFormat* dateFormatter 408 = icu::DateFormat::createDateInstance(DateFormat::kShort, *icuLocale); 409 if (dateFormatter == NULL) 410 return NULL; 411 412 SimpleDateFormat* dateFormatterImpl 413 = static_cast<SimpleDateFormat*>(dateFormatter); 414 415 BString format; 416 fConventions.GetDateFormat(style, format); 417 418 UnicodeString pattern(format.String()); 419 dateFormatterImpl->applyPattern(pattern); 420 421 return dateFormatter; 422 } 423 424