xref: /haiku/src/system/libroot/add-ons/icu/ICUTimeData.cpp (revision 1294543de9ac0eff000eaea1b18368c36435d08e)
1 /*
2  * Copyright 2010, Oliver Tappe, zooey@hirschkaefer.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ICUTimeData.h"
8 
9 #include <langinfo.h>
10 #include <strings.h>
11 
12 #include <unicode/dtfmtsym.h>
13 #include <unicode/gregocal.h>
14 #include <unicode/smpdtfmt.h>
15 
16 #include <AutoDeleter.h>
17 
18 
19 namespace BPrivate {
20 
21 
22 ICUTimeData::ICUTimeData(struct lc_time_t& lcTimeInfo)
23 	:
24 	fLCTimeInfo(lcTimeInfo),
25 	fDataBridge(NULL)
26 {
27 	for (int i = 0; i < 12; ++i) {
28 		fLCTimeInfo.mon[i] = fMon[i];
29 		fLCTimeInfo.month[i] = fMonth[i];
30 		fLCTimeInfo.alt_month[i] = fAltMonth[i];
31 	}
32 	for (int i = 0; i < 7; ++i) {
33 		fLCTimeInfo.wday[i] = fWday[i];
34 		fLCTimeInfo.weekday[i] = fWeekday[i];
35 	}
36 	fLCTimeInfo.X_fmt = fTimeFormat;
37 	fLCTimeInfo.x_fmt = fDateFormat;
38 	fLCTimeInfo.c_fmt = fDateTimeFormat;
39 	fLCTimeInfo.am = fAm;
40 	fLCTimeInfo.pm = fPm;
41 	fLCTimeInfo.date_fmt = fDateTimeZoneFormat;
42 	fLCTimeInfo.md_order = fMonthDayOrder;
43 	fLCTimeInfo.ampm_fmt = fAmPmFormat;
44 }
45 
46 
47 ICUTimeData::~ICUTimeData()
48 {
49 }
50 
51 
52 void
53 ICUTimeData::Initialize(LocaleTimeDataBridge* dataBridge)
54 {
55 	fDataBridge = dataBridge;
56 }
57 
58 
59 status_t
60 ICUTimeData::SetTo(const Locale& locale, const char* posixLocaleName)
61 {
62 	status_t result = inherited::SetTo(locale, posixLocaleName);
63 	if (result != B_OK)
64 		return result;
65 
66 	UErrorCode icuStatus = U_ZERO_ERROR;
67 	DateFormatSymbols formatSymbols(fLocale, icuStatus);
68 	if (!U_SUCCESS(icuStatus))
69 		return B_UNSUPPORTED;
70 
71 	int count = 0;
72 	const UnicodeString* strings = formatSymbols.getShortMonths(count);
73 	result = _SetLCTimeEntries(strings, fMon[0], sizeof(fMon[0]), count, 12);
74 
75 	if (result == B_OK) {
76 		strings = formatSymbols.getMonths(count);
77 		result = _SetLCTimeEntries(strings, fMonth[0], sizeof(fMonth[0]), count,
78 			12);
79 	}
80 
81 	if (result == B_OK) {
82 		strings = formatSymbols.getShortWeekdays(count);
83 		if (count == 8 && strings[0].length() == 0) {
84 			// ICUs weekday arrays are 1-based
85 			strings++;
86 			count = 7;
87 		}
88 		result
89 			= _SetLCTimeEntries(strings, fWday[0], sizeof(fWday[0]), count, 7);
90 	}
91 
92 	if (result == B_OK) {
93 		strings = formatSymbols.getWeekdays(count);
94 		if (count == 8 && strings[0].length() == 0) {
95 			// ICUs weekday arrays are 1-based
96 			strings++;
97 			count = 7;
98 		}
99 		result = _SetLCTimeEntries(strings, fWeekday[0], sizeof(fWeekday[0]),
100 			count, 7);
101 	}
102 
103 	if (result == B_OK) {
104 		try {
105 			DateFormat* format = DateFormat::createTimeInstance(
106 				DateFormat::kDefault, fLocale);
107 			result = _SetLCTimePattern(format, fTimeFormat, sizeof(fTimeFormat));
108 			delete format;
109 		} catch(...) {
110 			result = B_NO_MEMORY;
111 		}
112 	}
113 
114 	if (result == B_OK) {
115 		try {
116 			DateFormat* format = DateFormat::createDateInstance(
117 				DateFormat::kDefault, fLocale);
118 			result = _SetLCTimePattern(format, fDateFormat, sizeof(fDateFormat));
119 			delete format;
120 		} catch(...) {
121 			result = B_NO_MEMORY;
122 		}
123 	}
124 
125 	if (result == B_OK) {
126 		try {
127 			DateFormat* format = DateFormat::createDateTimeInstance(
128 				DateFormat::kFull, DateFormat::kFull, fLocale);
129 			result = _SetLCTimePattern(format, fDateTimeFormat,
130 				sizeof(fDateTimeFormat));
131 			delete format;
132 		} catch(...) {
133 			result = B_NO_MEMORY;
134 		}
135 	}
136 
137 	if (result == B_OK) {
138 		strings = formatSymbols.getAmPmStrings(count);
139 		result = _SetLCTimeEntries(strings, fAm, sizeof(fAm), 1, 1);
140 		if (result == B_OK)
141 			result = _SetLCTimeEntries(&strings[1], fPm, sizeof(fPm), 1, 1);
142 	}
143 
144 	if (result == B_OK) {
145 		strings = formatSymbols.getMonths(count, DateFormatSymbols::STANDALONE,
146 			DateFormatSymbols::WIDE);
147 		result = _SetLCTimeEntries(strings, fAltMonth[0], sizeof(fAltMonth[0]),
148 			count, 12);
149 	}
150 
151 	strcpy(fAmPmFormat, fDataBridge->posixLCTimeInfo->ampm_fmt);
152 		// ICU does not provide anything for this (and that makes sense, too)
153 
154 	return result;
155 }
156 
157 
158 status_t
159 ICUTimeData::SetToPosix()
160 {
161 	status_t result = inherited::SetToPosix();
162 
163 	if (result == B_OK) {
164 		for (int i = 0; i < 12; ++i) {
165 			strcpy(fMon[i], fDataBridge->posixLCTimeInfo->mon[i]);
166 			strcpy(fMonth[i], fDataBridge->posixLCTimeInfo->month[i]);
167 			strcpy(fAltMonth[i], fDataBridge->posixLCTimeInfo->alt_month[i]);
168 		}
169 		for (int i = 0; i < 7; ++i) {
170 			strcpy(fWday[i], fDataBridge->posixLCTimeInfo->wday[i]);
171 			strcpy(fWeekday[i], fDataBridge->posixLCTimeInfo->weekday[i]);
172 		}
173 		strcpy(fTimeFormat, fDataBridge->posixLCTimeInfo->X_fmt);
174 		strcpy(fDateFormat, fDataBridge->posixLCTimeInfo->x_fmt);
175 		strcpy(fDateTimeFormat, fDataBridge->posixLCTimeInfo->c_fmt);
176 		strcpy(fAm, fDataBridge->posixLCTimeInfo->am);
177 		strcpy(fPm, fDataBridge->posixLCTimeInfo->pm);
178 		strcpy(fDateTimeZoneFormat, fDataBridge->posixLCTimeInfo->date_fmt);
179 		strcpy(fMonthDayOrder, fDataBridge->posixLCTimeInfo->md_order);
180 		strcpy(fAmPmFormat, fDataBridge->posixLCTimeInfo->ampm_fmt);
181 	}
182 
183 	return result;
184 }
185 
186 
187 const char*
188 ICUTimeData::GetLanginfo(int index)
189 {
190 	switch(index) {
191 		case D_T_FMT:
192 			return fDateTimeFormat;
193 		case D_FMT:
194 			return fDateFormat;
195 		case T_FMT:
196 			return fTimeFormat;
197 		case T_FMT_AMPM:
198 			return fAmPmFormat;
199 		case AM_STR:
200 			return fAm;
201 		case PM_STR:
202 			return fPm;
203 
204 		case DAY_1:
205 		case DAY_2:
206 		case DAY_3:
207 		case DAY_4:
208 		case DAY_5:
209 		case DAY_6:
210 		case DAY_7:
211 			return fWeekday[index - DAY_1];
212 
213 		case ABDAY_1:
214 		case ABDAY_2:
215 		case ABDAY_3:
216 		case ABDAY_4:
217 		case ABDAY_5:
218 		case ABDAY_6:
219 		case ABDAY_7:
220 			return fWday[index - ABDAY_1];
221 
222 		case MON_1:
223 		case MON_2:
224 		case MON_3:
225 		case MON_4:
226 		case MON_5:
227 		case MON_6:
228 		case MON_7:
229 		case MON_8:
230 		case MON_9:
231 		case MON_10:
232 		case MON_11:
233 		case MON_12:
234 			return fMonth[index - MON_1];
235 
236 		case ABMON_1:
237 		case ABMON_2:
238 		case ABMON_3:
239 		case ABMON_4:
240 		case ABMON_5:
241 		case ABMON_6:
242 		case ABMON_7:
243 		case ABMON_8:
244 		case ABMON_9:
245 		case ABMON_10:
246 		case ABMON_11:
247 		case ABMON_12:
248 			return fMon[index - ABMON_1];
249 
250 		default:
251 			return "";
252 	}
253 }
254 
255 
256 status_t
257 ICUTimeData::_SetLCTimeEntries(const UnicodeString* strings, char* destination,
258 	int entrySize, int count, int maxCount)
259 {
260 	if (strings == NULL)
261 		return B_ERROR;
262 
263 	status_t result = B_OK;
264 	if (count > maxCount)
265 		count = maxCount;
266 	for (int32 i = 0; result == B_OK && i < count; ++i) {
267 		result = _ConvertUnicodeStringToLocaleconvEntry(strings[i], destination,
268 			entrySize);
269 		destination += entrySize;
270 	}
271 
272 	return result;
273 }
274 
275 
276 status_t
277 ICUTimeData::_SetLCTimePattern(DateFormat* format, char* destination,
278 	int destinationSize)
279 {
280 	SimpleDateFormat* simpleFormat = dynamic_cast<SimpleDateFormat*>(format);
281 	if (!simpleFormat)
282 		return B_BAD_TYPE;
283 
284 	// convert ICU-type pattern to posix (i.e. strftime()) format string
285 	UnicodeString icuPattern;
286 	simpleFormat->toPattern(icuPattern);
287 	UnicodeString posixPattern;
288 	if (icuPattern.length() > 0) {
289 		UChar lastCharSeen = 0;
290 		int lastCharCount = 1;
291 		bool inSingleQuotes = false;
292 		bool inDoubleQuotes = false;
293 		// we loop one character past the end on purpose, which will result in a
294 		// final -1 char to be processed, which in turn will let us handle the
295 		// last character (via lastCharSeen)
296 		for (int i = 0; i <= icuPattern.length(); ++i) {
297 			UChar currChar = icuPattern.charAt(i);
298 			if (lastCharSeen != 0 && currChar == lastCharSeen) {
299 				lastCharCount++;
300 				continue;
301 			}
302 
303 			if (!inSingleQuotes && !inDoubleQuotes) {
304 				switch (lastCharSeen) {
305 					case L'a':
306 						posixPattern.append(UnicodeString("%p", ""));
307 						break;
308 					case L'd':
309 						if (lastCharCount == 2)
310 							posixPattern.append(UnicodeString("%d", ""));
311 						else
312 							posixPattern.append(UnicodeString("%e", ""));
313 						break;
314 					case L'D':
315 						posixPattern.append(UnicodeString("%j", ""));
316 						break;
317 					case L'c':
318 						// fall through, to handle 'c' the same as 'e'
319 					case L'e':
320 						if (lastCharCount == 4)
321 							posixPattern.append(UnicodeString("%A", ""));
322 						else if (lastCharCount <= 2)
323 							posixPattern.append(UnicodeString("%u", ""));
324 						else
325 							posixPattern.append(UnicodeString("%a", ""));
326 						break;
327 					case L'E':
328 						if (lastCharCount == 4)
329 							posixPattern.append(UnicodeString("%A", ""));
330 						else
331 							posixPattern.append(UnicodeString("%a", ""));
332 						break;
333 					case L'k':
334 						// fall through, to handle 'k' the same as 'h'
335 					case L'h':
336 						if (lastCharCount == 2)
337 							posixPattern.append(UnicodeString("%I", ""));
338 						else
339 							posixPattern.append(UnicodeString("%l", ""));
340 						break;
341 					case L'H':
342 						if (lastCharCount == 2)
343 							posixPattern.append(UnicodeString("%H", ""));
344 						else
345 							posixPattern.append(UnicodeString("%k", ""));
346 						break;
347 					case L'm':
348 						posixPattern.append(UnicodeString("%M", ""));
349 						break;
350 					case L'L':
351 						// fall through, to handle 'L' the same as 'M'
352 					case L'M':
353 						if (lastCharCount == 4)
354 							posixPattern.append(UnicodeString("%B", ""));
355 						else if (lastCharCount == 3)
356 							posixPattern.append(UnicodeString("%b", ""));
357 						else
358 							posixPattern.append(UnicodeString("%m", ""));
359 						break;
360 					case L's':
361 						posixPattern.append(UnicodeString("%S", ""));
362 						break;
363 					case L'w':
364 						posixPattern.append(UnicodeString("%V", ""));
365 						break;
366 					case L'y':
367 						if (lastCharCount == 2)
368 							posixPattern.append(UnicodeString("%y", ""));
369 						else
370 							posixPattern.append(UnicodeString("%Y", ""));
371 						break;
372 					case L'Y':
373 						posixPattern.append(UnicodeString("%G", ""));
374 						break;
375 					case L'z':
376 						posixPattern.append(UnicodeString("%Z", ""));
377 						break;
378 					case L'Z':
379 						posixPattern.append(UnicodeString("%z", ""));
380 						break;
381 					default:
382 						if (lastCharSeen != 0)
383 							posixPattern.append(lastCharSeen);
384 				}
385 			} else {
386 				if (lastCharSeen != 0)
387 					posixPattern.append(lastCharSeen);
388 			}
389 
390 			if (currChar == L'"') {
391 				inDoubleQuotes = !inDoubleQuotes;
392 				lastCharSeen = 0;
393 			} else if (currChar == L'\'') {
394 				inSingleQuotes = !inSingleQuotes;
395 				lastCharSeen = 0;
396 			} else
397 				lastCharSeen = currChar;
398 
399 			lastCharCount = 1;
400 		}
401 	}
402 
403 	return _ConvertUnicodeStringToLocaleconvEntry(posixPattern, destination,
404 		destinationSize);
405 }
406 
407 
408 const Locale&
409 ICUTimeData::ICULocale() const
410 {
411 	return fLocale;
412 
413 }
414 
415 
416 }	// namespace BPrivate
417