1/*
2*******************************************************************************
3* Copyright (C) 2007-2011, International Business Machines Corporation and         *
4* others. All Rights Reserved.                                                *
5*******************************************************************************
6*/
7#include "unicode/utypes.h"
8
9#if !UCONFIG_NO_FORMATTING
10
11#include "tzoffloc.h"
12
13#include "unicode/ucal.h"
14#include "unicode/timezone.h"
15#include "unicode/calendar.h"
16#include "unicode/dtrule.h"
17#include "unicode/tzrule.h"
18#include "unicode/rbtz.h"
19#include "unicode/simpletz.h"
20#include "unicode/tzrule.h"
21#include "unicode/smpdtfmt.h"
22#include "unicode/gregocal.h"
23
24void
25TimeZoneOffsetLocalTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
26{
27    if (exec) {
28        logln("TestSuite TimeZoneOffsetLocalTest");
29    }
30    switch (index) {
31        TESTCASE(0, TestGetOffsetAroundTransition);
32        default: name = ""; break;
33    }
34}
35
36/*
37 * Testing getOffset APIs around rule transition by local standard/wall time.
38 */
39void
40TimeZoneOffsetLocalTest::TestGetOffsetAroundTransition() {
41    const int32_t NUM_DATES = 10;
42    const int32_t NUM_TIMEZONES = 3;
43
44    const int32_t HOUR = 60*60*1000;
45    const int32_t MINUTE = 60*1000;
46
47    const int32_t DATES[NUM_DATES][6] = {
48        {2006, UCAL_APRIL, 2, 1, 30, 1*HOUR+30*MINUTE},
49        {2006, UCAL_APRIL, 2, 2, 00, 2*HOUR},
50        {2006, UCAL_APRIL, 2, 2, 30, 2*HOUR+30*MINUTE},
51        {2006, UCAL_APRIL, 2, 3, 00, 3*HOUR},
52        {2006, UCAL_APRIL, 2, 3, 30, 3*HOUR+30*MINUTE},
53        {2006, UCAL_OCTOBER, 29, 0, 30, 0*HOUR+30*MINUTE},
54        {2006, UCAL_OCTOBER, 29, 1, 00, 1*HOUR},
55        {2006, UCAL_OCTOBER, 29, 1, 30, 1*HOUR+30*MINUTE},
56        {2006, UCAL_OCTOBER, 29, 2, 00, 2*HOUR},
57        {2006, UCAL_OCTOBER, 29, 2, 30, 2*HOUR+30*MINUTE},
58    };
59
60    // Expected offsets by int32_t getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
61    // uint8_t dayOfWeek, int32_t millis, UErrorCode& status)
62    const int32_t OFFSETS1[NUM_DATES] = {
63        // April 2, 2006
64        -8*HOUR,
65        -7*HOUR,
66        -7*HOUR,
67        -7*HOUR,
68        -7*HOUR,
69
70        // October 29, 2006
71        -7*HOUR,
72        -8*HOUR,
73        -8*HOUR,
74        -8*HOUR,
75        -8*HOUR,
76    };
77
78    // Expected offsets by void getOffset(UDate date, UBool local, int32_t& rawOffset,
79    // int32_t& dstOffset, UErrorCode& ec) with local=TRUE
80    // or void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
81    // int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) with
82    // nonExistingTimeOpt=kStandard/duplicatedTimeOpt=kStandard
83    const int32_t OFFSETS2[NUM_DATES][2] = {
84        // April 2, 2006
85        {-8*HOUR, 0},
86        {-8*HOUR, 0},
87        {-8*HOUR, 0},
88        {-8*HOUR, 1*HOUR},
89        {-8*HOUR, 1*HOUR},
90
91        // Oct 29, 2006
92        {-8*HOUR, 1*HOUR},
93        {-8*HOUR, 0},
94        {-8*HOUR, 0},
95        {-8*HOUR, 0},
96        {-8*HOUR, 0},
97    };
98
99    // Expected offsets by void getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt,
100    // int32_t duplicatedTimeOpt, int32_t& rawOffset, int32_t& dstOffset, UErrorCode& status) with
101    // nonExistingTimeOpt=kDaylight/duplicatedTimeOpt=kDaylight
102    const int32_t OFFSETS3[][2] = {
103        // April 2, 2006
104        {-8*HOUR, 0},
105        {-8*HOUR, 1*HOUR},
106        {-8*HOUR, 1*HOUR},
107        {-8*HOUR, 1*HOUR},
108        {-8*HOUR, 1*HOUR},
109
110        // October 29, 2006
111        {-8*HOUR, 1*HOUR},
112        {-8*HOUR, 1*HOUR},
113        {-8*HOUR, 1*HOUR},
114        {-8*HOUR, 0},
115        {-8*HOUR, 0},
116    };
117
118    UErrorCode status = U_ZERO_ERROR;
119
120    int32_t rawOffset, dstOffset;
121    TimeZone* utc = TimeZone::createTimeZone("UTC");
122    Calendar* cal = Calendar::createInstance(*utc, status);
123    if (U_FAILURE(status)) {
124        dataerrln("Calendar::createInstance failed: %s", u_errorName(status));
125        return;
126    }
127    cal->clear();
128
129    // Set up TimeZone objects - OlsonTimeZone, SimpleTimeZone and RuleBasedTimeZone
130    BasicTimeZone *TESTZONES[NUM_TIMEZONES];
131
132    TESTZONES[0] = (BasicTimeZone*)TimeZone::createTimeZone("America/Los_Angeles");
133    TESTZONES[1] = new SimpleTimeZone(-8*HOUR, "Simple Pacific Time",
134                                        UCAL_APRIL, 1, UCAL_SUNDAY, 2*HOUR,
135                                        UCAL_OCTOBER, -1, UCAL_SUNDAY, 2*HOUR, status);
136    if (U_FAILURE(status)) {
137        errln("SimpleTimeZone constructor failed");
138        return;
139    }
140
141    InitialTimeZoneRule *ir = new InitialTimeZoneRule(
142            "Pacific Standard Time", // Initial time Name
143            -8*HOUR,        // Raw offset
144            0*HOUR);        // DST saving amount
145
146    RuleBasedTimeZone *rbPT = new RuleBasedTimeZone("Rule based Pacific Time", ir);
147
148    DateTimeRule *dtr;
149    AnnualTimeZoneRule *atzr;
150    const int32_t STARTYEAR = 2000;
151
152    dtr = new DateTimeRule(UCAL_APRIL, 1, UCAL_SUNDAY,
153                        2*HOUR, DateTimeRule::WALL_TIME); // 1st Sunday in April, at 2AM wall time
154    atzr = new AnnualTimeZoneRule("Pacific Daylight Time",
155            -8*HOUR /* rawOffset */, 1*HOUR /* dstSavings */, dtr,
156            STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
157    rbPT->addTransitionRule(atzr, status);
158    if (U_FAILURE(status)) {
159        errln("Could not add DST start rule to the RuleBasedTimeZone rbPT");
160        return;
161    }
162
163    dtr = new DateTimeRule(UCAL_OCTOBER, -1, UCAL_SUNDAY,
164                        2*HOUR, DateTimeRule::WALL_TIME); // last Sunday in October, at 2AM wall time
165    atzr = new AnnualTimeZoneRule("Pacific Standard Time",
166            -8*HOUR /* rawOffset */, 0 /* dstSavings */, dtr,
167            STARTYEAR, AnnualTimeZoneRule::MAX_YEAR);
168    rbPT->addTransitionRule(atzr, status);
169    if (U_FAILURE(status)) {
170        errln("Could not add STD start rule to the RuleBasedTimeZone rbPT");
171        return;
172    }
173
174    rbPT->complete(status);
175    if (U_FAILURE(status)) {
176        errln("complete() failed for RuleBasedTimeZone rbPT");
177        return;
178    }
179
180    TESTZONES[2] = rbPT;
181
182    // Calculate millis
183    UDate MILLIS[NUM_DATES];
184    for (int32_t i = 0; i < NUM_DATES; i++) {
185        cal->clear();
186        cal->set(DATES[i][0], DATES[i][1], DATES[i][2], DATES[i][3], DATES[i][4]);
187        MILLIS[i] = cal->getTime(status);
188        if (U_FAILURE(status)) {
189            errln("cal->getTime failed");
190            return;
191        }
192    }
193
194    SimpleDateFormat df(UnicodeString("yyyy-MM-dd HH:mm:ss"), status);
195    if (U_FAILURE(status)) {
196        dataerrln("Failed to initialize a SimpleDateFormat - %s", u_errorName(status));
197    }
198    df.setTimeZone(*utc);
199    UnicodeString dateStr;
200
201    // Test getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
202    // uint8_t dayOfWeek, int32_t millis, UErrorCode& status)
203    for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
204        for (int32_t d = 0; d < NUM_DATES; d++) {
205            status = U_ZERO_ERROR;
206            int32_t offset = TESTZONES[i]->getOffset(GregorianCalendar::AD, DATES[d][0], DATES[d][1], DATES[d][2],
207                                                UCAL_SUNDAY, DATES[d][5], status);
208            if (U_FAILURE(status)) {
209                errln((UnicodeString)"getOffset(era,year,month,day,dayOfWeek,millis,status) failed for TESTZONES[" + i + "]");
210            } else if (offset != OFFSETS1[d]) {
211                dateStr.remove();
212                df.format(MILLIS[d], dateStr);
213                dataerrln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
214                        + dateStr + "(standard) - Got: " + offset + " Expected: " + OFFSETS1[d]);
215            }
216        }
217    }
218
219    // Test getOffset(UDate date, UBool local, int32_t& rawOffset,
220    // int32_t& dstOffset, UErrorCode& ec) with local = TRUE
221    for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
222        for (int32_t m = 0; m < NUM_DATES; m++) {
223            status = U_ZERO_ERROR;
224            TESTZONES[i]->getOffset(MILLIS[m], TRUE, rawOffset, dstOffset, status);
225            if (U_FAILURE(status)) {
226                errln((UnicodeString)"getOffset(date,local,rawOfset,dstOffset,ec) failed for TESTZONES[" + i + "]");
227            } else if (rawOffset != OFFSETS2[m][0] || dstOffset != OFFSETS2[m][1]) {
228                dateStr.remove();
229                df.format(MILLIS[m], dateStr);
230                dataerrln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
231                        + dateStr + "(wall) - Got: "
232                        + rawOffset + "/" + dstOffset
233                        + " Expected: " + OFFSETS2[m][0] + "/" + OFFSETS2[m][1]);
234            }
235        }
236    }
237
238    // Test getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
239    // int32_t& rawOffset, int32_t& dstOffset, UErroCode& status)
240    // with nonExistingTimeOpt=kStandard/duplicatedTimeOpt=kStandard
241    for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
242        for (int m = 0; m < NUM_DATES; m++) {
243            status = U_ZERO_ERROR;
244            TESTZONES[i]->getOffsetFromLocal(MILLIS[m], BasicTimeZone::kStandard, BasicTimeZone::kStandard,
245                rawOffset, dstOffset, status);
246            if (U_FAILURE(status)) {
247                errln((UnicodeString)"getOffsetFromLocal with kStandard/kStandard failed for TESTZONES[" + i + "]");
248            } else if (rawOffset != OFFSETS2[m][0] || dstOffset != OFFSETS2[m][1]) {
249                dateStr.remove();
250                df.format(MILLIS[m], dateStr);
251                dataerrln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
252                        + dateStr + "(wall/kStandard/kStandard) - Got: "
253                        + rawOffset + "/" + dstOffset
254                        + " Expected: " + OFFSETS2[m][0] + "/" + OFFSETS2[m][1]);
255            }
256        }
257    }
258
259    // Test getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
260    // int32_t& rawOffset, int32_t& dstOffset, UErroCode& status)
261    // with nonExistingTimeOpt=kDaylight/duplicatedTimeOpt=kDaylight
262    for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
263        for (int m = 0; m < NUM_DATES; m++) {
264            status = U_ZERO_ERROR;
265            TESTZONES[i]->getOffsetFromLocal(MILLIS[m], BasicTimeZone::kDaylight, BasicTimeZone::kDaylight,
266                rawOffset, dstOffset, status);
267            if (U_FAILURE(status)) {
268                errln((UnicodeString)"getOffsetFromLocal with kDaylight/kDaylight failed for TESTZONES[" + i + "]");
269            } else if (rawOffset != OFFSETS3[m][0] || dstOffset != OFFSETS3[m][1]) {
270                dateStr.remove();
271                df.format(MILLIS[m], dateStr);
272                dataerrln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
273                        + dateStr + "(wall/kDaylight/kDaylight) - Got: "
274                        + rawOffset + "/" + dstOffset
275                        + " Expected: " + OFFSETS3[m][0] + "/" + OFFSETS3[m][1]);
276            }
277        }
278    }
279
280    // Test getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
281    // int32_t& rawOffset, int32_t& dstOffset, UErroCode& status)
282    // with nonExistingTimeOpt=kFormer/duplicatedTimeOpt=kLatter
283    for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
284        for (int m = 0; m < NUM_DATES; m++) {
285            status = U_ZERO_ERROR;
286            TESTZONES[i]->getOffsetFromLocal(MILLIS[m], BasicTimeZone::kFormer, BasicTimeZone::kLatter,
287                rawOffset, dstOffset, status);
288            if (U_FAILURE(status)) {
289                errln((UnicodeString)"getOffsetFromLocal with kFormer/kLatter failed for TESTZONES[" + i + "]");
290            } else if (rawOffset != OFFSETS2[m][0] || dstOffset != OFFSETS2[m][1]) {
291                dateStr.remove();
292                df.format(MILLIS[m], dateStr);
293                dataerrln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
294                        + dateStr + "(wall/kFormer/kLatter) - Got: "
295                        + rawOffset + "/" + dstOffset
296                        + " Expected: " + OFFSETS2[m][0] + "/" + OFFSETS2[m][1]);
297            }
298        }
299    }
300
301    // Test getOffsetFromLocal(UDate date, int32_t nonExistingTimeOpt, int32_t duplicatedTimeOpt,
302    // int32_t& rawOffset, int32_t& dstOffset, UErroCode& status)
303    // with nonExistingTimeOpt=kLatter/duplicatedTimeOpt=kFormer
304    for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
305        for (int m = 0; m < NUM_DATES; m++) {
306            status = U_ZERO_ERROR;
307            TESTZONES[i]->getOffsetFromLocal(MILLIS[m], BasicTimeZone::kLatter, BasicTimeZone::kFormer,
308                rawOffset, dstOffset, status);
309            if (U_FAILURE(status)) {
310                errln((UnicodeString)"getOffsetFromLocal with kLatter/kFormer failed for TESTZONES[" + i + "]");
311            } else if (rawOffset != OFFSETS3[m][0] || dstOffset != OFFSETS3[m][1]) {
312                dateStr.remove();
313                df.format(MILLIS[m], dateStr);
314                dataerrln((UnicodeString)"Bad offset returned by TESTZONES[" + i + "] at "
315                        + dateStr + "(wall/kLatter/kFormer) - Got: "
316                        + rawOffset + "/" + dstOffset
317                        + " Expected: " + OFFSETS3[m][0] + "/" + OFFSETS3[m][1]);
318            }
319        }
320    }
321
322    for (int32_t i = 0; i < NUM_TIMEZONES; i++) {
323        delete TESTZONES[i];
324    }
325    delete utc;
326    delete cal;
327}
328
329#endif /* #if !UCONFIG_NO_FORMATTING */
330