BPicture: Fix archive constructor.
[haiku.git] / src / kits / locale / DateFormat.cpp
blob6542c3ba3e78ec8f4b0a7f36f48d0fa8ab6127ed
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 BDateFormat::BDateFormat(const BLocale* locale)
31 : BFormat(locale)
36 BDateFormat::BDateFormat(const BLanguage& language,
37 const BFormattingConventions& conventions)
38 : BFormat(language, conventions)
43 BDateFormat::BDateFormat(const BDateFormat &other)
44 : BFormat(other)
49 BDateFormat::~BDateFormat()
54 status_t
55 BDateFormat::GetDateFormat(BDateFormatStyle style,
56 BString& outFormat) const
58 return fConventions.GetDateFormat(style, outFormat);
62 void
63 BDateFormat::SetDateFormat(BDateFormatStyle style,
64 const BString& format)
66 fConventions.SetExplicitDateFormat(style, format);
70 ssize_t
71 BDateFormat::Format(char* string, const size_t maxSize, const time_t time,
72 const BDateFormatStyle style) const
74 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
75 if (dateFormatter.Get() == NULL)
76 return B_NO_MEMORY;
78 UnicodeString icuString;
79 dateFormatter->format((UDate)time * 1000, icuString);
81 CheckedArrayByteSink stringConverter(string, maxSize);
82 icuString.toUTF8(stringConverter);
84 if (stringConverter.Overflowed())
85 return B_BAD_VALUE;
87 return stringConverter.NumberOfBytesWritten();
91 status_t
92 BDateFormat::Format(BString& string, const time_t time,
93 const BDateFormatStyle style, const BTimeZone* timeZone) const
95 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
96 if (dateFormatter.Get() == NULL)
97 return B_NO_MEMORY;
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());
107 UnicodeString icuString;
108 dateFormatter->format((UDate)time * 1000, icuString);
110 string.Truncate(0);
111 BStringByteSink stringConverter(&string);
112 icuString.toUTF8(stringConverter);
114 return B_OK;
118 status_t
119 BDateFormat::Format(BString& string, const BDate& time,
120 const BDateFormatStyle style, const BTimeZone* timeZone) const
122 if (!time.IsValid())
123 return B_BAD_DATA;
125 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
126 if (dateFormatter.Get() == NULL)
127 return B_NO_MEMORY;
129 UErrorCode err = U_ZERO_ERROR;
130 ObjectDeleter<Calendar> calendar(Calendar::createInstance(err));
131 if (!U_SUCCESS(err))
132 return B_NO_MEMORY;
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());
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());
147 UnicodeString icuString;
148 FieldPosition p;
149 dateFormatter->format(*calendar.Get(), icuString, p);
151 string.Truncate(0);
152 BStringByteSink stringConverter(&string);
153 icuString.toUTF8(stringConverter);
155 return B_OK;
159 status_t
160 BDateFormat::Format(BString& string, int*& fieldPositions, int& fieldCount,
161 const time_t time, const BDateFormatStyle style) const
163 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
164 if (dateFormatter.Get() == NULL)
165 return B_NO_MEMORY;
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);
174 if (error != U_ZERO_ERROR)
175 return B_BAD_VALUE;
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;
186 fieldPositions = (int*) malloc(fieldCount * sizeof(int));
188 for (int i = 0 ; i < fieldCount ; i++ )
189 fieldPositions[i] = fieldPosStorage[i];
191 string.Truncate(0);
192 BStringByteSink stringConverter(&string);
194 icuString.toUTF8(stringConverter);
196 return B_OK;
200 status_t
201 BDateFormat::GetFields(BDateElement*& fields, int& fieldCount,
202 BDateFormatStyle style) const
204 ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
205 if (dateFormatter.Get() == NULL)
206 return B_NO_MEMORY;
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);
216 if (U_FAILURE(error))
217 return B_BAD_VALUE;
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 ++;
227 fields = (BDateElement*) malloc(fieldCount * sizeof(BDateElement));
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;
246 return B_OK;
250 status_t
251 BDateFormat::GetStartOfWeek(BWeekday* startOfWeek) const
253 if (startOfWeek == NULL)
254 return B_BAD_VALUE;
256 UErrorCode err = U_ZERO_ERROR;
257 ObjectDeleter<Calendar> calendar = Calendar::createInstance(
258 *BFormattingConventions::Private(&fConventions).ICULocale(), err);
260 if (U_FAILURE(err))
261 return B_ERROR;
263 UCalendarDaysOfWeek icuWeekStart = calendar->getFirstDayOfWeek(err);
264 if (U_FAILURE(err))
265 return B_ERROR;
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;
293 return B_OK;
297 status_t
298 BDateFormat::GetMonthName(int month, BString& outName)
300 DateFormat* format = _CreateDateFormatter(B_LONG_DATE_FORMAT);
302 SimpleDateFormat* simpleFormat = dynamic_cast<SimpleDateFormat*>(format);
303 if (simpleFormat == NULL) {
304 delete format;
305 return B_ERROR;
308 const DateFormatSymbols* symbols = simpleFormat->getDateFormatSymbols();
310 int32_t count;
311 const UnicodeString* names = symbols->getMonths(count);
313 if (month > count || month <= 0) {
314 delete simpleFormat;
315 return B_BAD_DATA;
318 BStringByteSink stringConverter(&outName);
319 names[month - 1].toUTF8(stringConverter);
321 delete simpleFormat;
322 return B_OK;
326 status_t
327 BDateFormat::Parse(BString source, BDateFormatStyle style, BDate& output)
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;
338 ParsePosition p(0);
339 UDate date = dateFormatter->parse(UnicodeString::fromUTF8(source.String()),
342 output.SetDate(1970, 1, 1);
343 output.AddDays(date / U_MILLIS_PER_DAY + 0.5);
345 return B_OK;
349 DateFormat*
350 BDateFormat::_CreateDateFormatter(const BDateFormatStyle style) const
352 Locale* icuLocale
353 = fConventions.UseStringsFromPreferredLanguage()
354 ? BLanguage::Private(&fLanguage).ICULocale()
355 : BFormattingConventions::Private(&fConventions).ICULocale();
357 icu::DateFormat* dateFormatter
358 = icu::DateFormat::createDateInstance(DateFormat::kShort, *icuLocale);
359 if (dateFormatter == NULL)
360 return NULL;
362 SimpleDateFormat* dateFormatterImpl
363 = static_cast<SimpleDateFormat*>(dateFormatter);
365 BString format;
366 fConventions.GetDateFormat(style, format);
368 UnicodeString pattern(format.String());
369 dateFormatterImpl->applyPattern(pattern);
371 return dateFormatter;