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