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