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