xref: /haiku/src/kits/locale/TimeFormat.cpp (revision 26b0a53d1249e607a1b0be57c7f3ad33a9d3f36f)
1b2c385c0SAdrien Destugues /*
203b2550eSAdrien Destugues  * Copyright 2010-2014, Haiku, Inc. All Rights Reserved.
3b2c385c0SAdrien Destugues  * Distributed under the terms of the MIT License.
438ac8defSOliver Tappe  *
538ac8defSOliver Tappe  * Authors:
638ac8defSOliver Tappe  *		Oliver Tappe <zooey@hirschkaefer.de>
7b2c385c0SAdrien Destugues  */
8b2c385c0SAdrien Destugues 
9b2c385c0SAdrien Destugues #include <TimeFormat.h>
10b2c385c0SAdrien Destugues 
1103b2550eSAdrien Destugues #include <AutoDeleter.h>
1203b2550eSAdrien Destugues #include <Autolock.h>
13*26b0a53dSAdrien Destugues #include <DateTime.h>
1403b2550eSAdrien Destugues #include <FormattingConventionsPrivate.h>
1503b2550eSAdrien Destugues #include <LanguagePrivate.h>
1603b2550eSAdrien Destugues #include <TimeZone.h>
17b2c385c0SAdrien Destugues 
1803b2550eSAdrien Destugues #include <ICUWrapper.h>
1903b2550eSAdrien Destugues 
2003b2550eSAdrien Destugues #include <unicode/datefmt.h>
2103b2550eSAdrien Destugues #include <unicode/smpdtfmt.h>
2203b2550eSAdrien Destugues 
2303b2550eSAdrien Destugues #include <vector>
2403b2550eSAdrien Destugues 
2503b2550eSAdrien Destugues 
2603b2550eSAdrien Destugues BTimeFormat::BTimeFormat(const BLanguage* const language,
2703b2550eSAdrien Destugues 	const BFormattingConventions* const conventions)
2803b2550eSAdrien Destugues {
2903b2550eSAdrien Destugues 	if (conventions != NULL)
3003b2550eSAdrien Destugues 		fConventions = *conventions;
3103b2550eSAdrien Destugues 
3203b2550eSAdrien Destugues 	if (language != NULL)
3303b2550eSAdrien Destugues 		fLanguage = *language;
3403b2550eSAdrien Destugues }
3503b2550eSAdrien Destugues 
3603b2550eSAdrien Destugues 
3738ac8defSOliver Tappe BTimeFormat::BTimeFormat(const BTimeFormat &other)
3803b2550eSAdrien Destugues 	: BFormat(other)
39b2c385c0SAdrien Destugues {
4038ac8defSOliver Tappe }
4133eead33SAdrien Destugues 
4203b2550eSAdrien Destugues 
4338ac8defSOliver Tappe BTimeFormat::~BTimeFormat()
4438ac8defSOliver Tappe {
4538ac8defSOliver Tappe }
4633eead33SAdrien Destugues 
4703b2550eSAdrien Destugues 
48*26b0a53dSAdrien Destugues void
49*26b0a53dSAdrien Destugues BTimeFormat::SetTimeFormat(BTimeFormatStyle style,
50*26b0a53dSAdrien Destugues 	const BString& format)
51*26b0a53dSAdrien Destugues {
52*26b0a53dSAdrien Destugues 	fConventions.SetExplicitTimeFormat(style, format);
53*26b0a53dSAdrien Destugues }
54*26b0a53dSAdrien Destugues 
55*26b0a53dSAdrien Destugues 
5603b2550eSAdrien Destugues // #pragma mark - Formatting
5703b2550eSAdrien Destugues 
5803b2550eSAdrien Destugues 
5903b2550eSAdrien Destugues ssize_t
6003b2550eSAdrien Destugues BTimeFormat::Format(char* string, size_t maxSize, time_t time,
6103b2550eSAdrien Destugues 	BTimeFormatStyle style) const
6238ac8defSOliver Tappe {
6303b2550eSAdrien Destugues 	BAutolock lock(fLock);
6403b2550eSAdrien Destugues 	if (!lock.IsLocked())
65b2c385c0SAdrien Destugues 		return B_ERROR;
6603b2550eSAdrien Destugues 
67*26b0a53dSAdrien Destugues 	ObjectDeleter<DateFormat> timeFormatter(_CreateTimeFormatter(style));
6803b2550eSAdrien Destugues 	if (timeFormatter.Get() == NULL)
6903b2550eSAdrien Destugues 		return B_NO_MEMORY;
7003b2550eSAdrien Destugues 
7103b2550eSAdrien Destugues 	UnicodeString icuString;
7203b2550eSAdrien Destugues 	timeFormatter->format((UDate)time * 1000, icuString);
7303b2550eSAdrien Destugues 
7403b2550eSAdrien Destugues 	CheckedArrayByteSink stringConverter(string, maxSize);
7503b2550eSAdrien Destugues 	icuString.toUTF8(stringConverter);
7603b2550eSAdrien Destugues 
7703b2550eSAdrien Destugues 	if (stringConverter.Overflowed())
7803b2550eSAdrien Destugues 		return B_BAD_VALUE;
7903b2550eSAdrien Destugues 
8003b2550eSAdrien Destugues 	return stringConverter.NumberOfBytesWritten();
8103b2550eSAdrien Destugues }
8203b2550eSAdrien Destugues 
8303b2550eSAdrien Destugues 
8403b2550eSAdrien Destugues status_t
8503b2550eSAdrien Destugues BTimeFormat::Format(BString& string, const time_t time,
8603b2550eSAdrien Destugues 	const BTimeFormatStyle style, const BTimeZone* timeZone) const
8703b2550eSAdrien Destugues {
8803b2550eSAdrien Destugues 	BAutolock lock(fLock);
8903b2550eSAdrien Destugues 	if (!lock.IsLocked())
9003b2550eSAdrien Destugues 		return B_ERROR;
9103b2550eSAdrien Destugues 
92*26b0a53dSAdrien Destugues 	ObjectDeleter<DateFormat> timeFormatter(_CreateTimeFormatter(style));
9303b2550eSAdrien Destugues 	if (timeFormatter.Get() == NULL)
9403b2550eSAdrien Destugues 		return B_NO_MEMORY;
9503b2550eSAdrien Destugues 
9603b2550eSAdrien Destugues 	if (timeZone != NULL) {
9703b2550eSAdrien Destugues 		ObjectDeleter<TimeZone> icuTimeZone(
9803b2550eSAdrien Destugues 			TimeZone::createTimeZone(timeZone->ID().String()));
9903b2550eSAdrien Destugues 		if (icuTimeZone.Get() == NULL)
10003b2550eSAdrien Destugues 			return B_NO_MEMORY;
10103b2550eSAdrien Destugues 		timeFormatter->setTimeZone(*icuTimeZone.Get());
10203b2550eSAdrien Destugues 	}
10303b2550eSAdrien Destugues 
10403b2550eSAdrien Destugues 	UnicodeString icuString;
10503b2550eSAdrien Destugues 	timeFormatter->format((UDate)time * 1000, icuString);
10603b2550eSAdrien Destugues 
10703b2550eSAdrien Destugues 	string.Truncate(0);
10803b2550eSAdrien Destugues 	BStringByteSink stringConverter(&string);
10903b2550eSAdrien Destugues 	icuString.toUTF8(stringConverter);
11003b2550eSAdrien Destugues 
11103b2550eSAdrien Destugues 	return B_OK;
11203b2550eSAdrien Destugues }
11303b2550eSAdrien Destugues 
11403b2550eSAdrien Destugues 
11503b2550eSAdrien Destugues status_t
11603b2550eSAdrien Destugues BTimeFormat::Format(BString& string, int*& fieldPositions, int& fieldCount,
11703b2550eSAdrien Destugues 	time_t time, BTimeFormatStyle style) const
11803b2550eSAdrien Destugues {
11903b2550eSAdrien Destugues 	BAutolock lock(fLock);
12003b2550eSAdrien Destugues 	if (!lock.IsLocked())
12103b2550eSAdrien Destugues 		return B_ERROR;
12203b2550eSAdrien Destugues 
123*26b0a53dSAdrien Destugues 	ObjectDeleter<DateFormat> timeFormatter(_CreateTimeFormatter(style));
12403b2550eSAdrien Destugues 	if (timeFormatter.Get() == NULL)
12503b2550eSAdrien Destugues 		return B_NO_MEMORY;
12603b2550eSAdrien Destugues 
12703b2550eSAdrien Destugues 	fieldPositions = NULL;
12803b2550eSAdrien Destugues 	UErrorCode error = U_ZERO_ERROR;
12903b2550eSAdrien Destugues 	icu::FieldPositionIterator positionIterator;
13003b2550eSAdrien Destugues 	UnicodeString icuString;
13103b2550eSAdrien Destugues 	timeFormatter->format((UDate)time * 1000, icuString, &positionIterator,
13203b2550eSAdrien Destugues 		error);
13303b2550eSAdrien Destugues 
13403b2550eSAdrien Destugues 	if (error != U_ZERO_ERROR)
13503b2550eSAdrien Destugues 		return B_BAD_VALUE;
13603b2550eSAdrien Destugues 
13703b2550eSAdrien Destugues 	icu::FieldPosition field;
13803b2550eSAdrien Destugues 	std::vector<int> fieldPosStorage;
13903b2550eSAdrien Destugues 	fieldCount  = 0;
14003b2550eSAdrien Destugues 	while (positionIterator.next(field)) {
14103b2550eSAdrien Destugues 		fieldPosStorage.push_back(field.getBeginIndex());
14203b2550eSAdrien Destugues 		fieldPosStorage.push_back(field.getEndIndex());
14303b2550eSAdrien Destugues 		fieldCount += 2;
14403b2550eSAdrien Destugues 	}
14503b2550eSAdrien Destugues 
14603b2550eSAdrien Destugues 	fieldPositions = (int*) malloc(fieldCount * sizeof(int));
14703b2550eSAdrien Destugues 
14803b2550eSAdrien Destugues 	for (int i = 0 ; i < fieldCount ; i++ )
14903b2550eSAdrien Destugues 		fieldPositions[i] = fieldPosStorage[i];
15003b2550eSAdrien Destugues 
15103b2550eSAdrien Destugues 	string.Truncate(0);
15203b2550eSAdrien Destugues 	BStringByteSink stringConverter(&string);
15303b2550eSAdrien Destugues 	icuString.toUTF8(stringConverter);
15403b2550eSAdrien Destugues 
15503b2550eSAdrien Destugues 	return B_OK;
15603b2550eSAdrien Destugues }
15703b2550eSAdrien Destugues 
15803b2550eSAdrien Destugues 
15903b2550eSAdrien Destugues status_t
16003b2550eSAdrien Destugues BTimeFormat::GetTimeFields(BDateElement*& fields, int& fieldCount,
16103b2550eSAdrien Destugues 	BTimeFormatStyle style) const
16203b2550eSAdrien Destugues {
16303b2550eSAdrien Destugues 	BAutolock lock(fLock);
16403b2550eSAdrien Destugues 	if (!lock.IsLocked())
16503b2550eSAdrien Destugues 		return B_ERROR;
16603b2550eSAdrien Destugues 
167*26b0a53dSAdrien Destugues 	ObjectDeleter<DateFormat> timeFormatter(_CreateTimeFormatter(style));
16803b2550eSAdrien Destugues 	if (timeFormatter.Get() == NULL)
16903b2550eSAdrien Destugues 		return B_NO_MEMORY;
17003b2550eSAdrien Destugues 
17103b2550eSAdrien Destugues 	fields = NULL;
17203b2550eSAdrien Destugues 	UErrorCode error = U_ZERO_ERROR;
17303b2550eSAdrien Destugues 	icu::FieldPositionIterator positionIterator;
17403b2550eSAdrien Destugues 	UnicodeString icuString;
17503b2550eSAdrien Destugues 	time_t now;
17603b2550eSAdrien Destugues 	timeFormatter->format((UDate)time(&now) * 1000, icuString,
17703b2550eSAdrien Destugues 		&positionIterator, error);
17803b2550eSAdrien Destugues 
17903b2550eSAdrien Destugues 	if (error != U_ZERO_ERROR)
18003b2550eSAdrien Destugues 		return B_BAD_VALUE;
18103b2550eSAdrien Destugues 
18203b2550eSAdrien Destugues 	icu::FieldPosition field;
18303b2550eSAdrien Destugues 	std::vector<int> fieldPosStorage;
18403b2550eSAdrien Destugues 	fieldCount  = 0;
18503b2550eSAdrien Destugues 	while (positionIterator.next(field)) {
18603b2550eSAdrien Destugues 		fieldPosStorage.push_back(field.getField());
18703b2550eSAdrien Destugues 		fieldCount ++;
18803b2550eSAdrien Destugues 	}
18903b2550eSAdrien Destugues 
19003b2550eSAdrien Destugues 	fields = (BDateElement*) malloc(fieldCount * sizeof(BDateElement));
19103b2550eSAdrien Destugues 
19203b2550eSAdrien Destugues 	for (int i = 0 ; i < fieldCount ; i++ ) {
19303b2550eSAdrien Destugues 		switch (fieldPosStorage[i]) {
19403b2550eSAdrien Destugues 			case UDAT_HOUR_OF_DAY1_FIELD:
19503b2550eSAdrien Destugues 			case UDAT_HOUR_OF_DAY0_FIELD:
19603b2550eSAdrien Destugues 			case UDAT_HOUR1_FIELD:
19703b2550eSAdrien Destugues 			case UDAT_HOUR0_FIELD:
19803b2550eSAdrien Destugues 				fields[i] = B_DATE_ELEMENT_HOUR;
19903b2550eSAdrien Destugues 				break;
20003b2550eSAdrien Destugues 			case UDAT_MINUTE_FIELD:
20103b2550eSAdrien Destugues 				fields[i] = B_DATE_ELEMENT_MINUTE;
20203b2550eSAdrien Destugues 				break;
20303b2550eSAdrien Destugues 			case UDAT_SECOND_FIELD:
20403b2550eSAdrien Destugues 				fields[i] = B_DATE_ELEMENT_SECOND;
20503b2550eSAdrien Destugues 				break;
20603b2550eSAdrien Destugues 			case UDAT_AM_PM_FIELD:
20703b2550eSAdrien Destugues 				fields[i] = B_DATE_ELEMENT_AM_PM;
20803b2550eSAdrien Destugues 				break;
20903b2550eSAdrien Destugues 			default:
21003b2550eSAdrien Destugues 				fields[i] = B_DATE_ELEMENT_INVALID;
21103b2550eSAdrien Destugues 				break;
21203b2550eSAdrien Destugues 		}
21303b2550eSAdrien Destugues 	}
21403b2550eSAdrien Destugues 
21503b2550eSAdrien Destugues 	return B_OK;
21603b2550eSAdrien Destugues }
21703b2550eSAdrien Destugues 
21803b2550eSAdrien Destugues 
219*26b0a53dSAdrien Destugues status_t
220*26b0a53dSAdrien Destugues BTimeFormat::Parse(BString source, BTimeFormatStyle style, BTime& output)
221*26b0a53dSAdrien Destugues {
222*26b0a53dSAdrien Destugues 	BAutolock lock(fLock);
223*26b0a53dSAdrien Destugues 	if (!lock.IsLocked())
224*26b0a53dSAdrien Destugues 		return B_ERROR;
225*26b0a53dSAdrien Destugues 
226*26b0a53dSAdrien Destugues 	ObjectDeleter<DateFormat> timeFormatter(_CreateTimeFormatter(style));
227*26b0a53dSAdrien Destugues 	if (timeFormatter.Get() == NULL)
228*26b0a53dSAdrien Destugues 		return B_NO_MEMORY;
229*26b0a53dSAdrien Destugues 
230*26b0a53dSAdrien Destugues 	// If no timezone is specified in the time string, assume GMT
231*26b0a53dSAdrien Destugues 	timeFormatter->setTimeZone(*icu::TimeZone::getGMT());
232*26b0a53dSAdrien Destugues 
233*26b0a53dSAdrien Destugues 	ParsePosition p(0);
234*26b0a53dSAdrien Destugues 	UDate date = timeFormatter->parse(UnicodeString::fromUTF8(source.String()),
235*26b0a53dSAdrien Destugues 		p);
236*26b0a53dSAdrien Destugues 
237*26b0a53dSAdrien Destugues 	printf("T %f\n", date);
238*26b0a53dSAdrien Destugues 
239*26b0a53dSAdrien Destugues 	output.SetTime(0, 0, 0);
240*26b0a53dSAdrien Destugues 	output.AddMilliseconds(date);
241*26b0a53dSAdrien Destugues 
242*26b0a53dSAdrien Destugues 	return B_OK;
243*26b0a53dSAdrien Destugues }
244*26b0a53dSAdrien Destugues 
245*26b0a53dSAdrien Destugues 
24603b2550eSAdrien Destugues DateFormat*
247*26b0a53dSAdrien Destugues BTimeFormat::_CreateTimeFormatter(const BTimeFormatStyle style) const
24803b2550eSAdrien Destugues {
24903b2550eSAdrien Destugues 	Locale* icuLocale
25003b2550eSAdrien Destugues 		= fConventions.UseStringsFromPreferredLanguage()
25103b2550eSAdrien Destugues 			? BLanguage::Private(&fLanguage).ICULocale()
25203b2550eSAdrien Destugues 			: BFormattingConventions::Private(&fConventions).ICULocale();
25303b2550eSAdrien Destugues 
25403b2550eSAdrien Destugues 	icu::DateFormat* timeFormatter
25503b2550eSAdrien Destugues 		= icu::DateFormat::createTimeInstance(DateFormat::kShort, *icuLocale);
25603b2550eSAdrien Destugues 	if (timeFormatter == NULL)
25703b2550eSAdrien Destugues 		return NULL;
25803b2550eSAdrien Destugues 
25903b2550eSAdrien Destugues 	SimpleDateFormat* timeFormatterImpl
26003b2550eSAdrien Destugues 		= static_cast<SimpleDateFormat*>(timeFormatter);
26103b2550eSAdrien Destugues 
262*26b0a53dSAdrien Destugues 	BString format;
263*26b0a53dSAdrien Destugues 	fConventions.GetTimeFormat(style, format);
264*26b0a53dSAdrien Destugues 
26503b2550eSAdrien Destugues 	UnicodeString pattern(format.String());
26603b2550eSAdrien Destugues 	timeFormatterImpl->applyPattern(pattern);
26703b2550eSAdrien Destugues 
26803b2550eSAdrien Destugues 	return timeFormatter;
269b2c385c0SAdrien Destugues }
270