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
30static const DateFormatSymbols::DtWidthType kDateFormatStyleToWidth[] = {
31	DateFormatSymbols::WIDE,
32	DateFormatSymbols::ABBREVIATED,
33	DateFormatSymbols::SHORT,
34	DateFormatSymbols::NARROW,
35};
36
37
38BDateFormat::BDateFormat(const BLocale* locale)
39	: BFormat(locale)
40{
41}
42
43
44BDateFormat::BDateFormat(const BLanguage& language,
45	const BFormattingConventions& conventions)
46	: BFormat(language, conventions)
47{
48}
49
50
51BDateFormat::BDateFormat(const BDateFormat &other)
52	: BFormat(other)
53{
54}
55
56
57BDateFormat::~BDateFormat()
58{
59}
60
61
62status_t
63BDateFormat::GetDateFormat(BDateFormatStyle style,
64	BString& outFormat) const
65{
66	return fConventions.GetDateFormat(style, outFormat);
67}
68
69
70void
71BDateFormat::SetDateFormat(BDateFormatStyle style,
72	const BString& format)
73{
74	fConventions.SetExplicitDateFormat(style, format);
75}
76
77
78ssize_t
79BDateFormat::Format(char* string, const size_t maxSize, const time_t time,
80	const BDateFormatStyle style) const
81{
82	ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
83	if (dateFormatter.Get() == NULL)
84		return B_NO_MEMORY;
85
86	UnicodeString icuString;
87	dateFormatter->format((UDate)time * 1000, icuString);
88
89	CheckedArrayByteSink stringConverter(string, maxSize);
90	icuString.toUTF8(stringConverter);
91
92	if (stringConverter.Overflowed())
93		return B_BAD_VALUE;
94
95	return stringConverter.NumberOfBytesWritten();
96}
97
98
99status_t
100BDateFormat::Format(BString& string, const time_t time,
101	const BDateFormatStyle style, const BTimeZone* timeZone) const
102{
103	ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
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
126status_t
127BDateFormat::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	ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
134	if (dateFormatter.Get() == NULL)
135		return B_NO_MEMORY;
136
137	UErrorCode err = U_ZERO_ERROR;
138	ObjectDeleter<Calendar> calendar(Calendar::createInstance(err));
139	if (!U_SUCCESS(err))
140		return B_NO_MEMORY;
141
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());
149	}
150
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());
154
155	UnicodeString icuString;
156	FieldPosition p;
157	dateFormatter->format(*calendar.Get(), icuString, p);
158
159	string.Truncate(0);
160	BStringByteSink stringConverter(&string);
161	icuString.toUTF8(stringConverter);
162
163	return B_OK;
164}
165
166
167status_t
168BDateFormat::Format(BString& string, int*& fieldPositions, int& fieldCount,
169	const time_t time, const BDateFormatStyle style) const
170{
171	ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
172	if (dateFormatter.Get() == NULL)
173		return B_NO_MEMORY;
174
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);
181
182	if (error != U_ZERO_ERROR)
183		return B_BAD_VALUE;
184
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;
192	}
193
194	fieldPositions = (int*) malloc(fieldCount * sizeof(int));
195
196	for (int i = 0 ; i < fieldCount ; i++ )
197		fieldPositions[i] = fieldPosStorage[i];
198
199	string.Truncate(0);
200	BStringByteSink stringConverter(&string);
201
202	icuString.toUTF8(stringConverter);
203
204	return B_OK;
205}
206
207
208status_t
209BDateFormat::GetFields(BDateElement*& fields, int& fieldCount,
210	BDateFormatStyle style) const
211{
212	ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
213	if (dateFormatter.Get() == NULL)
214		return B_NO_MEMORY;
215
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);
223
224	if (U_FAILURE(error))
225		return B_BAD_VALUE;
226
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 ++;
233	}
234
235	fields = (BDateElement*) malloc(fieldCount * sizeof(BDateElement));
236
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;
254		}
255	}
256
257	return B_OK;
258}
259
260
261status_t
262BDateFormat::GetStartOfWeek(BWeekday* startOfWeek) const
263{
264	if (startOfWeek == NULL)
265		return B_BAD_VALUE;
266
267	UErrorCode err = U_ZERO_ERROR;
268	ObjectDeleter<Calendar> calendar = Calendar::createInstance(
269		*BFormattingConventions::Private(&fConventions).ICULocale(), err);
270
271	if (U_FAILURE(err))
272		return B_ERROR;
273
274	UCalendarDaysOfWeek icuWeekStart = calendar->getFirstDayOfWeek(err);
275	if (U_FAILURE(err))
276		return B_ERROR;
277
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;
302	}
303
304	return B_OK;
305}
306
307
308status_t
309BDateFormat::GetMonthName(int month, BString& outName,
310	const BDateFormatStyle style) const
311{
312	if (style < 0 || style >= B_DATE_FORMAT_STYLE_COUNT)
313		return B_BAD_VALUE;
314
315	DateFormat* format = _CreateDateFormatter(B_LONG_DATE_FORMAT);
316
317	SimpleDateFormat* simpleFormat = dynamic_cast<SimpleDateFormat*>(format);
318	if (simpleFormat == NULL) {
319		delete format;
320		return B_ERROR;
321	}
322
323	const DateFormatSymbols* symbols = simpleFormat->getDateFormatSymbols();
324
325	int32_t count;
326	const UnicodeString* names = symbols->getMonths(count,
327		DateFormatSymbols::STANDALONE, kDateFormatStyleToWidth[style]);
328
329	if (month > count || month <= 0) {
330		delete simpleFormat;
331		return B_BAD_DATA;
332	}
333
334	BStringByteSink stringConverter(&outName);
335	names[month - 1].toUTF8(stringConverter);
336
337	delete simpleFormat;
338	return B_OK;
339}
340
341
342status_t
343BDateFormat::GetDayName(int day, BString& outName,
344	const BDateFormatStyle style) const
345{
346	if (style < 0 || style >= B_DATE_FORMAT_STYLE_COUNT)
347		return B_BAD_VALUE;
348
349	DateFormat* format = _CreateDateFormatter(B_LONG_DATE_FORMAT);
350
351	SimpleDateFormat* simpleFormat = dynamic_cast<SimpleDateFormat*>(format);
352	if (simpleFormat == NULL) {
353		delete format;
354		return B_ERROR;
355	}
356
357	const DateFormatSymbols* symbols = simpleFormat->getDateFormatSymbols();
358
359	int32_t count;
360	const UnicodeString* names = symbols->getWeekdays(count,
361		DateFormatSymbols::STANDALONE, kDateFormatStyleToWidth[style]);
362
363	if (day >= count || day <= 0) {
364		delete simpleFormat;
365		return B_BAD_DATA;
366	}
367
368	BStringByteSink stringConverter(&outName);
369	names[_ConvertDayNumberToICU(day)].toUTF8(stringConverter);
370
371	delete simpleFormat;
372	return B_OK;
373}
374
375
376status_t
377BDateFormat::Parse(BString source, BDateFormatStyle style, BDate& output)
378{
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;
387
388	ParsePosition p(0);
389	UDate date = dateFormatter->parse(UnicodeString::fromUTF8(source.String()),
390		p);
391
392	output.SetDate(1970, 1, 1);
393	output.AddDays(date / U_MILLIS_PER_DAY + 0.5);
394
395	return B_OK;
396}
397
398
399DateFormat*
400BDateFormat::_CreateDateFormatter(const BDateFormatStyle style) const
401{
402	Locale* icuLocale
403		= fConventions.UseStringsFromPreferredLanguage()
404			? BLanguage::Private(&fLanguage).ICULocale()
405			: BFormattingConventions::Private(&fConventions).ICULocale();
406
407	icu::DateFormat* dateFormatter
408		= icu::DateFormat::createDateInstance(DateFormat::kShort, *icuLocale);
409	if (dateFormatter == NULL)
410		return NULL;
411
412	SimpleDateFormat* dateFormatterImpl
413		= static_cast<SimpleDateFormat*>(dateFormatter);
414
415	BString format;
416	fConventions.GetDateFormat(style, format);
417
418	UnicodeString pattern(format.String());
419	dateFormatterImpl->applyPattern(pattern);
420
421	return dateFormatter;
422}
423
424