1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/***********************************************************************
4 * Copyright (c) 1997-2009, International Business Machines Corporation
5 * and others. All Rights Reserved.
6 ***********************************************************************/
7
8#include "unicode/utypes.h"
9
10#if !UCONFIG_NO_FORMATTING
11
12#include "unicode/datefmt.h"
13#include "unicode/smpdtfmt.h"
14#include "tsdate.h"
15#include "putilimp.h"
16#include "cstring.h"
17
18#include <float.h>
19#include <stdlib.h>
20#include <math.h>
21
22const double IntlTestDateFormat::ONEYEAR = 365.25 * ONEDAY; // Approximate
23
24IntlTestDateFormat::~IntlTestDateFormat() {}
25
26/**
27 * This test does round-trip testing (format -> parse -> format -> parse -> etc.) of
28 * DateFormat.
29 */
30// par is ignored throughout this file
31void IntlTestDateFormat::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
32{
33    if (exec) logln("TestSuite DateFormat");
34    switch (index) {
35        case 0: name = "GenericTest";
36            if (exec) {
37                logln(name);
38                fFormat = DateFormat::createInstance();
39                fTestName = "createInstance";
40                fLimit = 3;
41                testFormat(/* par */);
42            }
43            break;
44        case 1: name = "DefaultLocale";
45            if (exec) {
46                logln(name);
47                testLocale(/*par, */Locale::getDefault(), "Default Locale");
48            }
49            break;
50
51        case 2: name = "TestAvailableLocales";
52            if (exec) {
53                logln(name);
54                testAvailableLocales(/* par */);
55            }
56            break;
57
58        case 3: name = "MonsterTest";
59            if (exec) {
60                logln(name);
61                monsterTest(/*par*/);
62            }
63            break;
64
65        default: name = ""; break;
66    }
67}
68
69void
70IntlTestDateFormat::testLocale(/*char* par, */const Locale& locale, const UnicodeString& localeName)
71{
72    DateFormat::EStyle timeStyle, dateStyle;
73
74    // For patterns including only time information and a timezone, it may take
75    // up to three iterations, since the timezone may shift as the year number
76    // is determined.  For other patterns, 2 iterations should suffice.
77    fLimit = 3;
78
79    for(timeStyle = (DateFormat::EStyle)0;
80        timeStyle < (DateFormat::EStyle)4;
81        timeStyle = (DateFormat::EStyle) (timeStyle+1))
82    {
83        fTestName = (UnicodeString) "Time test " + (int32_t) timeStyle + " (" + localeName + ")";
84        fFormat = DateFormat::createTimeInstance(timeStyle, locale);
85        testFormat(/* par */);
86    }
87
88    fLimit = 2;
89
90    for(dateStyle = (DateFormat::EStyle)0;
91        dateStyle < (DateFormat::EStyle)4;
92        dateStyle = (DateFormat::EStyle) (dateStyle+1))
93    {
94        fTestName = (UnicodeString) "Date test " + (int32_t) dateStyle + " (" + localeName + ")";
95        fFormat = DateFormat::createDateInstance(dateStyle, locale);
96        testFormat(/* par */);
97    }
98
99    for(dateStyle = (DateFormat::EStyle)0;
100        dateStyle < (DateFormat::EStyle)4;
101        dateStyle = (DateFormat::EStyle) (dateStyle+1))
102    {
103        for(timeStyle = (DateFormat::EStyle)0;
104            timeStyle < (DateFormat::EStyle)4;
105            timeStyle = (DateFormat::EStyle) (timeStyle+1))
106        {
107            fTestName = (UnicodeString) "DateTime test " + (int32_t) dateStyle + "/" + (int32_t) timeStyle + " (" + localeName + ")";
108            fFormat = DateFormat::createDateTimeInstance(dateStyle, timeStyle, locale);
109            testFormat(/* par */);
110        }
111    }
112}
113
114void IntlTestDateFormat::testFormat(/* char* par */)
115{
116    if (fFormat == 0)
117    {
118        dataerrln("FAIL: DateFormat creation failed");
119        return;
120    }
121
122    describeTest();
123
124    UDate now = Calendar::getNow();
125    tryDate(0);
126    tryDate(1278161801778.0);
127    tryDate(5264498352317.0);   // Sunday, October 28, 2136 8:39:12 AM PST
128    tryDate(9516987689250.0);   // In the year 2271
129    tryDate(now);
130    // Shift 6 months into the future, AT THE SAME TIME OF DAY.
131    // This will test the DST handling.
132    tryDate(now + 6.0*30*ONEDAY);
133
134    UDate limit = now * 10; // Arbitrary limit
135    for (int32_t i=0; i<3; ++i)
136        tryDate(uprv_floor(randDouble() * limit));
137
138    delete fFormat;
139}
140
141void
142IntlTestDateFormat::describeTest()
143{
144    // Assume it's a SimpleDateFormat and get some info
145    SimpleDateFormat *s = (SimpleDateFormat*)fFormat;
146    UnicodeString str;
147    logln(fTestName + " Pattern " + s->toPattern(str));
148}
149
150void IntlTestDateFormat::tryDate(UDate theDate)
151{
152    const int32_t DEPTH = 10;
153    UDate date[DEPTH];
154    UnicodeString string[DEPTH];
155
156    int32_t dateMatch = 0;
157    int32_t stringMatch = 0;
158    UBool dump = FALSE;
159#if defined (U_CAL_DEBUG)
160    dump = TRUE;
161#endif
162    int32_t i;
163
164    date[0] = theDate;
165    fFormat->format(theDate, string[0]);
166
167    for (i=1; i<DEPTH; ++i)
168    {
169        UErrorCode status = U_ZERO_ERROR;
170        date[i] = fFormat->parse(string[i-1], status);
171        if (U_FAILURE(status))
172        {
173            describeTest();
174            errln("**** FAIL: Parse of " + prettify(string[i-1], FALSE) + " failed.");
175            dump = TRUE;
176            break;
177        }
178        fFormat->format(date[i], string[i]);
179        if (dateMatch == 0 && date[i] == date[i-1])
180            dateMatch = i;
181        else if (dateMatch > 0 && date[i] != date[i-1])
182        {
183            describeTest();
184            errln("**** FAIL: Date mismatch after match for " + string[i]);
185            dump = TRUE;
186            break;
187        }
188        if (stringMatch == 0 && string[i] == string[i-1])
189            stringMatch = i;
190        else if (stringMatch > 0 && string[i] != string[i-1])
191        {
192            describeTest();
193            errln("**** FAIL: String mismatch after match for " + string[i]);
194            dump = TRUE;
195            break;
196        }
197        if (dateMatch > 0 && stringMatch > 0)
198            break;
199    }
200    if (i == DEPTH)
201        --i;
202
203    if (stringMatch > fLimit || dateMatch > fLimit)
204    {
205        describeTest();
206        errln((UnicodeString)"**** FAIL: No string and/or date match within " + fLimit
207            + " iterations for the Date " + string[0] + "\t(" + theDate + ").");
208        dump = TRUE;
209    }
210
211    if (dump)
212    {
213        for (int32_t k=0; k<=i; ++k)
214        {
215            logln((UnicodeString)"" + k + ": " + date[k] + " F> " +
216                  string[k] + " P> ");
217        }
218    }
219}
220
221// Return a random double from 0.01 to 1, inclusive
222double IntlTestDateFormat::randDouble()
223{
224    // Assume 8-bit (or larger) rand values.  Also assume
225    // that the system rand() function is very poor, which it always is.
226    double d=0.0;
227    uint32_t i;
228    char* poke = (char*)&d;
229    do {
230        do {
231            for (i=0; i < sizeof(double); ++i)
232            {
233                poke[i] = (char)(rand() & 0xFF);
234            }
235        } while (uprv_isNaN(d) || uprv_isInfinite(d));
236
237        if (d < 0.0)
238            d = -d;
239        if (d > 0.0)
240        {
241            double e = uprv_floor(log10(d));
242            if (e < -2.0)
243                d *= uprv_pow10((int32_t)(-e-2));
244            else if (e > -1.0)
245                d /= uprv_pow10((int32_t)(e+1));
246        }
247    // While this is not a real normalized number make another one.
248    } while (uprv_isNaN(d) || uprv_isInfinite(d)
249        || !((-DBL_MAX < d && d < DBL_MAX) || (d < -DBL_MIN && DBL_MIN < d)));
250    return d;
251}
252
253void IntlTestDateFormat::testAvailableLocales(/* char* par */)
254{
255    int32_t count = 0;
256    const Locale* locales = DateFormat::getAvailableLocales(count);
257    logln((UnicodeString)"" + count + " available locales");
258    if (locales && count)
259    {
260        UnicodeString name;
261        UnicodeString all;
262        for (int32_t i=0; i<count; ++i)
263        {
264            if (i!=0) all += ", ";
265            all += locales[i].getName();
266        }
267        logln(all);
268    }
269    else dataerrln((UnicodeString)"**** FAIL: Zero available locales or null array pointer");
270}
271
272void IntlTestDateFormat::monsterTest(/*char *par*/)
273{
274    int32_t count;
275    const Locale* locales = DateFormat::getAvailableLocales(count);
276    if (locales && count)
277    {
278        if (quick && count > 3) {
279            logln("quick test: testing just 3 locales!");
280            count = 3;
281        }
282        for (int32_t i=0; i<count; ++i)
283        {
284            UnicodeString name = UnicodeString(locales[i].getName(), "");
285            logln((UnicodeString)"Testing " + name + "...");
286            testLocale(/*par, */locales[i], name);
287        }
288    }
289}
290
291#endif /* #if !UCONFIG_NO_FORMATTING */
292