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 fExplicitDateTimeFormats
[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 CoerceFormatForClock(outFormat
);
480 if (style
!= B_FULL_TIME_FORMAT
) {
481 // use abbreviated timezone in short timezone format
482 CoerceFormatToAbbreviatedTimezone(outFormat
);
485 fCachedTimeFormats
[style
] = outFormat
;
492 BFormattingConventions::GetDateTimeFormat(BDateFormatStyle dateStyle
,
493 BTimeFormatStyle timeStyle
, BString
& outFormat
) const
495 if (dateStyle
< 0 || dateStyle
>= B_DATE_FORMAT_STYLE_COUNT
)
498 if (timeStyle
< 0 || timeStyle
>= B_TIME_FORMAT_STYLE_COUNT
)
501 outFormat
= fExplicitDateTimeFormats
[dateStyle
][timeStyle
].Length()
502 ? fExplicitDateTimeFormats
[dateStyle
][timeStyle
]
503 : fCachedDateTimeFormats
[dateStyle
][timeStyle
];
505 if (outFormat
.Length() > 0)
508 ObjectDeleter
<DateFormat
> dateFormatter(
509 DateFormat::createDateTimeInstance((DateFormat::EStyle
)dateStyle
,
510 (DateFormat::EStyle
)timeStyle
, *fICULocale
));
511 if (dateFormatter
.Get() == NULL
)
514 SimpleDateFormat
* dateFormatterImpl
515 = static_cast<SimpleDateFormat
*>(dateFormatter
.Get());
517 UnicodeString icuString
;
518 dateFormatterImpl
->toPattern(icuString
);
519 BStringByteSink
stringConverter(&outFormat
);
520 icuString
.toUTF8(stringConverter
);
522 CoerceFormatForClock(outFormat
);
524 if (dateStyle
!= B_FULL_DATE_FORMAT
) {
525 // use abbreviated timezone in short timezone format
526 CoerceFormatToAbbreviatedTimezone(outFormat
);
529 fCachedDateTimeFormats
[dateStyle
][timeStyle
] = outFormat
;
536 BFormattingConventions::GetNumericFormat(BString
& outFormat
) const
539 return B_UNSUPPORTED
;
544 BFormattingConventions::GetMonetaryFormat(BString
& outFormat
) const
547 return B_UNSUPPORTED
;
552 BFormattingConventions::SetExplicitDateFormat(BDateFormatStyle style
,
553 const BString
& format
)
555 fExplicitDateFormats
[style
] = format
;
560 BFormattingConventions::SetExplicitTimeFormat(BTimeFormatStyle style
,
561 const BString
& format
)
563 fExplicitTimeFormats
[style
] = format
;
568 BFormattingConventions::SetExplicitDateTimeFormat(BDateFormatStyle dateStyle
,
569 BTimeFormatStyle timeStyle
, const BString
& format
)
571 fExplicitDateTimeFormats
[dateStyle
][timeStyle
] = format
;
576 BFormattingConventions::SetExplicitNumericFormat(const BString
& format
)
578 fExplicitNumericFormat
= format
;
583 BFormattingConventions::SetExplicitMonetaryFormat(const BString
& format
)
585 fExplicitMonetaryFormat
= format
;
590 BFormattingConventions::UseStringsFromPreferredLanguage() const
592 return fUseStringsFromPreferredLanguage
;
597 BFormattingConventions::SetUseStringsFromPreferredLanguage(bool value
)
599 fUseStringsFromPreferredLanguage
= value
;
604 BFormattingConventions::Use24HourClock() const
606 int8 use24HourClock
= fExplicitUse24HourClock
!= CLOCK_HOURS_UNSET
607 ? fExplicitUse24HourClock
: fCachedUse24HourClock
;
609 if (use24HourClock
== CLOCK_HOURS_UNSET
) {
611 GetTimeFormat(B_MEDIUM_TIME_FORMAT
, format
);
612 fCachedUse24HourClock
613 = FormatUsesAmPm(format
) ? CLOCK_HOURS_12
: CLOCK_HOURS_24
;
614 return fCachedUse24HourClock
== CLOCK_HOURS_24
;
617 return fExplicitUse24HourClock
== CLOCK_HOURS_24
;
622 BFormattingConventions::SetExplicitUse24HourClock(bool value
)
624 int8 newUse24HourClock
= value
? CLOCK_HOURS_24
: CLOCK_HOURS_12
;
625 if (fExplicitUse24HourClock
== newUse24HourClock
)
628 fExplicitUse24HourClock
= newUse24HourClock
;
630 for (int s
= 0; s
< B_TIME_FORMAT_STYLE_COUNT
; ++s
)
631 fCachedTimeFormats
[s
].Truncate(0);
636 BFormattingConventions::UnsetExplicitUse24HourClock()
638 fExplicitUse24HourClock
= CLOCK_HOURS_UNSET
;
640 for (int s
= 0; s
< B_TIME_FORMAT_STYLE_COUNT
; ++s
)
641 fCachedTimeFormats
[s
].Truncate(0);
646 BFormattingConventions::Archive(BMessage
* archive
, bool deep
) const
648 status_t status
= archive
->AddString("conventions", fICULocale
->getName());
649 for (int s
= 0; s
< B_DATE_FORMAT_STYLE_COUNT
&& status
== B_OK
; ++s
) {
650 status
= archive
->AddString("dateFormat", fExplicitDateFormats
[s
]);
652 status
= archive
->AddString("timeFormat", fExplicitTimeFormats
[s
]);
655 status
= archive
->AddInt8("use24HourClock", fExplicitUse24HourClock
);
656 if (status
== B_OK
) {
657 status
= archive
->AddBool("useStringsFromPreferredLanguage",
658 fUseStringsFromPreferredLanguage
);
666 BFormattingConventions::CoerceFormatForClock(BString
& outFormat
) const
668 int8 use24HourClock
= fExplicitUse24HourClock
!= CLOCK_HOURS_UNSET
669 ? fExplicitUse24HourClock
: fCachedUse24HourClock
;
670 if (use24HourClock
!= CLOCK_HOURS_UNSET
) {
671 // adjust to 12/24-hour clock as requested
672 bool localeUses24HourClock
= !FormatUsesAmPm(outFormat
);
673 if (localeUses24HourClock
) {
674 if (use24HourClock
== CLOCK_HOURS_12
)
675 CoerceFormatTo12HourClock(outFormat
);
677 if (use24HourClock
== CLOCK_HOURS_24
)
678 CoerceFormatTo24HourClock(outFormat
);