2 *******************************************************************************
3 * Copyright (C) 1997-2015, International Business Machines Corporation and *
4 * others. All Rights Reserved. *
5 *******************************************************************************
9 * Modification History:
11 * Date Name Description
12 * 02/19/97 aliu Converted from java.
13 * 03/31/97 aliu Modified extensively to work with 50 locales.
14 * 04/01/97 aliu Added support for centuries.
15 * 08/12/97 aliu Fixed operator== to use Calendar::equivalentTo.
16 * 07/20/98 stephen Changed ParsePosition initialization
17 ********************************************************************************
20 #include "unicode/utypes.h"
22 #if !UCONFIG_NO_FORMATTING
24 #include "unicode/ures.h"
25 #include "unicode/datefmt.h"
26 #include "unicode/smpdtfmt.h"
27 #include "unicode/dtptngen.h"
28 #include "unicode/udisplaycontext.h"
30 #include "sharedobject.h"
31 #include "unifiedcache.h"
37 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
41 // *****************************************************************************
43 // *****************************************************************************
47 class U_I18N_API DateFmtBestPattern
: public SharedObject
{
49 UnicodeString fPattern
;
51 DateFmtBestPattern(const UnicodeString
&pattern
)
52 : fPattern(pattern
) { }
53 ~DateFmtBestPattern();
56 DateFmtBestPattern::~DateFmtBestPattern() {
60 const DateFmtBestPattern
*LocaleCacheKey
<DateFmtBestPattern
>::createObject(
61 const void * /*creationContext*/, UErrorCode
&status
) const {
62 status
= U_UNSUPPORTED_ERROR
;
66 class U_I18N_API DateFmtBestPatternKey
: public LocaleCacheKey
<DateFmtBestPattern
> {
68 UnicodeString fSkeleton
;
70 DateFmtBestPatternKey(
72 const UnicodeString
&skeleton
,
74 : LocaleCacheKey
<DateFmtBestPattern
>(loc
),
75 fSkeleton(DateTimePatternGenerator::staticGetSkeleton(skeleton
, status
)) { }
76 DateFmtBestPatternKey(const DateFmtBestPatternKey
&other
) :
77 LocaleCacheKey
<DateFmtBestPattern
>(other
),
78 fSkeleton(other
.fSkeleton
) { }
79 virtual ~DateFmtBestPatternKey();
80 virtual int32_t hashCode() const {
81 return 37 * LocaleCacheKey
<DateFmtBestPattern
>::hashCode() + fSkeleton
.hashCode();
83 virtual UBool
operator==(const CacheKeyBase
&other
) const {
88 if (!LocaleCacheKey
<DateFmtBestPattern
>::operator==(other
)) {
91 // We know that this and other are of same class if we get this far.
92 const DateFmtBestPatternKey
&realOther
=
93 static_cast<const DateFmtBestPatternKey
&>(other
);
94 return (realOther
.fSkeleton
== fSkeleton
);
96 virtual CacheKeyBase
*clone() const {
97 return new DateFmtBestPatternKey(*this);
99 virtual const DateFmtBestPattern
*createObject(
100 const void * /*unused*/, UErrorCode
&status
) const {
101 LocalPointer
<DateTimePatternGenerator
> dtpg(
102 DateTimePatternGenerator::createInstance(fLoc
, status
));
103 if (U_FAILURE(status
)) {
107 LocalPointer
<DateFmtBestPattern
> pattern(
108 new DateFmtBestPattern(
109 dtpg
->getBestPattern(fSkeleton
, status
)),
111 if (U_FAILURE(status
)) {
114 DateFmtBestPattern
*result
= pattern
.orphan();
120 DateFmtBestPatternKey::~DateFmtBestPatternKey() { }
123 DateFormat::DateFormat()
126 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
130 //----------------------------------------------------------------------
132 DateFormat::DateFormat(const DateFormat
& other
)
136 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
141 //----------------------------------------------------------------------
143 DateFormat
& DateFormat::operator=(const DateFormat
& other
)
148 delete fNumberFormat
;
149 if(other
.fCalendar
) {
150 fCalendar
= other
.fCalendar
->clone();
154 if(other
.fNumberFormat
) {
155 fNumberFormat
= (NumberFormat
*)other
.fNumberFormat
->clone();
157 fNumberFormat
= NULL
;
159 fBoolFlags
= other
.fBoolFlags
;
160 fCapitalizationContext
= other
.fCapitalizationContext
;
165 //----------------------------------------------------------------------
167 DateFormat::~DateFormat()
170 delete fNumberFormat
;
173 //----------------------------------------------------------------------
176 DateFormat::operator==(const Format
& other
) const
178 // This protected comparison operator should only be called by subclasses
179 // which have confirmed that the other object being compared against is
180 // an instance of a sublcass of DateFormat. THIS IS IMPORTANT.
182 // Format::operator== guarantees that this cast is safe
183 DateFormat
* fmt
= (DateFormat
*)&other
;
185 return (this == fmt
) ||
186 (Format::operator==(other
) &&
187 fCalendar
&&(fCalendar
->isEquivalentTo(*fmt
->fCalendar
)) &&
188 (fNumberFormat
&& *fNumberFormat
== *fmt
->fNumberFormat
) &&
189 (fCapitalizationContext
== fmt
->fCapitalizationContext
) );
192 //----------------------------------------------------------------------
195 DateFormat::format(const Formattable
& obj
,
196 UnicodeString
& appendTo
,
197 FieldPosition
& fieldPosition
,
198 UErrorCode
& status
) const
200 if (U_FAILURE(status
)) return appendTo
;
202 // if the type of the Formattable is double or long, treat it as if it were a Date
204 switch (obj
.getType())
206 case Formattable::kDate
:
207 date
= obj
.getDate();
209 case Formattable::kDouble
:
210 date
= (UDate
)obj
.getDouble();
212 case Formattable::kLong
:
213 date
= (UDate
)obj
.getLong();
216 status
= U_ILLEGAL_ARGUMENT_ERROR
;
221 //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex())
222 // status = U_ILLEGAL_ARGUMENT_ERROR;
224 return format(date
, appendTo
, fieldPosition
);
227 //----------------------------------------------------------------------
230 DateFormat::format(const Formattable
& obj
,
231 UnicodeString
& appendTo
,
232 FieldPositionIterator
* posIter
,
233 UErrorCode
& status
) const
235 if (U_FAILURE(status
)) return appendTo
;
237 // if the type of the Formattable is double or long, treat it as if it were a Date
239 switch (obj
.getType())
241 case Formattable::kDate
:
242 date
= obj
.getDate();
244 case Formattable::kDouble
:
245 date
= (UDate
)obj
.getDouble();
247 case Formattable::kLong
:
248 date
= (UDate
)obj
.getLong();
251 status
= U_ILLEGAL_ARGUMENT_ERROR
;
256 //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex())
257 // status = U_ILLEGAL_ARGUMENT_ERROR;
259 return format(date
, appendTo
, posIter
, status
);
262 //----------------------------------------------------------------------
264 // Default implementation for backwards compatibility, subclasses should implement.
266 DateFormat::format(Calendar
& /* unused cal */,
267 UnicodeString
& appendTo
,
268 FieldPositionIterator
* /* unused posIter */,
269 UErrorCode
& status
) const {
270 if (U_SUCCESS(status
)) {
271 status
= U_UNSUPPORTED_ERROR
;
276 //----------------------------------------------------------------------
279 DateFormat::format(UDate date
, UnicodeString
& appendTo
, FieldPosition
& fieldPosition
) const {
280 if (fCalendar
!= NULL
) {
281 // Use a clone of our calendar instance
282 Calendar
* calClone
= fCalendar
->clone();
283 if (calClone
!= NULL
) {
284 UErrorCode ec
= U_ZERO_ERROR
;
285 calClone
->setTime(date
, ec
);
287 format(*calClone
, appendTo
, fieldPosition
);
295 //----------------------------------------------------------------------
298 DateFormat::format(UDate date
, UnicodeString
& appendTo
, FieldPositionIterator
* posIter
,
299 UErrorCode
& status
) const {
300 if (fCalendar
!= NULL
) {
301 Calendar
* calClone
= fCalendar
->clone();
302 if (calClone
!= NULL
) {
303 calClone
->setTime(date
, status
);
304 if (U_SUCCESS(status
)) {
305 format(*calClone
, appendTo
, posIter
, status
);
313 //----------------------------------------------------------------------
316 DateFormat::format(UDate date
, UnicodeString
& appendTo
) const
318 // Note that any error information is just lost. That's okay
319 // for this convenience method.
320 FieldPosition
fpos(0);
321 return format(date
, appendTo
, fpos
);
324 //----------------------------------------------------------------------
327 DateFormat::parse(const UnicodeString
& text
,
328 ParsePosition
& pos
) const
330 UDate d
= 0; // Error return UDate is 0 (the epoch)
331 if (fCalendar
!= NULL
) {
332 Calendar
* calClone
= fCalendar
->clone();
333 if (calClone
!= NULL
) {
334 int32_t start
= pos
.getIndex();
336 parse(text
, *calClone
, pos
);
337 if (pos
.getIndex() != start
) {
338 UErrorCode ec
= U_ZERO_ERROR
;
339 d
= calClone
->getTime(ec
);
341 // We arrive here if fCalendar => calClone is non-lenient and
342 // there is an out-of-range field. We don't know which field
343 // was illegal so we set the error index to the start.
345 pos
.setErrorIndex(start
);
355 //----------------------------------------------------------------------
358 DateFormat::parse(const UnicodeString
& text
,
359 UErrorCode
& status
) const
361 if (U_FAILURE(status
)) return 0;
363 ParsePosition
pos(0);
364 UDate result
= parse(text
, pos
);
365 if (pos
.getIndex() == 0) {
366 #if defined (U_DEBUG_CAL)
367 fprintf(stderr
, "%s:%d - - failed to parse - err index %d\n"
368 , __FILE__
, __LINE__
, pos
.getErrorIndex() );
370 status
= U_ILLEGAL_ARGUMENT_ERROR
;
375 //----------------------------------------------------------------------
378 DateFormat::parseObject(const UnicodeString
& source
,
380 ParsePosition
& pos
) const
382 result
.setDate(parse(source
, pos
));
385 //----------------------------------------------------------------------
387 DateFormat
* U_EXPORT2
388 DateFormat::createTimeInstance(DateFormat::EStyle style
,
389 const Locale
& aLocale
)
391 return createDateTimeInstance(kNone
, style
, aLocale
);
394 //----------------------------------------------------------------------
396 DateFormat
* U_EXPORT2
397 DateFormat::createDateInstance(DateFormat::EStyle style
,
398 const Locale
& aLocale
)
400 return createDateTimeInstance(style
, kNone
, aLocale
);
403 //----------------------------------------------------------------------
405 DateFormat
* U_EXPORT2
406 DateFormat::createDateTimeInstance(EStyle dateStyle
,
408 const Locale
& aLocale
)
410 if(dateStyle
!= kNone
)
412 dateStyle
= (EStyle
) (dateStyle
+ kDateOffset
);
414 return create(timeStyle
, dateStyle
, aLocale
);
417 //----------------------------------------------------------------------
419 DateFormat
* U_EXPORT2
420 DateFormat::createInstance()
422 return createDateTimeInstance(kShort
, kShort
, Locale::getDefault());
425 //----------------------------------------------------------------------
427 UnicodeString U_EXPORT2
428 DateFormat::getBestPattern(
429 const Locale
&locale
,
430 const UnicodeString
&skeleton
,
431 UErrorCode
&status
) {
432 UnifiedCache
*cache
= UnifiedCache::getInstance(status
);
433 if (U_FAILURE(status
)) {
434 return UnicodeString();
436 DateFmtBestPatternKey
key(locale
, skeleton
, status
);
437 const DateFmtBestPattern
*patternPtr
= NULL
;
438 cache
->get(key
, patternPtr
, status
);
439 if (U_FAILURE(status
)) {
440 return UnicodeString();
442 UnicodeString
result(patternPtr
->fPattern
);
443 patternPtr
->removeRef();
447 DateFormat
* U_EXPORT2
448 DateFormat::createInstanceForSkeleton(
449 Calendar
*calendarToAdopt
,
450 const UnicodeString
& skeleton
,
451 const Locale
&locale
,
452 UErrorCode
&status
) {
453 LocalPointer
<Calendar
> calendar(calendarToAdopt
);
454 if (U_FAILURE(status
)) {
457 if (calendar
.isNull()) {
458 status
= U_ILLEGAL_ARGUMENT_ERROR
;
461 DateFormat
*result
= createInstanceForSkeleton(skeleton
, locale
, status
);
462 if (U_FAILURE(status
)) {
465 result
->adoptCalendar(calendar
.orphan());
469 DateFormat
* U_EXPORT2
470 DateFormat::createInstanceForSkeleton(
471 const UnicodeString
& skeleton
,
472 const Locale
&locale
,
473 UErrorCode
&status
) {
474 if (U_FAILURE(status
)) {
477 LocalPointer
<DateFormat
> df(
478 new SimpleDateFormat(
479 getBestPattern(locale
, skeleton
, status
),
482 return U_SUCCESS(status
) ? df
.orphan() : NULL
;
485 DateFormat
* U_EXPORT2
486 DateFormat::createInstanceForSkeleton(
487 const UnicodeString
& skeleton
,
488 UErrorCode
&status
) {
489 return createInstanceForSkeleton(
490 skeleton
, Locale::getDefault(), status
);
493 //----------------------------------------------------------------------
495 DateFormat
* U_EXPORT2
496 DateFormat::create(EStyle timeStyle
, EStyle dateStyle
, const Locale
& locale
)
498 UErrorCode status
= U_ZERO_ERROR
;
499 #if U_PLATFORM_HAS_WIN32_API
501 int32_t count
= locale
.getKeywordValue("compat", buffer
, sizeof(buffer
), status
);
503 // if the locale has "@compat=host", create a host-specific DateFormat...
504 if (count
> 0 && uprv_strcmp(buffer
, "host") == 0) {
505 Win32DateFormat
*f
= new Win32DateFormat(timeStyle
, dateStyle
, locale
, status
);
507 if (U_SUCCESS(status
)) {
516 if(/*((timeStyle!=UDAT_NONE)&&(timeStyle & UDAT_RELATIVE)) || */((dateStyle
!=kNone
)&&((dateStyle
-kDateOffset
) & UDAT_RELATIVE
))) {
517 RelativeDateFormat
*r
= new RelativeDateFormat((UDateFormatStyle
)timeStyle
, (UDateFormatStyle
)(dateStyle
-kDateOffset
), locale
, status
);
518 if(U_SUCCESS(status
)) return r
;
520 status
= U_ZERO_ERROR
;
523 // Try to create a SimpleDateFormat of the desired style.
524 SimpleDateFormat
*f
= new SimpleDateFormat(timeStyle
, dateStyle
, locale
, status
);
525 if (U_SUCCESS(status
)) return f
;
528 // If that fails, try to create a format using the default pattern and
529 // the DateFormatSymbols for this locale.
530 status
= U_ZERO_ERROR
;
531 f
= new SimpleDateFormat(locale
, status
);
532 if (U_SUCCESS(status
)) return f
;
535 // This should never really happen, because the preceding constructor
536 // should always succeed. If the resource data is unavailable, a last
537 // resort object should be returned.
541 //----------------------------------------------------------------------
543 const Locale
* U_EXPORT2
544 DateFormat::getAvailableLocales(int32_t& count
)
546 // Get the list of installed locales.
547 // Even if root has the correct date format for this locale,
548 // it's still a valid locale (we don't worry about data fallbacks).
549 return Locale::getAvailableLocales(count
);
552 //----------------------------------------------------------------------
555 DateFormat::adoptCalendar(Calendar
* newCalendar
)
558 fCalendar
= newCalendar
;
561 //----------------------------------------------------------------------
563 DateFormat::setCalendar(const Calendar
& newCalendar
)
565 Calendar
* newCalClone
= newCalendar
.clone();
566 if (newCalClone
!= NULL
) {
567 adoptCalendar(newCalClone
);
571 //----------------------------------------------------------------------
574 DateFormat::getCalendar() const
579 //----------------------------------------------------------------------
582 DateFormat::adoptNumberFormat(NumberFormat
* newNumberFormat
)
584 delete fNumberFormat
;
585 fNumberFormat
= newNumberFormat
;
586 newNumberFormat
->setParseIntegerOnly(TRUE
);
588 //----------------------------------------------------------------------
591 DateFormat::setNumberFormat(const NumberFormat
& newNumberFormat
)
593 NumberFormat
* newNumFmtClone
= (NumberFormat
*)newNumberFormat
.clone();
594 if (newNumFmtClone
!= NULL
) {
595 adoptNumberFormat(newNumFmtClone
);
599 //----------------------------------------------------------------------
602 DateFormat::getNumberFormat() const
604 return fNumberFormat
;
607 //----------------------------------------------------------------------
610 DateFormat::adoptTimeZone(TimeZone
* zone
)
612 if (fCalendar
!= NULL
) {
613 fCalendar
->adoptTimeZone(zone
);
616 //----------------------------------------------------------------------
619 DateFormat::setTimeZone(const TimeZone
& zone
)
621 if (fCalendar
!= NULL
) {
622 fCalendar
->setTimeZone(zone
);
626 //----------------------------------------------------------------------
629 DateFormat::getTimeZone() const
631 if (fCalendar
!= NULL
) {
632 return fCalendar
->getTimeZone();
634 // If calendar doesn't exists, create default timezone.
635 // fCalendar is rarely null
636 return *(TimeZone::createDefault());
639 //----------------------------------------------------------------------
642 DateFormat::setLenient(UBool lenient
)
644 if (fCalendar
!= NULL
) {
645 fCalendar
->setLenient(lenient
);
647 UErrorCode status
= U_ZERO_ERROR
;
648 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, lenient
, status
);
649 setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, lenient
, status
);
652 //----------------------------------------------------------------------
655 DateFormat::isLenient() const
657 UBool lenient
= TRUE
;
658 if (fCalendar
!= NULL
) {
659 lenient
= fCalendar
->isLenient();
661 UErrorCode status
= U_ZERO_ERROR
;
663 && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
)
664 && getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
);
668 DateFormat::setCalendarLenient(UBool lenient
)
670 if (fCalendar
!= NULL
) {
671 fCalendar
->setLenient(lenient
);
675 //----------------------------------------------------------------------
678 DateFormat::isCalendarLenient() const
680 if (fCalendar
!= NULL
) {
681 return fCalendar
->isLenient();
683 // fCalendar is rarely null
688 //----------------------------------------------------------------------
691 void DateFormat::setContext(UDisplayContext value
, UErrorCode
& status
)
693 if (U_FAILURE(status
))
695 if ( (UDisplayContextType
)((uint32_t)value
>> 8) == UDISPCTX_TYPE_CAPITALIZATION
) {
696 fCapitalizationContext
= value
;
698 status
= U_ILLEGAL_ARGUMENT_ERROR
;
703 //----------------------------------------------------------------------
706 UDisplayContext
DateFormat::getContext(UDisplayContextType type
, UErrorCode
& status
) const
708 if (U_FAILURE(status
))
709 return (UDisplayContext
)0;
710 if (type
!= UDISPCTX_TYPE_CAPITALIZATION
) {
711 status
= U_ILLEGAL_ARGUMENT_ERROR
;
712 return (UDisplayContext
)0;
714 return fCapitalizationContext
;
718 //----------------------------------------------------------------------
722 DateFormat::setBooleanAttribute(UDateFormatBooleanAttribute attr
,
724 UErrorCode
&status
) {
725 if(!fBoolFlags
.isValidValue(newValue
)) {
726 status
= U_ILLEGAL_ARGUMENT_ERROR
;
728 fBoolFlags
.set(attr
, newValue
);
734 //----------------------------------------------------------------------
737 DateFormat::getBooleanAttribute(UDateFormatBooleanAttribute attr
, UErrorCode
&/*status*/) const {
739 return fBoolFlags
.get(attr
);
744 #endif /* #if !UCONFIG_NO_FORMATTING */