1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 *******************************************************************************
5 * Copyright (C) 1997-2015, International Business Machines Corporation and *
6 * others. All Rights Reserved. *
7 *******************************************************************************
11 * Modification History:
13 * Date Name Description
14 * 02/19/97 aliu Converted from java.
15 * 03/31/97 aliu Modified extensively to work with 50 locales.
16 * 04/01/97 aliu Added support for centuries.
17 * 08/12/97 aliu Fixed operator== to use Calendar::equivalentTo.
18 * 07/20/98 stephen Changed ParsePosition initialization
19 ********************************************************************************
22 #include "unicode/utypes.h"
24 #if !UCONFIG_NO_FORMATTING
26 #include "unicode/ures.h"
27 #include "unicode/datefmt.h"
28 #include "unicode/smpdtfmt.h"
29 #include "unicode/dtptngen.h"
30 #include "unicode/udisplaycontext.h"
32 #include "sharedobject.h"
33 #include "unifiedcache.h"
39 #if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
43 // *****************************************************************************
45 // *****************************************************************************
49 class U_I18N_API DateFmtBestPattern
: public SharedObject
{
51 UnicodeString fPattern
;
53 DateFmtBestPattern(const UnicodeString
&pattern
)
54 : fPattern(pattern
) { }
55 ~DateFmtBestPattern();
58 DateFmtBestPattern::~DateFmtBestPattern() {
62 const DateFmtBestPattern
*LocaleCacheKey
<DateFmtBestPattern
>::createObject(
63 const void * /*creationContext*/, UErrorCode
&status
) const {
64 status
= U_UNSUPPORTED_ERROR
;
68 class U_I18N_API DateFmtBestPatternKey
: public LocaleCacheKey
<DateFmtBestPattern
> {
70 UnicodeString fSkeleton
;
72 DateFmtBestPatternKey(
74 const UnicodeString
&skeleton
,
76 : LocaleCacheKey
<DateFmtBestPattern
>(loc
),
77 fSkeleton(DateTimePatternGenerator::staticGetSkeleton(skeleton
, status
)) { }
78 DateFmtBestPatternKey(const DateFmtBestPatternKey
&other
) :
79 LocaleCacheKey
<DateFmtBestPattern
>(other
),
80 fSkeleton(other
.fSkeleton
) { }
81 virtual ~DateFmtBestPatternKey();
82 virtual int32_t hashCode() const {
83 return (int32_t)(37u * (uint32_t)LocaleCacheKey
<DateFmtBestPattern
>::hashCode() + (uint32_t)fSkeleton
.hashCode());
85 virtual UBool
operator==(const CacheKeyBase
&other
) const {
90 if (!LocaleCacheKey
<DateFmtBestPattern
>::operator==(other
)) {
93 // We know that this and other are of same class if we get this far.
94 const DateFmtBestPatternKey
&realOther
=
95 static_cast<const DateFmtBestPatternKey
&>(other
);
96 return (realOther
.fSkeleton
== fSkeleton
);
98 virtual CacheKeyBase
*clone() const {
99 return new DateFmtBestPatternKey(*this);
101 virtual const DateFmtBestPattern
*createObject(
102 const void * /*unused*/, UErrorCode
&status
) const {
103 LocalPointer
<DateTimePatternGenerator
> dtpg(
104 DateTimePatternGenerator::createInstance(fLoc
, status
));
105 if (U_FAILURE(status
)) {
109 LocalPointer
<DateFmtBestPattern
> pattern(
110 new DateFmtBestPattern(
111 dtpg
->getBestPattern(fSkeleton
, status
)),
113 if (U_FAILURE(status
)) {
116 DateFmtBestPattern
*result
= pattern
.orphan();
122 DateFmtBestPatternKey::~DateFmtBestPatternKey() { }
125 DateFormat::DateFormat()
128 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
132 //----------------------------------------------------------------------
134 DateFormat::DateFormat(const DateFormat
& other
)
138 fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE
)
143 //----------------------------------------------------------------------
145 DateFormat
& DateFormat::operator=(const DateFormat
& other
)
150 delete fNumberFormat
;
151 if(other
.fCalendar
) {
152 fCalendar
= other
.fCalendar
->clone();
156 if(other
.fNumberFormat
) {
157 fNumberFormat
= (NumberFormat
*)other
.fNumberFormat
->clone();
159 fNumberFormat
= NULL
;
161 fBoolFlags
= other
.fBoolFlags
;
162 fCapitalizationContext
= other
.fCapitalizationContext
;
167 //----------------------------------------------------------------------
169 DateFormat::~DateFormat()
172 delete fNumberFormat
;
175 //----------------------------------------------------------------------
178 DateFormat::operator==(const Format
& other
) const
180 // This protected comparison operator should only be called by subclasses
181 // which have confirmed that the other object being compared against is
182 // an instance of a sublcass of DateFormat. THIS IS IMPORTANT.
184 // Format::operator== guarantees that this cast is safe
185 DateFormat
* fmt
= (DateFormat
*)&other
;
187 return (this == fmt
) ||
188 (Format::operator==(other
) &&
189 fCalendar
&&(fCalendar
->isEquivalentTo(*fmt
->fCalendar
)) &&
190 (fNumberFormat
&& *fNumberFormat
== *fmt
->fNumberFormat
) &&
191 (fCapitalizationContext
== fmt
->fCapitalizationContext
) );
194 //----------------------------------------------------------------------
197 DateFormat::format(const Formattable
& obj
,
198 UnicodeString
& appendTo
,
199 FieldPosition
& fieldPosition
,
200 UErrorCode
& status
) const
202 if (U_FAILURE(status
)) return appendTo
;
204 // if the type of the Formattable is double or long, treat it as if it were a Date
206 switch (obj
.getType())
208 case Formattable::kDate
:
209 date
= obj
.getDate();
211 case Formattable::kDouble
:
212 date
= (UDate
)obj
.getDouble();
214 case Formattable::kLong
:
215 date
= (UDate
)obj
.getLong();
218 status
= U_ILLEGAL_ARGUMENT_ERROR
;
223 //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex())
224 // status = U_ILLEGAL_ARGUMENT_ERROR;
226 return format(date
, appendTo
, fieldPosition
);
229 //----------------------------------------------------------------------
232 DateFormat::format(const Formattable
& obj
,
233 UnicodeString
& appendTo
,
234 FieldPositionIterator
* posIter
,
235 UErrorCode
& status
) const
237 if (U_FAILURE(status
)) return appendTo
;
239 // if the type of the Formattable is double or long, treat it as if it were a Date
241 switch (obj
.getType())
243 case Formattable::kDate
:
244 date
= obj
.getDate();
246 case Formattable::kDouble
:
247 date
= (UDate
)obj
.getDouble();
249 case Formattable::kLong
:
250 date
= (UDate
)obj
.getLong();
253 status
= U_ILLEGAL_ARGUMENT_ERROR
;
258 //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex())
259 // status = U_ILLEGAL_ARGUMENT_ERROR;
261 return format(date
, appendTo
, posIter
, status
);
264 //----------------------------------------------------------------------
266 // Default implementation for backwards compatibility, subclasses should implement.
268 DateFormat::format(Calendar
& /* unused cal */,
269 UnicodeString
& appendTo
,
270 FieldPositionIterator
* /* unused posIter */,
271 UErrorCode
& status
) const {
272 if (U_SUCCESS(status
)) {
273 status
= U_UNSUPPORTED_ERROR
;
278 //----------------------------------------------------------------------
281 DateFormat::format(UDate date
, UnicodeString
& appendTo
, FieldPosition
& fieldPosition
) const {
282 if (fCalendar
!= NULL
) {
283 // Use a clone of our calendar instance
284 Calendar
* calClone
= fCalendar
->clone();
285 if (calClone
!= NULL
) {
286 UErrorCode ec
= U_ZERO_ERROR
;
287 calClone
->setTime(date
, ec
);
289 format(*calClone
, appendTo
, fieldPosition
);
297 //----------------------------------------------------------------------
300 DateFormat::format(UDate date
, UnicodeString
& appendTo
, FieldPositionIterator
* posIter
,
301 UErrorCode
& status
) const {
302 if (fCalendar
!= NULL
) {
303 Calendar
* calClone
= fCalendar
->clone();
304 if (calClone
!= NULL
) {
305 calClone
->setTime(date
, status
);
306 if (U_SUCCESS(status
)) {
307 format(*calClone
, appendTo
, posIter
, status
);
315 //----------------------------------------------------------------------
318 DateFormat::format(UDate date
, UnicodeString
& appendTo
) const
320 // Note that any error information is just lost. That's okay
321 // for this convenience method.
322 FieldPosition
fpos(FieldPosition::DONT_CARE
);
323 return format(date
, appendTo
, fpos
);
326 //----------------------------------------------------------------------
329 DateFormat::parse(const UnicodeString
& text
,
330 ParsePosition
& pos
) const
332 UDate d
= 0; // Error return UDate is 0 (the epoch)
333 if (fCalendar
!= NULL
) {
334 Calendar
* calClone
= fCalendar
->clone();
335 if (calClone
!= NULL
) {
336 int32_t start
= pos
.getIndex();
338 parse(text
, *calClone
, pos
);
339 if (pos
.getIndex() != start
) {
340 UErrorCode ec
= U_ZERO_ERROR
;
341 d
= calClone
->getTime(ec
);
343 // We arrive here if fCalendar => calClone is non-lenient and
344 // there is an out-of-range field. We don't know which field
345 // was illegal so we set the error index to the start.
347 pos
.setErrorIndex(start
);
357 //----------------------------------------------------------------------
360 DateFormat::parse(const UnicodeString
& text
,
361 UErrorCode
& status
) const
363 if (U_FAILURE(status
)) return 0;
365 ParsePosition
pos(0);
366 UDate result
= parse(text
, pos
);
367 if (pos
.getIndex() == 0) {
368 #if defined (U_DEBUG_CAL)
369 fprintf(stderr
, "%s:%d - - failed to parse - err index %d\n"
370 , __FILE__
, __LINE__
, pos
.getErrorIndex() );
372 status
= U_ILLEGAL_ARGUMENT_ERROR
;
377 //----------------------------------------------------------------------
380 DateFormat::parseObject(const UnicodeString
& source
,
382 ParsePosition
& pos
) const
384 result
.setDate(parse(source
, pos
));
387 //----------------------------------------------------------------------
389 DateFormat
* U_EXPORT2
390 DateFormat::createTimeInstance(DateFormat::EStyle style
,
391 const Locale
& aLocale
)
393 return createDateTimeInstance(kNone
, style
, aLocale
);
396 //----------------------------------------------------------------------
398 DateFormat
* U_EXPORT2
399 DateFormat::createDateInstance(DateFormat::EStyle style
,
400 const Locale
& aLocale
)
402 return createDateTimeInstance(style
, kNone
, aLocale
);
405 //----------------------------------------------------------------------
407 DateFormat
* U_EXPORT2
408 DateFormat::createDateTimeInstance(EStyle dateStyle
,
410 const Locale
& aLocale
)
412 if(dateStyle
!= kNone
)
414 dateStyle
= (EStyle
) (dateStyle
+ kDateOffset
);
416 return create(timeStyle
, dateStyle
, aLocale
);
419 //----------------------------------------------------------------------
421 DateFormat
* U_EXPORT2
422 DateFormat::createInstance()
424 return createDateTimeInstance(kShort
, kShort
, Locale::getDefault());
427 //----------------------------------------------------------------------
429 UnicodeString U_EXPORT2
430 DateFormat::getBestPattern(
431 const Locale
&locale
,
432 const UnicodeString
&skeleton
,
433 UErrorCode
&status
) {
434 UnifiedCache
*cache
= UnifiedCache::getInstance(status
);
435 if (U_FAILURE(status
)) {
436 return UnicodeString();
438 DateFmtBestPatternKey
key(locale
, skeleton
, status
);
439 const DateFmtBestPattern
*patternPtr
= NULL
;
440 cache
->get(key
, patternPtr
, status
);
441 if (U_FAILURE(status
)) {
442 return UnicodeString();
444 UnicodeString
result(patternPtr
->fPattern
);
445 patternPtr
->removeRef();
449 DateFormat
* U_EXPORT2
450 DateFormat::createInstanceForSkeleton(
451 Calendar
*calendarToAdopt
,
452 const UnicodeString
& skeleton
,
453 const Locale
&locale
,
454 UErrorCode
&status
) {
455 LocalPointer
<Calendar
> calendar(calendarToAdopt
);
456 if (U_FAILURE(status
)) {
459 if (calendar
.isNull()) {
460 status
= U_ILLEGAL_ARGUMENT_ERROR
;
463 Locale localeWithCalendar
= locale
;
464 localeWithCalendar
.setKeywordValue("calendar", calendar
->getType(), status
);
465 if (U_FAILURE(status
)) {
468 DateFormat
*result
= createInstanceForSkeleton(skeleton
, localeWithCalendar
, status
);
469 if (U_FAILURE(status
)) {
472 result
->adoptCalendar(calendar
.orphan());
476 DateFormat
* U_EXPORT2
477 DateFormat::createInstanceForSkeleton(
478 const UnicodeString
& skeleton
,
479 const Locale
&locale
,
480 UErrorCode
&status
) {
481 if (U_FAILURE(status
)) {
484 LocalPointer
<DateFormat
> df(
485 new SimpleDateFormat(
486 getBestPattern(locale
, skeleton
, status
),
489 return U_SUCCESS(status
) ? df
.orphan() : NULL
;
492 DateFormat
* U_EXPORT2
493 DateFormat::createInstanceForSkeleton(
494 const UnicodeString
& skeleton
,
495 UErrorCode
&status
) {
496 return createInstanceForSkeleton(
497 skeleton
, Locale::getDefault(), status
);
500 //----------------------------------------------------------------------
502 DateFormat
* U_EXPORT2
503 DateFormat::create(EStyle timeStyle
, EStyle dateStyle
, const Locale
& locale
)
505 UErrorCode status
= U_ZERO_ERROR
;
506 #if U_PLATFORM_USES_ONLY_WIN32_API
508 int32_t count
= locale
.getKeywordValue("compat", buffer
, sizeof(buffer
), status
);
510 // if the locale has "@compat=host", create a host-specific DateFormat...
511 if (count
> 0 && uprv_strcmp(buffer
, "host") == 0) {
512 Win32DateFormat
*f
= new Win32DateFormat(timeStyle
, dateStyle
, locale
, status
);
514 if (U_SUCCESS(status
)) {
523 if(/*((timeStyle!=UDAT_NONE)&&(timeStyle & UDAT_RELATIVE)) || */((dateStyle
!=kNone
)&&((dateStyle
-kDateOffset
) & UDAT_RELATIVE
))) {
524 RelativeDateFormat
*r
= new RelativeDateFormat((UDateFormatStyle
)timeStyle
, (UDateFormatStyle
)(dateStyle
-kDateOffset
), locale
, status
);
525 if(U_SUCCESS(status
)) return r
;
527 status
= U_ZERO_ERROR
;
530 // Try to create a SimpleDateFormat of the desired style.
531 SimpleDateFormat
*f
= new SimpleDateFormat(timeStyle
, dateStyle
, locale
, status
);
532 if (U_SUCCESS(status
)) return f
;
535 // If that fails, try to create a format using the default pattern and
536 // the DateFormatSymbols for this locale.
537 status
= U_ZERO_ERROR
;
538 f
= new SimpleDateFormat(locale
, status
);
539 if (U_SUCCESS(status
)) return f
;
542 // This should never really happen, because the preceding constructor
543 // should always succeed. If the resource data is unavailable, a last
544 // resort object should be returned.
548 //----------------------------------------------------------------------
550 const Locale
* U_EXPORT2
551 DateFormat::getAvailableLocales(int32_t& count
)
553 // Get the list of installed locales.
554 // Even if root has the correct date format for this locale,
555 // it's still a valid locale (we don't worry about data fallbacks).
556 return Locale::getAvailableLocales(count
);
559 //----------------------------------------------------------------------
562 DateFormat::adoptCalendar(Calendar
* newCalendar
)
565 fCalendar
= newCalendar
;
568 //----------------------------------------------------------------------
570 DateFormat::setCalendar(const Calendar
& newCalendar
)
572 if (fCalendar
&& fCalendar
->isEquivalentTo( newCalendar
))
574 Calendar
* newCalClone
= newCalendar
.clone();
575 if (newCalClone
!= NULL
) {
576 adoptCalendar(newCalClone
);
580 //----------------------------------------------------------------------
583 DateFormat::getCalendar() const
588 //----------------------------------------------------------------------
591 DateFormat::adoptNumberFormat(NumberFormat
* newNumberFormat
)
593 delete fNumberFormat
;
594 fNumberFormat
= newNumberFormat
;
595 newNumberFormat
->setParseIntegerOnly(TRUE
);
596 newNumberFormat
->setGroupingUsed(FALSE
);
598 //----------------------------------------------------------------------
601 DateFormat::setNumberFormat(const NumberFormat
& newNumberFormat
)
603 NumberFormat
* newNumFmtClone
= (NumberFormat
*)newNumberFormat
.clone();
604 if (newNumFmtClone
!= NULL
) {
605 adoptNumberFormat(newNumFmtClone
);
609 //----------------------------------------------------------------------
612 DateFormat::getNumberFormat() const
614 return fNumberFormat
;
617 //----------------------------------------------------------------------
620 DateFormat::adoptTimeZone(TimeZone
* zone
)
622 if (fCalendar
!= NULL
) {
623 fCalendar
->adoptTimeZone(zone
);
626 //----------------------------------------------------------------------
629 DateFormat::setTimeZone(const TimeZone
& zone
)
631 if (fCalendar
!= NULL
) {
632 fCalendar
->setTimeZone(zone
);
636 //----------------------------------------------------------------------
639 DateFormat::getTimeZone() const
641 if (fCalendar
!= NULL
) {
642 return fCalendar
->getTimeZone();
644 // If calendar doesn't exists, create default timezone.
645 // fCalendar is rarely null
646 return *(TimeZone::createDefault());
649 //----------------------------------------------------------------------
652 DateFormat::setLenient(UBool lenient
)
654 if (fCalendar
!= NULL
) {
655 fCalendar
->setLenient(lenient
);
657 UErrorCode status
= U_ZERO_ERROR
;
658 setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, lenient
, status
);
659 setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, lenient
, status
);
662 //----------------------------------------------------------------------
665 DateFormat::isLenient() const
667 UBool lenient
= TRUE
;
668 if (fCalendar
!= NULL
) {
669 lenient
= fCalendar
->isLenient();
671 UErrorCode status
= U_ZERO_ERROR
;
673 && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE
, status
)
674 && getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC
, status
);
678 DateFormat::setCalendarLenient(UBool lenient
)
680 if (fCalendar
!= NULL
) {
681 fCalendar
->setLenient(lenient
);
685 //----------------------------------------------------------------------
688 DateFormat::isCalendarLenient() const
690 if (fCalendar
!= NULL
) {
691 return fCalendar
->isLenient();
693 // fCalendar is rarely null
698 //----------------------------------------------------------------------
701 void DateFormat::setContext(UDisplayContext value
, UErrorCode
& status
)
703 if (U_FAILURE(status
))
705 if ( (UDisplayContextType
)((uint32_t)value
>> 8) == UDISPCTX_TYPE_CAPITALIZATION
) {
706 fCapitalizationContext
= value
;
708 status
= U_ILLEGAL_ARGUMENT_ERROR
;
713 //----------------------------------------------------------------------
716 UDisplayContext
DateFormat::getContext(UDisplayContextType type
, UErrorCode
& status
) const
718 if (U_FAILURE(status
))
719 return (UDisplayContext
)0;
720 if (type
!= UDISPCTX_TYPE_CAPITALIZATION
) {
721 status
= U_ILLEGAL_ARGUMENT_ERROR
;
722 return (UDisplayContext
)0;
724 return fCapitalizationContext
;
728 //----------------------------------------------------------------------
732 DateFormat::setBooleanAttribute(UDateFormatBooleanAttribute attr
,
734 UErrorCode
&status
) {
735 if(!fBoolFlags
.isValidValue(newValue
)) {
736 status
= U_ILLEGAL_ARGUMENT_ERROR
;
738 fBoolFlags
.set(attr
, newValue
);
744 //----------------------------------------------------------------------
747 DateFormat::getBooleanAttribute(UDateFormatBooleanAttribute attr
, UErrorCode
&/*status*/) const {
749 return static_cast<UBool
>(fBoolFlags
.get(attr
));
754 #endif /* #if !UCONFIG_NO_FORMATTING */