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 30U_NAMESPACE_USE 31 32 33static const DateFormatSymbols::DtWidthType kDateFormatStyleToWidth[] = { 34 DateFormatSymbols::WIDE, 35 DateFormatSymbols::ABBREVIATED, 36 DateFormatSymbols::SHORT, 37 DateFormatSymbols::NARROW, 38}; 39 40 41BDateFormat::BDateFormat(const BLocale* locale) 42 : BFormat(locale) 43{ 44} 45 46 47BDateFormat::BDateFormat(const BLanguage& language, 48 const BFormattingConventions& conventions) 49 : BFormat(language, conventions) 50{ 51} 52 53 54BDateFormat::BDateFormat(const BDateFormat &other) 55 : BFormat(other) 56{ 57} 58 59 60BDateFormat::~BDateFormat() 61{ 62} 63 64 65status_t 66BDateFormat::GetDateFormat(BDateFormatStyle style, 67 BString& outFormat) const 68{ 69 return fConventions.GetDateFormat(style, outFormat); 70} 71 72 73void 74BDateFormat::SetDateFormat(BDateFormatStyle style, 75 const BString& format) 76{ 77 fConventions.SetExplicitDateFormat(style, format); 78} 79 80 81ssize_t 82BDateFormat::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.IsSet()) 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 102status_t 103BDateFormat::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.IsSet()) 108 return B_NO_MEMORY; 109 110 if (timeZone != NULL) { 111 ObjectDeleter<TimeZone> icuTimeZone( 112 TimeZone::createTimeZone(timeZone->ID().String())); 113 if (!icuTimeZone.IsSet()) 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 129status_t 130BDateFormat::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.IsSet()) 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.IsSet()) 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 170status_t 171BDateFormat::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.IsSet()) 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 211status_t 212BDateFormat::GetFields(BDateElement*& fields, int& fieldCount, 213 BDateFormatStyle style) const 214{ 215 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style)); 216 if (!dateFormatter.IsSet()) 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 264status_t 265BDateFormat::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 311status_t 312BDateFormat::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 345status_t 346BDateFormat::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 379status_t 380BDateFormat::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.IsSet()) 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 402DateFormat* 403BDateFormat::_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