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
30U_NAMESPACE_USE
31
32
33static const DateFormatSymbols::DtWidthType kDateFormatStyleToWidth[] = {
34	DateFormatSymbols::WIDE,
35	DateFormatSymbols::ABBREVIATED,
36	DateFormatSymbols::SHORT,
37	DateFormatSymbols::NARROW,
38};
39
40
41BDateFormat::BDateFormat(const BLocale* locale)
42	: BFormat(locale)
43{
44}
45
46
47BDateFormat::BDateFormat(const BLanguage& language,
48	const BFormattingConventions& conventions)
49	: BFormat(language, conventions)
50{
51}
52
53
54BDateFormat::BDateFormat(const BDateFormat &other)
55	: BFormat(other)
56{
57}
58
59
60BDateFormat::~BDateFormat()
61{
62}
63
64
65status_t
66BDateFormat::GetDateFormat(BDateFormatStyle style,
67	BString& outFormat) const
68{
69	return fConventions.GetDateFormat(style, outFormat);
70}
71
72
73void
74BDateFormat::SetDateFormat(BDateFormatStyle style,
75	const BString& format)
76{
77	fConventions.SetExplicitDateFormat(style, format);
78}
79
80
81ssize_t
82BDateFormat::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.IsSet())
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
102status_t
103BDateFormat::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.IsSet())
108		return B_NO_MEMORY;
109
110	if (timeZone != NULL) {
111		ObjectDeleter<TimeZone> icuTimeZone(
112			TimeZone::createTimeZone(timeZone->ID().String()));
113		if (!icuTimeZone.IsSet())
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
129status_t
130BDateFormat::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.IsSet())
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.IsSet())
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
170status_t
171BDateFormat::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.IsSet())
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
211status_t
212BDateFormat::GetFields(BDateElement*& fields, int& fieldCount,
213	BDateFormatStyle style) const
214{
215	ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
216	if (!dateFormatter.IsSet())
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
264status_t
265BDateFormat::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
311status_t
312BDateFormat::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
345status_t
346BDateFormat::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
379status_t
380BDateFormat::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.IsSet())
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
402DateFormat*
403BDateFormat::_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