1/*
2 *******************************************************************************
3 * Copyright (C) 1997-2014, International Business Machines Corporation and    *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 *
7 * File DATEFMT.CPP
8 *
9 * Modification History:
10 *
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 ********************************************************************************
18 */
19
20#include "unicode/utypes.h"
21
22#if !UCONFIG_NO_FORMATTING
23
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"
29#include "reldtfmt.h"
30
31#include "cstring.h"
32#include "windtfmt.h"
33
34#if defined( U_DEBUG_CALSVC ) || defined (U_DEBUG_CAL)
35#include <stdio.h>
36#endif
37
38// *****************************************************************************
39// class DateFormat
40// *****************************************************************************
41
42U_NAMESPACE_BEGIN
43
44DateFormat::DateFormat()
45:   fCalendar(0),
46    fNumberFormat(0),
47    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
48{
49}
50
51//----------------------------------------------------------------------
52
53DateFormat::DateFormat(const DateFormat& other)
54:   Format(other),
55    fCalendar(0),
56    fNumberFormat(0),
57    fCapitalizationContext(UDISPCTX_CAPITALIZATION_NONE)
58{
59    *this = other;
60}
61
62//----------------------------------------------------------------------
63
64DateFormat& DateFormat::operator=(const DateFormat& other)
65{
66    if (this != &other)
67    {
68        delete fCalendar;
69        delete fNumberFormat;
70        if(other.fCalendar) {
71          fCalendar = other.fCalendar->clone();
72        } else {
73          fCalendar = NULL;
74        }
75        if(other.fNumberFormat) {
76          fNumberFormat = (NumberFormat*)other.fNumberFormat->clone();
77        } else {
78          fNumberFormat = NULL;
79        }
80        fBoolFlags = other.fBoolFlags;
81        fCapitalizationContext = other.fCapitalizationContext;
82    }
83    return *this;
84}
85
86//----------------------------------------------------------------------
87
88DateFormat::~DateFormat()
89{
90    delete fCalendar;
91    delete fNumberFormat;
92}
93
94//----------------------------------------------------------------------
95
96UBool
97DateFormat::operator==(const Format& other) const
98{
99    // This protected comparison operator should only be called by subclasses
100    // which have confirmed that the other object being compared against is
101    // an instance of a sublcass of DateFormat.  THIS IS IMPORTANT.
102
103    // Format::operator== guarantees that this cast is safe
104    DateFormat* fmt = (DateFormat*)&other;
105
106    return (this == fmt) ||
107        (Format::operator==(other) &&
108         fCalendar&&(fCalendar->isEquivalentTo(*fmt->fCalendar)) &&
109         (fNumberFormat && *fNumberFormat == *fmt->fNumberFormat) &&
110         (fCapitalizationContext == fmt->fCapitalizationContext) );
111}
112
113//----------------------------------------------------------------------
114
115UnicodeString&
116DateFormat::format(const Formattable& obj,
117                   UnicodeString& appendTo,
118                   FieldPosition& fieldPosition,
119                   UErrorCode& status) const
120{
121    if (U_FAILURE(status)) return appendTo;
122
123    // if the type of the Formattable is double or long, treat it as if it were a Date
124    UDate date = 0;
125    switch (obj.getType())
126    {
127    case Formattable::kDate:
128        date = obj.getDate();
129        break;
130    case Formattable::kDouble:
131        date = (UDate)obj.getDouble();
132        break;
133    case Formattable::kLong:
134        date = (UDate)obj.getLong();
135        break;
136    default:
137        status = U_ILLEGAL_ARGUMENT_ERROR;
138        return appendTo;
139    }
140
141    // Is this right?
142    //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex())
143    //  status = U_ILLEGAL_ARGUMENT_ERROR;
144
145    return format(date, appendTo, fieldPosition);
146}
147
148//----------------------------------------------------------------------
149
150UnicodeString&
151DateFormat::format(const Formattable& obj,
152                   UnicodeString& appendTo,
153                   FieldPositionIterator* posIter,
154                   UErrorCode& status) const
155{
156    if (U_FAILURE(status)) return appendTo;
157
158    // if the type of the Formattable is double or long, treat it as if it were a Date
159    UDate date = 0;
160    switch (obj.getType())
161    {
162    case Formattable::kDate:
163        date = obj.getDate();
164        break;
165    case Formattable::kDouble:
166        date = (UDate)obj.getDouble();
167        break;
168    case Formattable::kLong:
169        date = (UDate)obj.getLong();
170        break;
171    default:
172        status = U_ILLEGAL_ARGUMENT_ERROR;
173        return appendTo;
174    }
175
176    // Is this right?
177    //if (fieldPosition.getBeginIndex() == fieldPosition.getEndIndex())
178    //  status = U_ILLEGAL_ARGUMENT_ERROR;
179
180    return format(date, appendTo, posIter, status);
181}
182
183//----------------------------------------------------------------------
184
185// Default implementation for backwards compatibility, subclasses should implement.
186UnicodeString&
187DateFormat::format(Calendar& /* unused cal */,
188                   UnicodeString& appendTo,
189                   FieldPositionIterator* /* unused posIter */,
190                   UErrorCode& status) const {
191    if (U_SUCCESS(status)) {
192        status = U_UNSUPPORTED_ERROR;
193    }
194    return appendTo;
195}
196
197//----------------------------------------------------------------------
198
199UnicodeString&
200DateFormat::format(UDate date, UnicodeString& appendTo, FieldPosition& fieldPosition) const {
201    if (fCalendar != NULL) {
202        // Use a clone of our calendar instance
203        Calendar* calClone = fCalendar->clone();
204        if (calClone != NULL) {
205            UErrorCode ec = U_ZERO_ERROR;
206            calClone->setTime(date, ec);
207            if (U_SUCCESS(ec)) {
208                format(*calClone, appendTo, fieldPosition);
209            }
210            delete calClone;
211        }
212    }
213    return appendTo;
214}
215
216//----------------------------------------------------------------------
217
218UnicodeString&
219DateFormat::format(UDate date, UnicodeString& appendTo, FieldPositionIterator* posIter,
220                   UErrorCode& status) const {
221    if (fCalendar != NULL) {
222        Calendar* calClone = fCalendar->clone();
223        if (calClone != NULL) {
224            calClone->setTime(date, status);
225            if (U_SUCCESS(status)) {
226               format(*calClone, appendTo, posIter, status);
227            }
228            delete calClone;
229        }
230    }
231    return appendTo;
232}
233
234//----------------------------------------------------------------------
235
236UnicodeString&
237DateFormat::format(UDate date, UnicodeString& appendTo) const
238{
239    // Note that any error information is just lost.  That's okay
240    // for this convenience method.
241    FieldPosition fpos(0);
242    return format(date, appendTo, fpos);
243}
244
245//----------------------------------------------------------------------
246
247UDate
248DateFormat::parse(const UnicodeString& text,
249                  ParsePosition& pos) const
250{
251    UDate d = 0; // Error return UDate is 0 (the epoch)
252    if (fCalendar != NULL) {
253        Calendar* calClone = fCalendar->clone();
254        if (calClone != NULL) {
255            int32_t start = pos.getIndex();
256            calClone->clear();
257            parse(text, *calClone, pos);
258            if (pos.getIndex() != start) {
259                UErrorCode ec = U_ZERO_ERROR;
260                d = calClone->getTime(ec);
261                if (U_FAILURE(ec)) {
262                    // We arrive here if fCalendar => calClone is non-lenient and
263                    // there is an out-of-range field.  We don't know which field
264                    // was illegal so we set the error index to the start.
265                    pos.setIndex(start);
266                    pos.setErrorIndex(start);
267                    d = 0;
268                }
269            }
270            delete calClone;
271        }
272    }
273    return d;
274}
275
276//----------------------------------------------------------------------
277
278UDate
279DateFormat::parse(const UnicodeString& text,
280                  UErrorCode& status) const
281{
282    if (U_FAILURE(status)) return 0;
283
284    ParsePosition pos(0);
285    UDate result = parse(text, pos);
286    if (pos.getIndex() == 0) {
287#if defined (U_DEBUG_CAL)
288      fprintf(stderr, "%s:%d - - failed to parse  - err index %d\n"
289              , __FILE__, __LINE__, pos.getErrorIndex() );
290#endif
291      status = U_ILLEGAL_ARGUMENT_ERROR;
292    }
293    return result;
294}
295
296//----------------------------------------------------------------------
297
298void
299DateFormat::parseObject(const UnicodeString& source,
300                        Formattable& result,
301                        ParsePosition& pos) const
302{
303    result.setDate(parse(source, pos));
304}
305
306//----------------------------------------------------------------------
307
308DateFormat* U_EXPORT2
309DateFormat::createTimeInstance(DateFormat::EStyle style,
310                               const Locale& aLocale)
311{
312    return create(style, kNone, aLocale);
313}
314
315//----------------------------------------------------------------------
316
317DateFormat* U_EXPORT2
318DateFormat::createDateInstance(DateFormat::EStyle style,
319                               const Locale& aLocale)
320{
321    // +4 to set the correct index for getting data out of
322    // LocaleElements.
323    if(style != kNone)
324    {
325        style = (EStyle) (style + kDateOffset);
326    }
327    return create(kNone, (EStyle) (style), aLocale);
328}
329
330//----------------------------------------------------------------------
331
332DateFormat* U_EXPORT2
333DateFormat::createDateTimeInstance(EStyle dateStyle,
334                                   EStyle timeStyle,
335                                   const Locale& aLocale)
336{
337    if(dateStyle != kNone)
338    {
339        dateStyle = (EStyle) (dateStyle + kDateOffset);
340    }
341    return create(timeStyle, dateStyle, aLocale);
342}
343
344//----------------------------------------------------------------------
345
346DateFormat* U_EXPORT2
347DateFormat::createInstance()
348{
349    return create(kShort, (EStyle) (kShort + kDateOffset), Locale::getDefault());
350}
351
352//----------------------------------------------------------------------
353
354DateFormat* U_EXPORT2
355DateFormat::create(EStyle timeStyle, EStyle dateStyle, const Locale& locale)
356{
357    UErrorCode status = U_ZERO_ERROR;
358#if U_PLATFORM_HAS_WIN32_API
359    char buffer[8];
360    int32_t count = locale.getKeywordValue("compat", buffer, sizeof(buffer), status);
361
362    // if the locale has "@compat=host", create a host-specific DateFormat...
363    if (count > 0 && uprv_strcmp(buffer, "host") == 0) {
364        Win32DateFormat *f = new Win32DateFormat(timeStyle, dateStyle, locale, status);
365
366        if (U_SUCCESS(status)) {
367            return f;
368        }
369
370        delete f;
371    }
372#endif
373
374    // is it relative?
375    if(/*((timeStyle!=UDAT_NONE)&&(timeStyle & UDAT_RELATIVE)) || */((dateStyle!=kNone)&&((dateStyle-kDateOffset) & UDAT_RELATIVE))) {
376        RelativeDateFormat *r = new RelativeDateFormat((UDateFormatStyle)timeStyle, (UDateFormatStyle)(dateStyle-kDateOffset), locale, status);
377        if(U_SUCCESS(status)) return r;
378        delete r;
379        status = U_ZERO_ERROR;
380    }
381
382    // Try to create a SimpleDateFormat of the desired style.
383    SimpleDateFormat *f = new SimpleDateFormat(timeStyle, dateStyle, locale, status);
384    if (U_SUCCESS(status)) return f;
385    delete f;
386
387    // If that fails, try to create a format using the default pattern and
388    // the DateFormatSymbols for this locale.
389    status = U_ZERO_ERROR;
390    f = new SimpleDateFormat(locale, status);
391    if (U_SUCCESS(status)) return f;
392    delete f;
393
394    // This should never really happen, because the preceding constructor
395    // should always succeed.  If the resource data is unavailable, a last
396    // resort object should be returned.
397    return 0;
398}
399
400//----------------------------------------------------------------------
401
402const Locale* U_EXPORT2
403DateFormat::getAvailableLocales(int32_t& count)
404{
405    // Get the list of installed locales.
406    // Even if root has the correct date format for this locale,
407    // it's still a valid locale (we don't worry about data fallbacks).
408    return Locale::getAvailableLocales(count);
409}
410
411//----------------------------------------------------------------------
412
413void
414DateFormat::adoptCalendar(Calendar* newCalendar)
415{
416    delete fCalendar;
417    fCalendar = newCalendar;
418}
419
420//----------------------------------------------------------------------
421void
422DateFormat::setCalendar(const Calendar& newCalendar)
423{
424    Calendar* newCalClone = newCalendar.clone();
425    if (newCalClone != NULL) {
426        adoptCalendar(newCalClone);
427    }
428}
429
430//----------------------------------------------------------------------
431
432const Calendar*
433DateFormat::getCalendar() const
434{
435    return fCalendar;
436}
437
438//----------------------------------------------------------------------
439
440void
441DateFormat::adoptNumberFormat(NumberFormat* newNumberFormat)
442{
443    delete fNumberFormat;
444    fNumberFormat = newNumberFormat;
445    newNumberFormat->setParseIntegerOnly(TRUE);
446}
447//----------------------------------------------------------------------
448
449void
450DateFormat::setNumberFormat(const NumberFormat& newNumberFormat)
451{
452    NumberFormat* newNumFmtClone = (NumberFormat*)newNumberFormat.clone();
453    if (newNumFmtClone != NULL) {
454        adoptNumberFormat(newNumFmtClone);
455    }
456}
457
458//----------------------------------------------------------------------
459
460const NumberFormat*
461DateFormat::getNumberFormat() const
462{
463    return fNumberFormat;
464}
465
466//----------------------------------------------------------------------
467
468void
469DateFormat::adoptTimeZone(TimeZone* zone)
470{
471    if (fCalendar != NULL) {
472        fCalendar->adoptTimeZone(zone);
473    }
474}
475//----------------------------------------------------------------------
476
477void
478DateFormat::setTimeZone(const TimeZone& zone)
479{
480    if (fCalendar != NULL) {
481        fCalendar->setTimeZone(zone);
482    }
483}
484
485//----------------------------------------------------------------------
486
487const TimeZone&
488DateFormat::getTimeZone() const
489{
490    if (fCalendar != NULL) {
491        return fCalendar->getTimeZone();
492    }
493    // If calendar doesn't exists, create default timezone.
494    // fCalendar is rarely null
495    return *(TimeZone::createDefault());
496}
497
498//----------------------------------------------------------------------
499
500void
501DateFormat::setLenient(UBool lenient)
502{
503    if (fCalendar != NULL) {
504        fCalendar->setLenient(lenient);
505    }
506    UErrorCode status = U_ZERO_ERROR;
507    setBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, lenient, status);
508    setBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, lenient, status);
509}
510
511//----------------------------------------------------------------------
512
513UBool
514DateFormat::isLenient() const
515{
516    UBool lenient = TRUE;
517    if (fCalendar != NULL) {
518        lenient = fCalendar->isLenient();
519    }
520    UErrorCode status = U_ZERO_ERROR;
521    return lenient
522        && getBooleanAttribute(UDAT_PARSE_ALLOW_WHITESPACE, status)
523        && getBooleanAttribute(UDAT_PARSE_ALLOW_NUMERIC, status);
524}
525
526void
527DateFormat::setCalendarLenient(UBool lenient)
528{
529    if (fCalendar != NULL) {
530        fCalendar->setLenient(lenient);
531    }
532}
533
534//----------------------------------------------------------------------
535
536UBool
537DateFormat::isCalendarLenient() const
538{
539    if (fCalendar != NULL) {
540        return fCalendar->isLenient();
541    }
542    // fCalendar is rarely null
543    return FALSE;
544}
545
546
547//----------------------------------------------------------------------
548
549
550void DateFormat::setContext(UDisplayContext value, UErrorCode& status)
551{
552    if (U_FAILURE(status))
553        return;
554    if ( (UDisplayContextType)((uint32_t)value >> 8) == UDISPCTX_TYPE_CAPITALIZATION ) {
555        fCapitalizationContext = value;
556    } else {
557        status = U_ILLEGAL_ARGUMENT_ERROR;
558   }
559}
560
561
562//----------------------------------------------------------------------
563
564
565UDisplayContext DateFormat::getContext(UDisplayContextType type, UErrorCode& status) const
566{
567    if (U_FAILURE(status))
568        return (UDisplayContext)0;
569    if (type != UDISPCTX_TYPE_CAPITALIZATION) {
570        status = U_ILLEGAL_ARGUMENT_ERROR;
571        return (UDisplayContext)0;
572    }
573    return fCapitalizationContext;
574}
575
576
577//----------------------------------------------------------------------
578
579
580DateFormat&
581DateFormat::setBooleanAttribute(UDateFormatBooleanAttribute attr,
582    									UBool newValue,
583    									UErrorCode &status) {
584    if(!fBoolFlags.isValidValue(newValue)) {
585        status = U_ILLEGAL_ARGUMENT_ERROR;
586    } else {
587        fBoolFlags.set(attr, newValue);
588    }
589
590    return *this;
591}
592
593//----------------------------------------------------------------------
594
595UBool
596DateFormat::getBooleanAttribute(UDateFormatBooleanAttribute attr, UErrorCode &/*status*/) const {
597
598    return fBoolFlags.get(attr);
599}
600
601U_NAMESPACE_END
602
603#endif /* #if !UCONFIG_NO_FORMATTING */
604
605//eof
606