/* * Copyright 2010-2014, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Oliver Tappe * Adrien Desutugues */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 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 dateFormatter(_CreateDateFormatter(style)); if (!dateFormatter.IsSet()) return B_NO_MEMORY; if (timeZone != NULL) { ObjectDeleter 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 dateFormatter(_CreateDateFormatter(style)); if (!dateFormatter.IsSet()) return B_NO_MEMORY; UErrorCode err = U_ZERO_ERROR; ObjectDeleter calendar(Calendar::createInstance(err)); if (!U_SUCCESS(err)) return B_NO_MEMORY; if (timeZone != NULL) { ObjectDeleter icuTimeZone( TimeZone::createTimeZone(timeZone->ID().String())); if (!icuTimeZone.IsSet()) return B_NO_MEMORY; dateFormatter->setTimeZone(*icuTimeZone.Get()); calendar->setTimeZone(*icuTimeZone.Get()); } // Note ICU calendar uses months in range 0..11, while we use the more // natural 1..12 in BDate. 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 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 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 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 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::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(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(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) { // FIXME currently this parses a date in any timezone (or the local one if // none is specified) to a BDate in UTC. This may not be a good idea, we // may want to parse to a "local" date instead. But BDate should be made // timezone aware so things like BDate::Difference can work for dates in // different timezones. ObjectDeleter 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(dateFormatter); BString format; fConventions.GetDateFormat(style, format); UnicodeString pattern(format.String()); dateFormatterImpl->applyPattern(pattern); return dateFormatter; }