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