xref: /haiku/src/kits/locale/TimeFormat.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
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 
27 U_NAMESPACE_USE
28 
29 
30 BTimeFormat::BTimeFormat()
31 	: BFormat()
32 {
33 }
34 
35 
36 BTimeFormat::BTimeFormat(const BLanguage& language,
37 	const BFormattingConventions& conventions)
38 	: BFormat(language, conventions)
39 {
40 }
41 
42 
43 BTimeFormat::BTimeFormat(const BTimeFormat &other)
44 	: BFormat(other)
45 {
46 }
47 
48 
49 BTimeFormat::~BTimeFormat()
50 {
51 }
52 
53 
54 void
55 BTimeFormat::SetTimeFormat(BTimeFormatStyle style,
56 	const BString& format)
57 {
58 	fConventions.SetExplicitTimeFormat(style, format);
59 }
60 
61 
62 // #pragma mark - Formatting
63 
64 
65 ssize_t
66 BTimeFormat::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 
86 status_t
87 BTimeFormat::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 
113 status_t
114 BTimeFormat::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 
153 status_t
154 BTimeFormat::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 
209 status_t
210 BTimeFormat::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 
230 DateFormat*
231 BTimeFormat::_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