RemoteDrawingEngine: Reduce RP_READ_BITMAP result timeout.
[haiku.git] / src / kits / locale / DateFormat.cpp
blobb70d601915d37b05d05c135e7c8bef35741a0962
1 /*
2 * Copyright 2010-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Oliver Tappe <zooey@hirschkaefer.de>
7 * Adrien Desutugues <pulkomandy@pulkomandy.tk>
8 */
10 #include <unicode/uversion.h>
11 #include <DateFormat.h>
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>
21 #include <ICUWrapper.h>
23 #include <unicode/datefmt.h>
24 #include <unicode/dtfmtsym.h>
25 #include <unicode/smpdtfmt.h>
27 #include <vector>
30 static const DateFormatSymbols::DtWidthType kDateFormatStyleToWidth[] = {
31 DateFormatSymbols::WIDE,
32 DateFormatSymbols::ABBREVIATED,
33 DateFormatSymbols::SHORT,
34 DateFormatSymbols::NARROW,
38 BDateFormat::BDateFormat(const BLocale* locale)
39 : BFormat(locale)
44 BDateFormat::BDateFormat(const BLanguage& language,
45 const BFormattingConventions& conventions)
46 : BFormat(language, conventions)
51 BDateFormat::BDateFormat(const BDateFormat &other)
52 : BFormat(other)
57 BDateFormat::~BDateFormat()
62 status_t
63 BDateFormat::GetDateFormat(BDateFormatStyle style,
64 BString& outFormat) const
66 return fConventions.GetDateFormat(style, outFormat);
70 void
71 BDateFormat::SetDateFormat(BDateFormatStyle style,
72 const BString& format)
74 fConventions.SetExplicitDateFormat(style, format);
78 ssize_t
79 BDateFormat::Format(char* string, const size_t maxSize, const time_t time,
80 const BDateFormatStyle style) const
82 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
83 if (dateFormatter.Get() == NULL)
84 return B_NO_MEMORY;
86 UnicodeString icuString;
87 dateFormatter->format((UDate)time * 1000, icuString);
89 CheckedArrayByteSink stringConverter(string, maxSize);
90 icuString.toUTF8(stringConverter);
92 if (stringConverter.Overflowed())
93 return B_BAD_VALUE;
95 return stringConverter.NumberOfBytesWritten();
99 status_t
100 BDateFormat::Format(BString& string, const time_t time,
101 const BDateFormatStyle style, const BTimeZone* timeZone) const
103 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
104 if (dateFormatter.Get() == NULL)
105 return B_NO_MEMORY;
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());
115 UnicodeString icuString;
116 dateFormatter->format((UDate)time * 1000, icuString);
118 string.Truncate(0);
119 BStringByteSink stringConverter(&string);
120 icuString.toUTF8(stringConverter);
122 return B_OK;
126 status_t
127 BDateFormat::Format(BString& string, const BDate& time,
128 const BDateFormatStyle style, const BTimeZone* timeZone) const
130 if (!time.IsValid())
131 return B_BAD_DATA;
133 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
134 if (dateFormatter.Get() == NULL)
135 return B_NO_MEMORY;
137 UErrorCode err = U_ZERO_ERROR;
138 ObjectDeleter<Calendar> calendar(Calendar::createInstance(err));
139 if (!U_SUCCESS(err))
140 return B_NO_MEMORY;
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());
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());
155 UnicodeString icuString;
156 FieldPosition p;
157 dateFormatter->format(*calendar.Get(), icuString, p);
159 string.Truncate(0);
160 BStringByteSink stringConverter(&string);
161 icuString.toUTF8(stringConverter);
163 return B_OK;
167 status_t
168 BDateFormat::Format(BString& string, int*& fieldPositions, int& fieldCount,
169 const time_t time, const BDateFormatStyle style) const
171 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
172 if (dateFormatter.Get() == NULL)
173 return B_NO_MEMORY;
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);
182 if (error != U_ZERO_ERROR)
183 return B_BAD_VALUE;
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;
194 fieldPositions = (int*) malloc(fieldCount * sizeof(int));
196 for (int i = 0 ; i < fieldCount ; i++ )
197 fieldPositions[i] = fieldPosStorage[i];
199 string.Truncate(0);
200 BStringByteSink stringConverter(&string);
202 icuString.toUTF8(stringConverter);
204 return B_OK;
208 status_t
209 BDateFormat::GetFields(BDateElement*& fields, int& fieldCount,
210 BDateFormatStyle style) const
212 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
213 if (dateFormatter.Get() == NULL)
214 return B_NO_MEMORY;
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);
224 if (U_FAILURE(error))
225 return B_BAD_VALUE;
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 ++;
235 fields = (BDateElement*) malloc(fieldCount * sizeof(BDateElement));
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;
257 return B_OK;
261 status_t
262 BDateFormat::GetStartOfWeek(BWeekday* startOfWeek) const
264 if (startOfWeek == NULL)
265 return B_BAD_VALUE;
267 UErrorCode err = U_ZERO_ERROR;
268 ObjectDeleter<Calendar> calendar = Calendar::createInstance(
269 *BFormattingConventions::Private(&fConventions).ICULocale(), err);
271 if (U_FAILURE(err))
272 return B_ERROR;
274 UCalendarDaysOfWeek icuWeekStart = calendar->getFirstDayOfWeek(err);
275 if (U_FAILURE(err))
276 return B_ERROR;
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;
304 return B_OK;
308 status_t
309 BDateFormat::GetMonthName(int month, BString& outName,
310 const BDateFormatStyle style) const
312 if (style < 0 || style >= B_DATE_FORMAT_STYLE_COUNT)
313 return B_BAD_VALUE;
315 DateFormat* format = _CreateDateFormatter(B_LONG_DATE_FORMAT);
317 SimpleDateFormat* simpleFormat = dynamic_cast<SimpleDateFormat*>(format);
318 if (simpleFormat == NULL) {
319 delete format;
320 return B_ERROR;
323 const DateFormatSymbols* symbols = simpleFormat->getDateFormatSymbols();
325 int32_t count;
326 const UnicodeString* names = symbols->getMonths(count,
327 DateFormatSymbols::STANDALONE, kDateFormatStyleToWidth[style]);
329 if (month > count || month <= 0) {
330 delete simpleFormat;
331 return B_BAD_DATA;
334 BStringByteSink stringConverter(&outName);
335 names[month - 1].toUTF8(stringConverter);
337 delete simpleFormat;
338 return B_OK;
342 status_t
343 BDateFormat::GetDayName(int day, BString& outName,
344 const BDateFormatStyle style) const
346 if (style < 0 || style >= B_DATE_FORMAT_STYLE_COUNT)
347 return B_BAD_VALUE;
349 DateFormat* format = _CreateDateFormatter(B_LONG_DATE_FORMAT);
351 SimpleDateFormat* simpleFormat = dynamic_cast<SimpleDateFormat*>(format);
352 if (simpleFormat == NULL) {
353 delete format;
354 return B_ERROR;
357 const DateFormatSymbols* symbols = simpleFormat->getDateFormatSymbols();
359 int32_t count;
360 const UnicodeString* names = symbols->getWeekdays(count,
361 DateFormatSymbols::STANDALONE, kDateFormatStyleToWidth[style]);
363 if (day >= count || day <= 0) {
364 delete simpleFormat;
365 return B_BAD_DATA;
368 BStringByteSink stringConverter(&outName);
369 names[_ConvertDayNumberToICU(day)].toUTF8(stringConverter);
371 delete simpleFormat;
372 return B_OK;
376 status_t
377 BDateFormat::Parse(BString source, BDateFormatStyle style, BDate& output)
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;
388 ParsePosition p(0);
389 UDate date = dateFormatter->parse(UnicodeString::fromUTF8(source.String()),
392 output.SetDate(1970, 1, 1);
393 output.AddDays(date / U_MILLIS_PER_DAY + 0.5);
395 return B_OK;
399 DateFormat*
400 BDateFormat::_CreateDateFormatter(const BDateFormatStyle style) const
402 Locale* icuLocale
403 = fConventions.UseStringsFromPreferredLanguage()
404 ? BLanguage::Private(&fLanguage).ICULocale()
405 : BFormattingConventions::Private(&fConventions).ICULocale();
407 icu::DateFormat* dateFormatter
408 = icu::DateFormat::createDateInstance(DateFormat::kShort, *icuLocale);
409 if (dateFormatter == NULL)
410 return NULL;
412 SimpleDateFormat* dateFormatterImpl
413 = static_cast<SimpleDateFormat*>(dateFormatter);
415 BString format;
416 fConventions.GetDateFormat(style, format);
418 UnicodeString pattern(format.String());
419 dateFormatterImpl->applyPattern(pattern);
421 return dateFormatter;