2 * Copyright 2003-2009, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2009-2010, Adrien Destugues, pulkomandy@gmail.com.
4 * Copyright 2010-2011, Oliver Tappe <zooey@hirschkaefer.de>.
5 * Distributed under the terms of the MIT License.
9 #include <unicode/uversion.h>
10 #include <FormattingConventions.h>
12 #include <AutoDeleter.h>
13 #include <IconUtils.h>
17 #include <LocaleRoster.h>
18 #include <Resources.h>
20 #include <UnicodeChar.h>
22 #include <unicode/datefmt.h>
23 #include <unicode/locid.h>
24 #include <unicode/smpdtfmt.h>
25 #include <unicode/ulocdata.h>
26 #include <ICUWrapper.h>
36 // #pragma mark - helpers
40 FormatUsesAmPm(const BString
& format
)
42 if (format
.Length() == 0)
46 for (const char* s
= format
.String(); *s
!= '\0'; ++s
) {
63 CoerceFormatTo12HourClock(BString
& format
)
65 char* s
= format
.LockBuffer(format
.Length());
69 // change format to use h instead of H, k instead of K, and append an
72 for (; *s
!= '\0'; ++s
) {
87 format
.UnlockBuffer(format
.Length());
94 CoerceFormatTo24HourClock(BString
& format
)
96 char* buffer
= format
.LockBuffer(format
.Length());
97 char* currentPos
= buffer
;
98 if (currentPos
== NULL
)
101 // change the format to use H instead of h, K instead of k, and determine
102 // and remove the am/pm marker (including leading whitespace)
103 bool inQuote
= false;
104 bool lastWasWhitespace
= false;
106 const char* amPmStartPos
= NULL
;
107 const char* amPmEndPos
= NULL
;
108 const char* lastWhitespaceStart
= NULL
;
109 for (char* previousPos
= currentPos
; (ch
= BUnicodeChar::FromUTF8(
110 (const char**)¤tPos
)) != 0; previousPos
= currentPos
) {
125 if (lastWasWhitespace
)
126 amPmStartPos
= lastWhitespaceStart
;
128 amPmStartPos
= previousPos
;
129 amPmEndPos
= currentPos
;
133 if (!inQuote
&& BUnicodeChar::IsWhitespace(ch
)) {
134 if (!lastWasWhitespace
) {
135 lastWhitespaceStart
= previousPos
;
136 lastWasWhitespace
= true;
141 lastWasWhitespace
= false;
144 format
.UnlockBuffer(format
.Length());
145 if (amPmStartPos
!= NULL
&& amPmEndPos
> amPmStartPos
)
146 format
.Remove(amPmStartPos
- buffer
, amPmEndPos
- amPmStartPos
);
151 CoerceFormatToAbbreviatedTimezone(BString
& format
)
153 char* s
= format
.LockBuffer(format
.Length());
157 // replace a single 'z' with 'V'
158 bool inQuote
= false;
159 bool lastWasZ
= false;
160 for (; *s
!= '\0'; ++s
) {
166 if (!inQuote
&& !lastWasZ
&& *(s
+1) != 'z')
173 format
.UnlockBuffer(format
.Length());
177 // #pragma mark - BFormattingConventions
180 enum ClockHoursState
{
181 CLOCK_HOURS_UNSET
= 0,
187 BFormattingConventions::BFormattingConventions(const char* id
)
189 fCachedUse24HourClock(CLOCK_HOURS_UNSET
),
190 fExplicitUse24HourClock(CLOCK_HOURS_UNSET
),
191 fUseStringsFromPreferredLanguage(false),
192 fICULocale(new icu::Locale(id
))
197 BFormattingConventions::BFormattingConventions(
198 const BFormattingConventions
& other
)
200 fCachedNumericFormat(other
.fCachedNumericFormat
),
201 fCachedMonetaryFormat(other
.fCachedMonetaryFormat
),
202 fCachedUse24HourClock(other
.fCachedUse24HourClock
),
203 fExplicitNumericFormat(other
.fExplicitNumericFormat
),
204 fExplicitMonetaryFormat(other
.fExplicitMonetaryFormat
),
205 fExplicitUse24HourClock(other
.fExplicitUse24HourClock
),
206 fUseStringsFromPreferredLanguage(other
.fUseStringsFromPreferredLanguage
),
207 fICULocale(new icu::Locale(*other
.fICULocale
))
209 for (int s
= 0; s
< B_DATE_FORMAT_STYLE_COUNT
; ++s
) {
210 fCachedDateFormats
[s
] = other
.fCachedDateFormats
[s
];
211 fExplicitDateFormats
[s
] = other
.fExplicitDateFormats
[s
];
213 for (int t
= 0; t
< B_TIME_FORMAT_STYLE_COUNT
; ++t
) {
214 fCachedDateTimeFormats
[s
][t
] = other
.fCachedDateFormats
[s
][t
];
215 fExplicitDateFormats
[s
][t
] = other
.fExplicitDateFormats
[s
][t
];
218 for (int s
= 0; s
< B_TIME_FORMAT_STYLE_COUNT
; ++s
) {
219 fCachedTimeFormats
[s
] = other
.fCachedTimeFormats
[s
];
220 fExplicitTimeFormats
[s
] = other
.fExplicitTimeFormats
[s
];
225 BFormattingConventions::BFormattingConventions(const BMessage
* archive
)
227 fCachedUse24HourClock(CLOCK_HOURS_UNSET
),
228 fExplicitUse24HourClock(CLOCK_HOURS_UNSET
),
229 fUseStringsFromPreferredLanguage(false)
231 BString conventionsID
;
232 status_t status
= archive
->FindString("conventions", &conventionsID
);
233 fICULocale
= new icu::Locale(conventionsID
);
235 for (int s
= 0; s
< B_DATE_FORMAT_STYLE_COUNT
&& status
== B_OK
; ++s
) {
237 status
= archive
->FindString("dateFormat", s
, &format
);
239 fExplicitDateFormats
[s
] = format
;
241 status
= archive
->FindString("timeFormat", s
, &format
);
243 fExplicitTimeFormats
[s
] = format
;
246 if (status
== B_OK
) {
248 status
= archive
->FindInt8("use24HourClock", &use24HourClock
);
250 fExplicitUse24HourClock
= use24HourClock
;
252 if (status
== B_OK
) {
253 bool useStringsFromPreferredLanguage
;
254 status
= archive
->FindBool("useStringsFromPreferredLanguage",
255 &useStringsFromPreferredLanguage
);
257 fUseStringsFromPreferredLanguage
= useStringsFromPreferredLanguage
;
262 BFormattingConventions
&
263 BFormattingConventions::operator=(const BFormattingConventions
& other
)
268 for (int s
= 0; s
< B_DATE_FORMAT_STYLE_COUNT
; ++s
) {
269 fCachedDateFormats
[s
] = other
.fCachedDateFormats
[s
];
270 fExplicitDateFormats
[s
] = other
.fExplicitDateFormats
[s
];
271 for (int t
= 0; t
< B_TIME_FORMAT_STYLE_COUNT
; ++t
) {
272 fCachedDateTimeFormats
[s
][t
] = other
.fCachedDateTimeFormats
[s
][t
];
273 fExplicitDateTimeFormats
[s
][t
]
274 = other
.fExplicitDateTimeFormats
[s
][t
];
277 for (int s
= 0; s
< B_TIME_FORMAT_STYLE_COUNT
; ++s
) {
278 fCachedTimeFormats
[s
] = other
.fCachedTimeFormats
[s
];
279 fExplicitTimeFormats
[s
] = other
.fExplicitTimeFormats
[s
];
281 fCachedNumericFormat
= other
.fCachedNumericFormat
;
282 fCachedMonetaryFormat
= other
.fCachedMonetaryFormat
;
283 fCachedUse24HourClock
= other
.fCachedUse24HourClock
;
285 fExplicitNumericFormat
= other
.fExplicitNumericFormat
;
286 fExplicitMonetaryFormat
= other
.fExplicitMonetaryFormat
;
287 fExplicitUse24HourClock
= other
.fExplicitUse24HourClock
;
289 fUseStringsFromPreferredLanguage
= other
.fUseStringsFromPreferredLanguage
;
291 *fICULocale
= *other
.fICULocale
;
297 BFormattingConventions::~BFormattingConventions()
304 BFormattingConventions::operator==(const BFormattingConventions
& other
) const
309 for (int s
= 0; s
< B_DATE_FORMAT_STYLE_COUNT
; ++s
) {
310 if (fExplicitDateFormats
[s
] != other
.fExplicitDateFormats
[s
])
313 for (int s
= 0; s
< B_TIME_FORMAT_STYLE_COUNT
; ++s
) {
314 if (fExplicitTimeFormats
[s
] != other
.fExplicitTimeFormats
[s
])
318 return fExplicitNumericFormat
== other
.fExplicitNumericFormat
319 && fExplicitMonetaryFormat
== other
.fExplicitMonetaryFormat
320 && fExplicitUse24HourClock
== other
.fExplicitUse24HourClock
321 && fUseStringsFromPreferredLanguage
322 == other
.fUseStringsFromPreferredLanguage
323 && *fICULocale
== *other
.fICULocale
;
328 BFormattingConventions::operator!=(const BFormattingConventions
& other
) const
330 return !(*this == other
);
335 BFormattingConventions::ID() const
337 return fICULocale
->getName();
342 BFormattingConventions::LanguageCode() const
344 return fICULocale
->getLanguage();
349 BFormattingConventions::CountryCode() const
351 const char* country
= fICULocale
->getCountry();
352 if (country
== NULL
|| country
[0] == '\0')
360 BFormattingConventions::AreCountrySpecific() const
362 return CountryCode() != NULL
;
367 BFormattingConventions::GetNativeName(BString
& name
) const
369 UnicodeString string
;
370 fICULocale
->getDisplayName(*fICULocale
, string
);
371 string
.toTitle(NULL
, *fICULocale
);
374 BStringByteSink
converter(&name
);
375 string
.toUTF8(converter
);
382 BFormattingConventions::GetName(BString
& name
,
383 const BLanguage
* displayLanguage
) const
385 BString displayLanguageID
;
386 if (displayLanguage
== NULL
) {
387 BLanguage defaultLanguage
;
388 BLocale::Default()->GetLanguage(&defaultLanguage
);
389 displayLanguageID
= defaultLanguage
.Code();
391 displayLanguageID
= displayLanguage
->Code();
394 UnicodeString uString
;
395 fICULocale
->getDisplayName(Locale(displayLanguageID
.String()), uString
);
397 BStringByteSink
stringConverter(&name
);
398 uString
.toUTF8(stringConverter
);
405 BFormattingConventions::MeasurementKind() const
407 UErrorCode error
= U_ZERO_ERROR
;
408 switch (ulocdata_getMeasurementSystem(ID(), &error
)) {
419 BFormattingConventions::GetDateFormat(BDateFormatStyle style
,
420 BString
& outFormat
) const
422 if (style
< 0 || style
>= B_DATE_FORMAT_STYLE_COUNT
)
425 outFormat
= fExplicitDateFormats
[style
].Length()
426 ? fExplicitDateFormats
[style
]
427 : fCachedDateFormats
[style
];
429 if (outFormat
.Length() > 0)
432 ObjectDeleter
<DateFormat
> dateFormatter(
433 DateFormat::createDateInstance((DateFormat::EStyle
)style
, *fICULocale
));
434 if (dateFormatter
.Get() == NULL
)
437 SimpleDateFormat
* dateFormatterImpl
438 = static_cast<SimpleDateFormat
*>(dateFormatter
.Get());
440 UnicodeString icuString
;
441 dateFormatterImpl
->toPattern(icuString
);
442 BStringByteSink
stringConverter(&outFormat
);
443 icuString
.toUTF8(stringConverter
);
445 fCachedDateFormats
[style
] = outFormat
;
452 BFormattingConventions::GetTimeFormat(BTimeFormatStyle style
,
453 BString
& outFormat
) const
455 if (style
< 0 || style
>= B_TIME_FORMAT_STYLE_COUNT
)
458 outFormat
= fExplicitTimeFormats
[style
].Length()
459 ? fExplicitTimeFormats
[style
]
460 : fCachedTimeFormats
[style
];
462 if (outFormat
.Length() > 0)
465 ObjectDeleter
<DateFormat
> timeFormatter(
466 DateFormat::createTimeInstance((DateFormat::EStyle
)style
, *fICULocale
));
467 if (timeFormatter
.Get() == NULL
)
470 SimpleDateFormat
* timeFormatterImpl
471 = static_cast<SimpleDateFormat
*>(timeFormatter
.Get());
473 UnicodeString icuString
;
474 timeFormatterImpl
->toPattern(icuString
);
475 BStringByteSink
stringConverter(&outFormat
);
476 icuString
.toUTF8(stringConverter
);
478 int8 use24HourClock
= fExplicitUse24HourClock
!= CLOCK_HOURS_UNSET
479 ? fExplicitUse24HourClock
: fCachedUse24HourClock
;
480 if (use24HourClock
!= CLOCK_HOURS_UNSET
) {
481 // adjust to 12/24-hour clock as requested
482 bool localeUses24HourClock
= !FormatUsesAmPm(outFormat
);
483 if (localeUses24HourClock
) {
484 if (use24HourClock
== CLOCK_HOURS_12
)
485 CoerceFormatTo12HourClock(outFormat
);
487 if (use24HourClock
== CLOCK_HOURS_24
)
488 CoerceFormatTo24HourClock(outFormat
);
492 if (style
!= B_FULL_TIME_FORMAT
) {
493 // use abbreviated timezone in short timezone format
494 CoerceFormatToAbbreviatedTimezone(outFormat
);
497 fCachedTimeFormats
[style
] = outFormat
;
504 BFormattingConventions::GetDateTimeFormat(BDateFormatStyle dateStyle
,
505 BTimeFormatStyle timeStyle
, BString
& outFormat
) const
507 if (dateStyle
< 0 || dateStyle
>= B_DATE_FORMAT_STYLE_COUNT
)
510 if (timeStyle
< 0 || timeStyle
>= B_TIME_FORMAT_STYLE_COUNT
)
513 outFormat
= fExplicitDateTimeFormats
[dateStyle
][timeStyle
].Length()
514 ? fExplicitDateTimeFormats
[dateStyle
][timeStyle
]
515 : fCachedDateTimeFormats
[dateStyle
][timeStyle
];
517 if (outFormat
.Length() > 0)
520 ObjectDeleter
<DateFormat
> dateFormatter(
521 DateFormat::createDateTimeInstance((DateFormat::EStyle
)dateStyle
,
522 (DateFormat::EStyle
)timeStyle
, *fICULocale
));
523 if (dateFormatter
.Get() == NULL
)
526 SimpleDateFormat
* dateFormatterImpl
527 = static_cast<SimpleDateFormat
*>(dateFormatter
.Get());
529 UnicodeString icuString
;
530 dateFormatterImpl
->toPattern(icuString
);
531 BStringByteSink
stringConverter(&outFormat
);
532 icuString
.toUTF8(stringConverter
);
534 fCachedDateTimeFormats
[dateStyle
][timeStyle
] = outFormat
;
541 BFormattingConventions::GetNumericFormat(BString
& outFormat
) const
544 return B_UNSUPPORTED
;
549 BFormattingConventions::GetMonetaryFormat(BString
& outFormat
) const
552 return B_UNSUPPORTED
;
557 BFormattingConventions::SetExplicitDateFormat(BDateFormatStyle style
,
558 const BString
& format
)
560 fExplicitDateFormats
[style
] = format
;
565 BFormattingConventions::SetExplicitTimeFormat(BTimeFormatStyle style
,
566 const BString
& format
)
568 fExplicitTimeFormats
[style
] = format
;
573 BFormattingConventions::SetExplicitDateTimeFormat(BDateFormatStyle dateStyle
,
574 BTimeFormatStyle timeStyle
, const BString
& format
)
576 fExplicitDateTimeFormats
[dateStyle
][timeStyle
] = format
;
581 BFormattingConventions::SetExplicitNumericFormat(const BString
& format
)
583 fExplicitNumericFormat
= format
;
588 BFormattingConventions::SetExplicitMonetaryFormat(const BString
& format
)
590 fExplicitMonetaryFormat
= format
;
595 BFormattingConventions::UseStringsFromPreferredLanguage() const
597 return fUseStringsFromPreferredLanguage
;
602 BFormattingConventions::SetUseStringsFromPreferredLanguage(bool value
)
604 fUseStringsFromPreferredLanguage
= value
;
609 BFormattingConventions::Use24HourClock() const
611 int8 use24HourClock
= fExplicitUse24HourClock
!= CLOCK_HOURS_UNSET
612 ? fExplicitUse24HourClock
: fCachedUse24HourClock
;
614 if (use24HourClock
== CLOCK_HOURS_UNSET
) {
616 GetTimeFormat(B_MEDIUM_TIME_FORMAT
, format
);
617 fCachedUse24HourClock
618 = FormatUsesAmPm(format
) ? CLOCK_HOURS_12
: CLOCK_HOURS_24
;
619 return fCachedUse24HourClock
== CLOCK_HOURS_24
;
622 return fExplicitUse24HourClock
== CLOCK_HOURS_24
;
627 BFormattingConventions::SetExplicitUse24HourClock(bool value
)
629 int8 newUse24HourClock
= value
? CLOCK_HOURS_24
: CLOCK_HOURS_12
;
630 if (fExplicitUse24HourClock
== newUse24HourClock
)
633 fExplicitUse24HourClock
= newUse24HourClock
;
635 for (int s
= 0; s
< B_TIME_FORMAT_STYLE_COUNT
; ++s
)
636 fCachedTimeFormats
[s
].Truncate(0);
641 BFormattingConventions::UnsetExplicitUse24HourClock()
643 fExplicitUse24HourClock
= CLOCK_HOURS_UNSET
;
645 for (int s
= 0; s
< B_TIME_FORMAT_STYLE_COUNT
; ++s
)
646 fCachedTimeFormats
[s
].Truncate(0);
651 BFormattingConventions::Archive(BMessage
* archive
, bool deep
) const
653 status_t status
= archive
->AddString("conventions", fICULocale
->getName());
654 for (int s
= 0; s
< B_DATE_FORMAT_STYLE_COUNT
&& status
== B_OK
; ++s
) {
655 status
= archive
->AddString("dateFormat", fExplicitDateFormats
[s
]);
657 status
= archive
->AddString("timeFormat", fExplicitTimeFormats
[s
]);
660 status
= archive
->AddInt8("use24HourClock", fExplicitUse24HourClock
);
661 if (status
== B_OK
) {
662 status
= archive
->AddBool("useStringsFromPreferredLanguage",
663 fUseStringsFromPreferredLanguage
);