* Copyright 2010-2014, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Oliver Tappe <zooey@hirschkaefer.de>
* Adrien Desutugues <pulkomandy@pulkomandy.tk>
*/
#include <unicode/uversion.h>
#include <DateFormat.h>
#include <AutoDeleter.h>
#include <Autolock.h>
#include <FormattingConventionsPrivate.h>
#include <LanguagePrivate.h>
#include <Locale.h>
#include <LocaleRoster.h>
#include <TimeZone.h>
#include <ICUWrapper.h>
#include <unicode/datefmt.h>
#include <unicode/dtfmtsym.h>
#include <unicode/smpdtfmt.h>
#include <vector>
U_NAMESPACE_USE
static const DateFormatSymbols::DtWidthType kDateFormatStyleToWidth[] = {
DateFormatSymbols::WIDE,
DateFormatSymbols::ABBREVIATED,
DateFormatSymbols::SHORT,
DateFormatSymbols::NARROW,
};
BDateFormat::BDateFormat(const BLocale* locale)
: BFormat(locale)
{
}
BDateFormat::BDateFormat(const BLanguage& language,
const BFormattingConventions& conventions)
: BFormat(language, conventions)
{
}
BDateFormat::BDateFormat(const BDateFormat &other)
: BFormat(other)
{
}
BDateFormat::~BDateFormat()
{
}
status_t
BDateFormat::GetDateFormat(BDateFormatStyle style,
BString& outFormat) const
{
return fConventions.GetDateFormat(style, outFormat);
}
void
BDateFormat::SetDateFormat(BDateFormatStyle style,
const BString& format)
{
fConventions.SetExplicitDateFormat(style, format);
}
ssize_t
BDateFormat::Format(char* string, const size_t maxSize, const time_t time,
const BDateFormatStyle style) const
{
ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
if (!dateFormatter.IsSet())
return B_NO_MEMORY;
UnicodeString icuString;
dateFormatter->format((UDate)time * 1000, icuString);
CheckedArrayByteSink stringConverter(string, maxSize);
icuString.toUTF8(stringConverter);
if (stringConverter.Overflowed())
return B_BAD_VALUE;
return stringConverter.NumberOfBytesWritten();
}
status_t
BDateFormat::Format(BString& string, const time_t time,
const BDateFormatStyle style, const BTimeZone* timeZone) const
{
ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
if (!dateFormatter.IsSet())
return B_NO_MEMORY;
if (timeZone != NULL) {
ObjectDeleter<TimeZone> icuTimeZone(
TimeZone::createTimeZone(timeZone->ID().String()));
if (!icuTimeZone.IsSet())
return B_NO_MEMORY;
dateFormatter->setTimeZone(*icuTimeZone.Get());
}
UnicodeString icuString;
dateFormatter->format((UDate)time * 1000, icuString);
string.Truncate(0);
BStringByteSink stringConverter(&string);
icuString.toUTF8(stringConverter);
return B_OK;
}
status_t
BDateFormat::Format(BString& string, const BDate& time,
const BDateFormatStyle style, const BTimeZone* timeZone) const
{
if (!time.IsValid())
return B_BAD_DATA;
ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
if (!dateFormatter.IsSet())
return B_NO_MEMORY;
UErrorCode err = U_ZERO_ERROR;
ObjectDeleter<Calendar> calendar(Calendar::createInstance(err));
if (!U_SUCCESS(err))
return B_NO_MEMORY;
if (timeZone != NULL) {
ObjectDeleter<TimeZone> icuTimeZone(
TimeZone::createTimeZone(timeZone->ID().String()));
if (!icuTimeZone.IsSet())
return B_NO_MEMORY;
dateFormatter->setTimeZone(*icuTimeZone.Get());
calendar->setTimeZone(*icuTimeZone.Get());
}
calendar->set(time.Year(), time.Month() - 1, time.Day());
UnicodeString icuString;
FieldPosition p;
dateFormatter->format(*calendar.Get(), icuString, p);
string.Truncate(0);
BStringByteSink stringConverter(&string);
icuString.toUTF8(stringConverter);
return B_OK;
}
status_t
BDateFormat::Format(BString& string, int*& fieldPositions, int& fieldCount,
const time_t time, const BDateFormatStyle style) const
{
ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
if (!dateFormatter.IsSet())
return B_NO_MEMORY;
fieldPositions = NULL;
UErrorCode error = U_ZERO_ERROR;
icu::FieldPositionIterator positionIterator;
UnicodeString icuString;
dateFormatter->format((UDate)time * 1000, icuString, &positionIterator,
error);
if (error != U_ZERO_ERROR)
return B_BAD_VALUE;
icu::FieldPosition field;
std::vector<int> fieldPosStorage;
fieldCount = 0;
while (positionIterator.next(field)) {
fieldPosStorage.push_back(field.getBeginIndex());
fieldPosStorage.push_back(field.getEndIndex());
fieldCount += 2;
}
fieldPositions = (int*) malloc(fieldCount * sizeof(int));
for (int i = 0 ; i < fieldCount ; i++ )
fieldPositions[i] = fieldPosStorage[i];
string.Truncate(0);
BStringByteSink stringConverter(&string);
icuString.toUTF8(stringConverter);
return B_OK;
}
status_t
BDateFormat::GetFields(BDateElement*& fields, int& fieldCount,
BDateFormatStyle style) const
{
ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
if (!dateFormatter.IsSet())
return B_NO_MEMORY;
fields = NULL;
UErrorCode error = U_ZERO_ERROR;
icu::FieldPositionIterator positionIterator;
UnicodeString icuString;
time_t now;
dateFormatter->format((UDate)time(&now) * 1000, icuString,
&positionIterator, error);
if (U_FAILURE(error))
return B_BAD_VALUE;
icu::FieldPosition field;
std::vector<int> fieldPosStorage;
fieldCount = 0;
while (positionIterator.next(field)) {
fieldPosStorage.push_back(field.getField());
fieldCount ++;
}
fields = (BDateElement*) malloc(fieldCount * sizeof(BDateElement));
for (int i = 0 ; i < fieldCount ; i++ ) {
switch (fieldPosStorage[i]) {
case UDAT_YEAR_FIELD:
fields[i] = B_DATE_ELEMENT_YEAR;
break;
case UDAT_MONTH_FIELD:
fields[i] = B_DATE_ELEMENT_MONTH;
break;
case UDAT_DATE_FIELD:
fields[i] = B_DATE_ELEMENT_DAY;
break;
case UDAT_DAY_OF_WEEK_FIELD:
fields[i] = B_DATE_ELEMENT_WEEKDAY;
break;
default:
fields[i] = B_DATE_ELEMENT_INVALID;
break;
}
}
return B_OK;
}
status_t
BDateFormat::GetStartOfWeek(BWeekday* startOfWeek) const
{
if (startOfWeek == NULL)
return B_BAD_VALUE;
UErrorCode err = U_ZERO_ERROR;
ObjectDeleter<Calendar> calendar(Calendar::createInstance(
*BFormattingConventions::Private(&fConventions).ICULocale(), err));
if (U_FAILURE(err))
return B_ERROR;
UCalendarDaysOfWeek icuWeekStart = calendar->getFirstDayOfWeek(err);
if (U_FAILURE(err))
return B_ERROR;
switch (icuWeekStart) {
case UCAL_SUNDAY:
*startOfWeek = B_WEEKDAY_SUNDAY;
break;
case UCAL_MONDAY:
*startOfWeek = B_WEEKDAY_MONDAY;
break;
case UCAL_TUESDAY:
*startOfWeek = B_WEEKDAY_TUESDAY;
break;
case UCAL_WEDNESDAY:
*startOfWeek = B_WEEKDAY_WEDNESDAY;
break;
case UCAL_THURSDAY:
*startOfWeek = B_WEEKDAY_THURSDAY;
break;
case UCAL_FRIDAY:
*startOfWeek = B_WEEKDAY_FRIDAY;
break;
case UCAL_SATURDAY:
*startOfWeek = B_WEEKDAY_SATURDAY;
break;
default:
return B_ERROR;
}
return B_OK;
}
status_t
BDateFormat::GetMonthName(int month, BString& outName,
const BDateFormatStyle style) const
{
if (style < 0 || style >= B_DATE_FORMAT_STYLE_COUNT)
return B_BAD_VALUE;
DateFormat* format = _CreateDateFormatter(B_LONG_DATE_FORMAT);
SimpleDateFormat* simpleFormat = dynamic_cast<SimpleDateFormat*>(format);
if (simpleFormat == NULL) {
delete format;
return B_ERROR;
}
const DateFormatSymbols* symbols = simpleFormat->getDateFormatSymbols();
int32_t count;
const UnicodeString* names = symbols->getMonths(count,
DateFormatSymbols::STANDALONE, kDateFormatStyleToWidth[style]);
if (month > count || month <= 0) {
delete simpleFormat;
return B_BAD_DATA;
}
BStringByteSink stringConverter(&outName);
names[month - 1].toUTF8(stringConverter);
delete simpleFormat;
return B_OK;
}
status_t
BDateFormat::GetDayName(int day, BString& outName,
const BDateFormatStyle style) const
{
if (style < 0 || style >= B_DATE_FORMAT_STYLE_COUNT)
return B_BAD_VALUE;
DateFormat* format = _CreateDateFormatter(B_LONG_DATE_FORMAT);
SimpleDateFormat* simpleFormat = dynamic_cast<SimpleDateFormat*>(format);
if (simpleFormat == NULL) {
delete format;
return B_ERROR;
}
const DateFormatSymbols* symbols = simpleFormat->getDateFormatSymbols();
int32_t count;
const UnicodeString* names = symbols->getWeekdays(count,
DateFormatSymbols::STANDALONE, kDateFormatStyleToWidth[style]);
if (day >= count || day <= 0) {
delete simpleFormat;
return B_BAD_DATA;
}
BStringByteSink stringConverter(&outName);
names[_ConvertDayNumberToICU(day)].toUTF8(stringConverter);
delete simpleFormat;
return B_OK;
}
status_t
BDateFormat::Parse(BString source, BDateFormatStyle style, BDate& output)
{
ObjectDeleter<DateFormat> dateFormatter(_CreateDateFormatter(style));
if (!dateFormatter.IsSet())
return B_NO_MEMORY;
ParsePosition p(0);
UDate date = dateFormatter->parse(UnicodeString::fromUTF8(source.String()),
p);
output.SetDate(1970, 1, 1);
output.AddDays(date / U_MILLIS_PER_DAY + 0.5);
return B_OK;
}
DateFormat*
BDateFormat::_CreateDateFormatter(const BDateFormatStyle style) const
{
Locale* icuLocale
= fConventions.UseStringsFromPreferredLanguage()
? BLanguage::Private(&fLanguage).ICULocale()
: BFormattingConventions::Private(&fConventions).ICULocale();
icu::DateFormat* dateFormatter
= icu::DateFormat::createDateInstance(DateFormat::kShort, *icuLocale);
if (dateFormatter == NULL)
return NULL;
SimpleDateFormat* dateFormatterImpl
= static_cast<SimpleDateFormat*>(dateFormatter);
BString format;
fConventions.GetDateFormat(style, format);
UnicodeString pattern(format.String());
dateFormatterImpl->applyPattern(pattern);
return dateFormatter;
}