2 * Copyright 2010-2014, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Oliver Tappe <zooey@hirschkaefer.de>
7 * Adrien Desutugues <pulkomandy@pulkomandy.tk>
10 #include <unicode/uversion.h>
11 #include <DateFormat.h>
13 #include <AutoDeleter.h>
15 #include <FormattingConventionsPrivate.h>
16 #include <LanguagePrivate.h>
18 #include <LocaleRoster.h>
21 #include <ICUWrapper.h>
23 #include <unicode/datefmt.h>
24 #include <unicode/dtfmtsym.h>
25 #include <unicode/smpdtfmt.h>
30 static const DateFormatSymbols::DtWidthType kDateFormatStyleToWidth
[] = {
31 DateFormatSymbols::WIDE
,
32 DateFormatSymbols::ABBREVIATED
,
33 DateFormatSymbols::SHORT
,
34 DateFormatSymbols::NARROW
,
38 BDateFormat::BDateFormat(const BLocale
* locale
)
44 BDateFormat::BDateFormat(const BLanguage
& language
,
45 const BFormattingConventions
& conventions
)
46 : BFormat(language
, conventions
)
51 BDateFormat::BDateFormat(const BDateFormat
&other
)
57 BDateFormat::~BDateFormat()
63 BDateFormat::GetDateFormat(BDateFormatStyle style
,
64 BString
& outFormat
) const
66 return fConventions
.GetDateFormat(style
, outFormat
);
71 BDateFormat::SetDateFormat(BDateFormatStyle style
,
72 const BString
& format
)
74 fConventions
.SetExplicitDateFormat(style
, format
);
79 BDateFormat::Format(char* string
, const size_t maxSize
, const time_t time
,
80 const BDateFormatStyle style
) const
82 ObjectDeleter
<DateFormat
> dateFormatter(_CreateDateFormatter(style
));
83 if (dateFormatter
.Get() == NULL
)
86 UnicodeString icuString
;
87 dateFormatter
->format((UDate
)time
* 1000, icuString
);
89 CheckedArrayByteSink
stringConverter(string
, maxSize
);
90 icuString
.toUTF8(stringConverter
);
92 if (stringConverter
.Overflowed())
95 return stringConverter
.NumberOfBytesWritten();
100 BDateFormat::Format(BString
& string
, const time_t time
,
101 const BDateFormatStyle style
, const BTimeZone
* timeZone
) const
103 ObjectDeleter
<DateFormat
> dateFormatter(_CreateDateFormatter(style
));
104 if (dateFormatter
.Get() == NULL
)
107 if (timeZone
!= NULL
) {
108 ObjectDeleter
<TimeZone
> icuTimeZone(
109 TimeZone::createTimeZone(timeZone
->ID().String()));
110 if (icuTimeZone
.Get() == NULL
)
112 dateFormatter
->setTimeZone(*icuTimeZone
.Get());
115 UnicodeString icuString
;
116 dateFormatter
->format((UDate
)time
* 1000, icuString
);
119 BStringByteSink
stringConverter(&string
);
120 icuString
.toUTF8(stringConverter
);
127 BDateFormat::Format(BString
& string
, const BDate
& time
,
128 const BDateFormatStyle style
, const BTimeZone
* timeZone
) const
133 ObjectDeleter
<DateFormat
> dateFormatter(_CreateDateFormatter(style
));
134 if (dateFormatter
.Get() == NULL
)
137 UErrorCode err
= U_ZERO_ERROR
;
138 ObjectDeleter
<Calendar
> calendar(Calendar::createInstance(err
));
142 if (timeZone
!= NULL
) {
143 ObjectDeleter
<TimeZone
> icuTimeZone(
144 TimeZone::createTimeZone(timeZone
->ID().String()));
145 if (icuTimeZone
.Get() == NULL
)
147 dateFormatter
->setTimeZone(*icuTimeZone
.Get());
148 calendar
->setTimeZone(*icuTimeZone
.Get());
151 // Note ICU calendar uses months in range 0..11, while we use the more
152 // natural 1..12 in BDate.
153 calendar
->set(time
.Year(), time
.Month() - 1, time
.Day());
155 UnicodeString icuString
;
157 dateFormatter
->format(*calendar
.Get(), icuString
, p
);
160 BStringByteSink
stringConverter(&string
);
161 icuString
.toUTF8(stringConverter
);
168 BDateFormat::Format(BString
& string
, int*& fieldPositions
, int& fieldCount
,
169 const time_t time
, const BDateFormatStyle style
) const
171 ObjectDeleter
<DateFormat
> dateFormatter(_CreateDateFormatter(style
));
172 if (dateFormatter
.Get() == NULL
)
175 fieldPositions
= NULL
;
176 UErrorCode error
= U_ZERO_ERROR
;
177 icu::FieldPositionIterator positionIterator
;
178 UnicodeString icuString
;
179 dateFormatter
->format((UDate
)time
* 1000, icuString
, &positionIterator
,
182 if (error
!= U_ZERO_ERROR
)
185 icu::FieldPosition field
;
186 std::vector
<int> fieldPosStorage
;
188 while (positionIterator
.next(field
)) {
189 fieldPosStorage
.push_back(field
.getBeginIndex());
190 fieldPosStorage
.push_back(field
.getEndIndex());
194 fieldPositions
= (int*) malloc(fieldCount
* sizeof(int));
196 for (int i
= 0 ; i
< fieldCount
; i
++ )
197 fieldPositions
[i
] = fieldPosStorage
[i
];
200 BStringByteSink
stringConverter(&string
);
202 icuString
.toUTF8(stringConverter
);
209 BDateFormat::GetFields(BDateElement
*& fields
, int& fieldCount
,
210 BDateFormatStyle style
) const
212 ObjectDeleter
<DateFormat
> dateFormatter(_CreateDateFormatter(style
));
213 if (dateFormatter
.Get() == NULL
)
217 UErrorCode error
= U_ZERO_ERROR
;
218 icu::FieldPositionIterator positionIterator
;
219 UnicodeString icuString
;
221 dateFormatter
->format((UDate
)time(&now
) * 1000, icuString
,
222 &positionIterator
, error
);
224 if (U_FAILURE(error
))
227 icu::FieldPosition field
;
228 std::vector
<int> fieldPosStorage
;
230 while (positionIterator
.next(field
)) {
231 fieldPosStorage
.push_back(field
.getField());
235 fields
= (BDateElement
*) malloc(fieldCount
* sizeof(BDateElement
));
237 for (int i
= 0 ; i
< fieldCount
; i
++ ) {
238 switch (fieldPosStorage
[i
]) {
239 case UDAT_YEAR_FIELD
:
240 fields
[i
] = B_DATE_ELEMENT_YEAR
;
242 case UDAT_MONTH_FIELD
:
243 fields
[i
] = B_DATE_ELEMENT_MONTH
;
245 case UDAT_DATE_FIELD
:
246 fields
[i
] = B_DATE_ELEMENT_DAY
;
248 case UDAT_DAY_OF_WEEK_FIELD
:
249 fields
[i
] = B_DATE_ELEMENT_WEEKDAY
;
252 fields
[i
] = B_DATE_ELEMENT_INVALID
;
262 BDateFormat::GetStartOfWeek(BWeekday
* startOfWeek
) const
264 if (startOfWeek
== NULL
)
267 UErrorCode err
= U_ZERO_ERROR
;
268 ObjectDeleter
<Calendar
> calendar
= Calendar::createInstance(
269 *BFormattingConventions::Private(&fConventions
).ICULocale(), err
);
274 UCalendarDaysOfWeek icuWeekStart
= calendar
->getFirstDayOfWeek(err
);
278 switch (icuWeekStart
) {
280 *startOfWeek
= B_WEEKDAY_SUNDAY
;
283 *startOfWeek
= B_WEEKDAY_MONDAY
;
286 *startOfWeek
= B_WEEKDAY_TUESDAY
;
289 *startOfWeek
= B_WEEKDAY_WEDNESDAY
;
292 *startOfWeek
= B_WEEKDAY_THURSDAY
;
295 *startOfWeek
= B_WEEKDAY_FRIDAY
;
298 *startOfWeek
= B_WEEKDAY_SATURDAY
;
309 BDateFormat::GetMonthName(int month
, BString
& outName
,
310 const BDateFormatStyle style
) const
312 if (style
< 0 || style
>= B_DATE_FORMAT_STYLE_COUNT
)
315 DateFormat
* format
= _CreateDateFormatter(B_LONG_DATE_FORMAT
);
317 SimpleDateFormat
* simpleFormat
= dynamic_cast<SimpleDateFormat
*>(format
);
318 if (simpleFormat
== NULL
) {
323 const DateFormatSymbols
* symbols
= simpleFormat
->getDateFormatSymbols();
326 const UnicodeString
* names
= symbols
->getMonths(count
,
327 DateFormatSymbols::STANDALONE
, kDateFormatStyleToWidth
[style
]);
329 if (month
> count
|| month
<= 0) {
334 BStringByteSink
stringConverter(&outName
);
335 names
[month
- 1].toUTF8(stringConverter
);
343 BDateFormat::GetDayName(int day
, BString
& outName
,
344 const BDateFormatStyle style
) const
346 if (style
< 0 || style
>= B_DATE_FORMAT_STYLE_COUNT
)
349 DateFormat
* format
= _CreateDateFormatter(B_LONG_DATE_FORMAT
);
351 SimpleDateFormat
* simpleFormat
= dynamic_cast<SimpleDateFormat
*>(format
);
352 if (simpleFormat
== NULL
) {
357 const DateFormatSymbols
* symbols
= simpleFormat
->getDateFormatSymbols();
360 const UnicodeString
* names
= symbols
->getWeekdays(count
,
361 DateFormatSymbols::STANDALONE
, kDateFormatStyleToWidth
[style
]);
363 if (day
>= count
|| day
<= 0) {
368 BStringByteSink
stringConverter(&outName
);
369 names
[_ConvertDayNumberToICU(day
)].toUTF8(stringConverter
);
377 BDateFormat::Parse(BString source
, BDateFormatStyle style
, BDate
& output
)
379 // FIXME currently this parses a date in any timezone (or the local one if
380 // none is specified) to a BDate in UTC. This may not be a good idea, we
381 // may want to parse to a "local" date instead. But BDate should be made
382 // timezone aware so things like BDate::Difference can work for dates in
383 // different timezones.
384 ObjectDeleter
<DateFormat
> dateFormatter(_CreateDateFormatter(style
));
385 if (dateFormatter
.Get() == NULL
)
389 UDate date
= dateFormatter
->parse(UnicodeString::fromUTF8(source
.String()),
392 output
.SetDate(1970, 1, 1);
393 output
.AddDays(date
/ U_MILLIS_PER_DAY
+ 0.5);
400 BDateFormat::_CreateDateFormatter(const BDateFormatStyle style
) const
403 = fConventions
.UseStringsFromPreferredLanguage()
404 ? BLanguage::Private(&fLanguage
).ICULocale()
405 : BFormattingConventions::Private(&fConventions
).ICULocale();
407 icu::DateFormat
* dateFormatter
408 = icu::DateFormat::createDateInstance(DateFormat::kShort
, *icuLocale
);
409 if (dateFormatter
== NULL
)
412 SimpleDateFormat
* dateFormatterImpl
413 = static_cast<SimpleDateFormat
*>(dateFormatter
);
416 fConventions
.GetDateFormat(style
, format
);
418 UnicodeString
pattern(format
.String());
419 dateFormatterImpl
->applyPattern(pattern
);
421 return dateFormatter
;