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