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