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