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