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