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