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 BDateFormat::BDateFormat(const BLocale
* locale
)
36 BDateFormat::BDateFormat(const BLanguage
& language
,
37 const BFormattingConventions
& conventions
)
38 : BFormat(language
, conventions
)
43 BDateFormat::BDateFormat(const BDateFormat
&other
)
49 BDateFormat::~BDateFormat()
55 BDateFormat::GetDateFormat(BDateFormatStyle style
,
56 BString
& outFormat
) const
58 return fConventions
.GetDateFormat(style
, outFormat
);
63 BDateFormat::SetDateFormat(BDateFormatStyle style
,
64 const BString
& format
)
66 fConventions
.SetExplicitDateFormat(style
, format
);
71 BDateFormat::Format(char* string
, const size_t maxSize
, const time_t time
,
72 const BDateFormatStyle style
) const
74 ObjectDeleter
<DateFormat
> dateFormatter(_CreateDateFormatter(style
));
75 if (dateFormatter
.Get() == NULL
)
78 UnicodeString icuString
;
79 dateFormatter
->format((UDate
)time
* 1000, icuString
);
81 CheckedArrayByteSink
stringConverter(string
, maxSize
);
82 icuString
.toUTF8(stringConverter
);
84 if (stringConverter
.Overflowed())
87 return stringConverter
.NumberOfBytesWritten();
92 BDateFormat::Format(BString
& string
, const time_t time
,
93 const BDateFormatStyle style
, const BTimeZone
* timeZone
) const
95 ObjectDeleter
<DateFormat
> dateFormatter(_CreateDateFormatter(style
));
96 if (dateFormatter
.Get() == NULL
)
99 if (timeZone
!= NULL
) {
100 ObjectDeleter
<TimeZone
> icuTimeZone(
101 TimeZone::createTimeZone(timeZone
->ID().String()));
102 if (icuTimeZone
.Get() == NULL
)
104 dateFormatter
->setTimeZone(*icuTimeZone
.Get());
107 UnicodeString icuString
;
108 dateFormatter
->format((UDate
)time
* 1000, icuString
);
111 BStringByteSink
stringConverter(&string
);
112 icuString
.toUTF8(stringConverter
);
119 BDateFormat::Format(BString
& string
, const BDate
& time
,
120 const BDateFormatStyle style
, const BTimeZone
* timeZone
) const
125 ObjectDeleter
<DateFormat
> dateFormatter(_CreateDateFormatter(style
));
126 if (dateFormatter
.Get() == NULL
)
129 UErrorCode err
= U_ZERO_ERROR
;
130 ObjectDeleter
<Calendar
> calendar(Calendar::createInstance(err
));
134 if (timeZone
!= NULL
) {
135 ObjectDeleter
<TimeZone
> icuTimeZone(
136 TimeZone::createTimeZone(timeZone
->ID().String()));
137 if (icuTimeZone
.Get() == NULL
)
139 dateFormatter
->setTimeZone(*icuTimeZone
.Get());
140 calendar
->setTimeZone(*icuTimeZone
.Get());
143 // Note ICU calendar uses months in range 0..11, while we use the more
144 // natural 1..12 in BDate.
145 calendar
->set(time
.Year(), time
.Month() - 1, time
.Day());
147 UnicodeString icuString
;
149 dateFormatter
->format(*calendar
.Get(), icuString
, p
);
152 BStringByteSink
stringConverter(&string
);
153 icuString
.toUTF8(stringConverter
);
160 BDateFormat::Format(BString
& string
, int*& fieldPositions
, int& fieldCount
,
161 const time_t time
, const BDateFormatStyle style
) const
163 ObjectDeleter
<DateFormat
> dateFormatter(_CreateDateFormatter(style
));
164 if (dateFormatter
.Get() == NULL
)
167 fieldPositions
= NULL
;
168 UErrorCode error
= U_ZERO_ERROR
;
169 icu::FieldPositionIterator positionIterator
;
170 UnicodeString icuString
;
171 dateFormatter
->format((UDate
)time
* 1000, icuString
, &positionIterator
,
174 if (error
!= U_ZERO_ERROR
)
177 icu::FieldPosition field
;
178 std::vector
<int> fieldPosStorage
;
180 while (positionIterator
.next(field
)) {
181 fieldPosStorage
.push_back(field
.getBeginIndex());
182 fieldPosStorage
.push_back(field
.getEndIndex());
186 fieldPositions
= (int*) malloc(fieldCount
* sizeof(int));
188 for (int i
= 0 ; i
< fieldCount
; i
++ )
189 fieldPositions
[i
] = fieldPosStorage
[i
];
192 BStringByteSink
stringConverter(&string
);
194 icuString
.toUTF8(stringConverter
);
201 BDateFormat::GetFields(BDateElement
*& fields
, int& fieldCount
,
202 BDateFormatStyle style
) const
204 ObjectDeleter
<DateFormat
> dateFormatter(_CreateDateFormatter(style
));
205 if (dateFormatter
.Get() == NULL
)
209 UErrorCode error
= U_ZERO_ERROR
;
210 icu::FieldPositionIterator positionIterator
;
211 UnicodeString icuString
;
213 dateFormatter
->format((UDate
)time(&now
) * 1000, icuString
,
214 &positionIterator
, error
);
216 if (U_FAILURE(error
))
219 icu::FieldPosition field
;
220 std::vector
<int> fieldPosStorage
;
222 while (positionIterator
.next(field
)) {
223 fieldPosStorage
.push_back(field
.getField());
227 fields
= (BDateElement
*) malloc(fieldCount
* sizeof(BDateElement
));
229 for (int i
= 0 ; i
< fieldCount
; i
++ ) {
230 switch (fieldPosStorage
[i
]) {
231 case UDAT_YEAR_FIELD
:
232 fields
[i
] = B_DATE_ELEMENT_YEAR
;
234 case UDAT_MONTH_FIELD
:
235 fields
[i
] = B_DATE_ELEMENT_MONTH
;
237 case UDAT_DATE_FIELD
:
238 fields
[i
] = B_DATE_ELEMENT_DAY
;
241 fields
[i
] = B_DATE_ELEMENT_INVALID
;
251 BDateFormat::GetStartOfWeek(BWeekday
* startOfWeek
) const
253 if (startOfWeek
== NULL
)
256 UErrorCode err
= U_ZERO_ERROR
;
257 ObjectDeleter
<Calendar
> calendar
= Calendar::createInstance(
258 *BFormattingConventions::Private(&fConventions
).ICULocale(), err
);
263 UCalendarDaysOfWeek icuWeekStart
= calendar
->getFirstDayOfWeek(err
);
267 switch (icuWeekStart
) {
269 *startOfWeek
= B_WEEKDAY_SUNDAY
;
272 *startOfWeek
= B_WEEKDAY_MONDAY
;
275 *startOfWeek
= B_WEEKDAY_TUESDAY
;
278 *startOfWeek
= B_WEEKDAY_WEDNESDAY
;
281 *startOfWeek
= B_WEEKDAY_THURSDAY
;
284 *startOfWeek
= B_WEEKDAY_FRIDAY
;
287 *startOfWeek
= B_WEEKDAY_SATURDAY
;
298 BDateFormat::GetMonthName(int month
, BString
& outName
)
300 DateFormat
* format
= _CreateDateFormatter(B_LONG_DATE_FORMAT
);
302 SimpleDateFormat
* simpleFormat
= dynamic_cast<SimpleDateFormat
*>(format
);
303 if (simpleFormat
== NULL
) {
308 const DateFormatSymbols
* symbols
= simpleFormat
->getDateFormatSymbols();
311 const UnicodeString
* names
= symbols
->getMonths(count
);
313 if (month
> count
|| month
<= 0) {
318 BStringByteSink
stringConverter(&outName
);
319 names
[month
- 1].toUTF8(stringConverter
);
327 BDateFormat::Parse(BString source
, BDateFormatStyle style
, BDate
& output
)
329 // FIXME currently this parses a date in any timezone (or the local one if
330 // none is specified) to a BDate in UTC. This may not be a good idea, we
331 // may want to parse to a "local" date instead. But BDate should be made
332 // timezone aware so things like BDate::Difference can work for dates in
333 // different timezones.
334 ObjectDeleter
<DateFormat
> dateFormatter(_CreateDateFormatter(style
));
335 if (dateFormatter
.Get() == NULL
)
339 UDate date
= dateFormatter
->parse(UnicodeString::fromUTF8(source
.String()),
342 output
.SetDate(1970, 1, 1);
343 output
.AddDays(date
/ U_MILLIS_PER_DAY
+ 0.5);
350 BDateFormat::_CreateDateFormatter(const BDateFormatStyle style
) const
353 = fConventions
.UseStringsFromPreferredLanguage()
354 ? BLanguage::Private(&fLanguage
).ICULocale()
355 : BFormattingConventions::Private(&fConventions
).ICULocale();
357 icu::DateFormat
* dateFormatter
358 = icu::DateFormat::createDateInstance(DateFormat::kShort
, *icuLocale
);
359 if (dateFormatter
== NULL
)
362 SimpleDateFormat
* dateFormatterImpl
363 = static_cast<SimpleDateFormat
*>(dateFormatter
);
366 fConventions
.GetDateFormat(style
, format
);
368 UnicodeString
pattern(format
.String());
369 dateFormatterImpl
->applyPattern(pattern
);
371 return dateFormatter
;