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 */
8
9#include <unicode/uversion.h>
10#include <TimeFormat.h>
11
12#include <AutoDeleter.h>
13#include <Autolock.h>
14#include <DateTime.h>
15#include <FormattingConventionsPrivate.h>
16#include <LanguagePrivate.h>
17#include <TimeZone.h>
18
19#include <ICUWrapper.h>
20
21#include <unicode/datefmt.h>
22#include <unicode/smpdtfmt.h>
23
24#include <vector>
25
26
27U_NAMESPACE_USE
28
29
30BTimeFormat::BTimeFormat()
31	: BFormat()
32{
33}
34
35
36BTimeFormat::BTimeFormat(const BLanguage& language,
37	const BFormattingConventions& conventions)
38	: BFormat(language, conventions)
39{
40}
41
42
43BTimeFormat::BTimeFormat(const BTimeFormat &other)
44	: BFormat(other)
45{
46}
47
48
49BTimeFormat::~BTimeFormat()
50{
51}
52
53
54void
55BTimeFormat::SetTimeFormat(BTimeFormatStyle style,
56	const BString& format)
57{
58	fConventions.SetExplicitTimeFormat(style, format);
59}
60
61
62// #pragma mark - Formatting
63
64
65ssize_t
66BTimeFormat::Format(char* string, size_t maxSize, time_t time,
67	BTimeFormatStyle style) const
68{
69	ObjectDeleter<DateFormat> timeFormatter(_CreateTimeFormatter(style));
70	if (!timeFormatter.IsSet())
71		return B_NO_MEMORY;
72
73	UnicodeString icuString;
74	timeFormatter->format((UDate)time * 1000, icuString);
75
76	CheckedArrayByteSink stringConverter(string, maxSize);
77	icuString.toUTF8(stringConverter);
78
79	if (stringConverter.Overflowed())
80		return B_BAD_VALUE;
81
82	return stringConverter.NumberOfBytesWritten();
83}
84
85
86status_t
87BTimeFormat::Format(BString& string, const time_t time,
88	const BTimeFormatStyle style, const BTimeZone* timeZone) const
89{
90	ObjectDeleter<DateFormat> timeFormatter(_CreateTimeFormatter(style));
91	if (!timeFormatter.IsSet())
92		return B_NO_MEMORY;
93
94	if (timeZone != NULL) {
95		ObjectDeleter<TimeZone> icuTimeZone(
96			TimeZone::createTimeZone(timeZone->ID().String()));
97		if (!icuTimeZone.IsSet())
98			return B_NO_MEMORY;
99		timeFormatter->setTimeZone(*icuTimeZone.Get());
100	}
101
102	UnicodeString icuString;
103	timeFormatter->format((UDate)time * 1000, icuString);
104
105	string.Truncate(0);
106	BStringByteSink stringConverter(&string);
107	icuString.toUTF8(stringConverter);
108
109	return B_OK;
110}
111
112
113status_t
114BTimeFormat::Format(BString& string, int*& fieldPositions, int& fieldCount,
115	time_t time, BTimeFormatStyle style) const
116{
117	ObjectDeleter<DateFormat> timeFormatter(_CreateTimeFormatter(style));
118	if (!timeFormatter.IsSet())
119		return B_NO_MEMORY;
120
121	fieldPositions = NULL;
122	UErrorCode error = U_ZERO_ERROR;
123	icu::FieldPositionIterator positionIterator;
124	UnicodeString icuString;
125	timeFormatter->format((UDate)time * 1000, icuString, &positionIterator,
126		error);
127
128	if (error != U_ZERO_ERROR)
129		return B_BAD_VALUE;
130
131	icu::FieldPosition field;
132	std::vector<int> fieldPosStorage;
133	fieldCount  = 0;
134	while (positionIterator.next(field)) {
135		fieldPosStorage.push_back(field.getBeginIndex());
136		fieldPosStorage.push_back(field.getEndIndex());
137		fieldCount += 2;
138	}
139
140	fieldPositions = (int*) malloc(fieldCount * sizeof(int));
141
142	for (int i = 0 ; i < fieldCount ; i++ )
143		fieldPositions[i] = fieldPosStorage[i];
144
145	string.Truncate(0);
146	BStringByteSink stringConverter(&string);
147	icuString.toUTF8(stringConverter);
148
149	return B_OK;
150}
151
152
153status_t
154BTimeFormat::GetTimeFields(BDateElement*& fields, int& fieldCount,
155	BTimeFormatStyle style) const
156{
157	ObjectDeleter<DateFormat> timeFormatter(_CreateTimeFormatter(style));
158	if (!timeFormatter.IsSet())
159		return B_NO_MEMORY;
160
161	fields = NULL;
162	UErrorCode error = U_ZERO_ERROR;
163	icu::FieldPositionIterator positionIterator;
164	UnicodeString icuString;
165	time_t now;
166	timeFormatter->format((UDate)time(&now) * 1000, icuString,
167		&positionIterator, error);
168
169	if (error != U_ZERO_ERROR)
170		return B_BAD_VALUE;
171
172	icu::FieldPosition field;
173	std::vector<int> fieldPosStorage;
174	fieldCount  = 0;
175	while (positionIterator.next(field)) {
176		fieldPosStorage.push_back(field.getField());
177		fieldCount ++;
178	}
179
180	fields = (BDateElement*) malloc(fieldCount * sizeof(BDateElement));
181
182	for (int i = 0 ; i < fieldCount ; i++ ) {
183		switch (fieldPosStorage[i]) {
184			case UDAT_HOUR_OF_DAY1_FIELD:
185			case UDAT_HOUR_OF_DAY0_FIELD:
186			case UDAT_HOUR1_FIELD:
187			case UDAT_HOUR0_FIELD:
188				fields[i] = B_DATE_ELEMENT_HOUR;
189				break;
190			case UDAT_MINUTE_FIELD:
191				fields[i] = B_DATE_ELEMENT_MINUTE;
192				break;
193			case UDAT_SECOND_FIELD:
194				fields[i] = B_DATE_ELEMENT_SECOND;
195				break;
196			case UDAT_AM_PM_FIELD:
197				fields[i] = B_DATE_ELEMENT_AM_PM;
198				break;
199			default:
200				fields[i] = B_DATE_ELEMENT_INVALID;
201				break;
202		}
203	}
204
205	return B_OK;
206}
207
208
209status_t
210BTimeFormat::Parse(BString source, BTimeFormatStyle style, BTime& output)
211{
212	ObjectDeleter<DateFormat> timeFormatter(_CreateTimeFormatter(style));
213	if (!timeFormatter.IsSet())
214		return B_NO_MEMORY;
215
216	// If no timezone is specified in the time string, assume GMT
217	timeFormatter->setTimeZone(*icu::TimeZone::getGMT());
218
219	ParsePosition p(0);
220	UDate date = timeFormatter->parse(UnicodeString::fromUTF8(source.String()),
221		p);
222
223	output.SetTime(0, 0, 0);
224	output.AddMilliseconds(date);
225
226	return B_OK;
227}
228
229
230DateFormat*
231BTimeFormat::_CreateTimeFormatter(const BTimeFormatStyle style) const
232{
233	Locale* icuLocale
234		= fConventions.UseStringsFromPreferredLanguage()
235			? BLanguage::Private(&fLanguage).ICULocale()
236			: BFormattingConventions::Private(&fConventions).ICULocale();
237
238	icu::DateFormat* timeFormatter
239		= icu::DateFormat::createTimeInstance(DateFormat::kShort, *icuLocale);
240	if (timeFormatter == NULL)
241		return NULL;
242
243	SimpleDateFormat* timeFormatterImpl
244		= static_cast<SimpleDateFormat*>(timeFormatter);
245
246	BString format;
247	fConventions.GetTimeFormat(style, format);
248
249	UnicodeString pattern(format.String());
250	timeFormatterImpl->applyPattern(pattern);
251
252	return timeFormatter;
253}
254