Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/intl/icu/source/i18n/vtzone.cpp
Line
Count
Source (jump to first uncovered line)
1
// © 2016 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
/*
4
*******************************************************************************
5
* Copyright (C) 2007-2016, International Business Machines Corporation and
6
* others. All Rights Reserved.
7
*******************************************************************************
8
*/
9
10
#include "utypeinfo.h"  // for 'typeid' to work
11
12
#include "unicode/utypes.h"
13
14
#if !UCONFIG_NO_FORMATTING
15
16
#include "unicode/vtzone.h"
17
#include "unicode/rbtz.h"
18
#include "unicode/ucal.h"
19
#include "unicode/ures.h"
20
#include "cmemory.h"
21
#include "uvector.h"
22
#include "gregoimp.h"
23
#include "uassert.h"
24
25
U_NAMESPACE_BEGIN
26
27
// This is the deleter that will be use to remove TimeZoneRule
28
U_CDECL_BEGIN
29
static void U_CALLCONV
30
0
deleteTimeZoneRule(void* obj) {
31
0
    delete (TimeZoneRule*) obj;
32
0
}
33
U_CDECL_END
34
35
// Smybol characters used by RFC2445 VTIMEZONE
36
static const UChar COLON = 0x3A; /* : */
37
static const UChar SEMICOLON = 0x3B; /* ; */
38
static const UChar EQUALS_SIGN = 0x3D; /* = */
39
static const UChar COMMA = 0x2C; /* , */
40
static const UChar PLUS = 0x2B; /* + */
41
static const UChar MINUS = 0x2D; /* - */
42
43
// RFC2445 VTIMEZONE tokens
44
static const UChar ICAL_BEGIN_VTIMEZONE[] = {0x42, 0x45, 0x47, 0x49, 0x4E, 0x3A, 0x56, 0x54, 0x49, 0x4D, 0x45, 0x5A, 0x4F, 0x4E, 0x45, 0}; /* "BEGIN:VTIMEZONE" */
45
static const UChar ICAL_END_VTIMEZONE[] = {0x45, 0x4E, 0x44, 0x3A, 0x56, 0x54, 0x49, 0x4D, 0x45, 0x5A, 0x4F, 0x4E, 0x45, 0}; /* "END:VTIMEZONE" */
46
static const UChar ICAL_BEGIN[] = {0x42, 0x45, 0x47, 0x49, 0x4E, 0}; /* "BEGIN" */
47
static const UChar ICAL_END[] = {0x45, 0x4E, 0x44, 0}; /* "END" */
48
static const UChar ICAL_VTIMEZONE[] = {0x56, 0x54, 0x49, 0x4D, 0x45, 0x5A, 0x4F, 0x4E, 0x45, 0}; /* "VTIMEZONE" */
49
static const UChar ICAL_TZID[] = {0x54, 0x5A, 0x49, 0x44, 0}; /* "TZID" */
50
static const UChar ICAL_STANDARD[] = {0x53, 0x54, 0x41, 0x4E, 0x44, 0x41, 0x52, 0x44, 0}; /* "STANDARD" */
51
static const UChar ICAL_DAYLIGHT[] = {0x44, 0x41, 0x59, 0x4C, 0x49, 0x47, 0x48, 0x54, 0}; /* "DAYLIGHT" */
52
static const UChar ICAL_DTSTART[] = {0x44, 0x54, 0x53, 0x54, 0x41, 0x52, 0x54, 0}; /* "DTSTART" */
53
static const UChar ICAL_TZOFFSETFROM[] = {0x54, 0x5A, 0x4F, 0x46, 0x46, 0x53, 0x45, 0x54, 0x46, 0x52, 0x4F, 0x4D, 0}; /* "TZOFFSETFROM" */
54
static const UChar ICAL_TZOFFSETTO[] = {0x54, 0x5A, 0x4F, 0x46, 0x46, 0x53, 0x45, 0x54, 0x54, 0x4F, 0}; /* "TZOFFSETTO" */
55
static const UChar ICAL_RDATE[] = {0x52, 0x44, 0x41, 0x54, 0x45, 0}; /* "RDATE" */
56
static const UChar ICAL_RRULE[] = {0x52, 0x52, 0x55, 0x4C, 0x45, 0}; /* "RRULE" */
57
static const UChar ICAL_TZNAME[] = {0x54, 0x5A, 0x4E, 0x41, 0x4D, 0x45, 0}; /* "TZNAME" */
58
static const UChar ICAL_TZURL[] = {0x54, 0x5A, 0x55, 0x52, 0x4C, 0}; /* "TZURL" */
59
static const UChar ICAL_LASTMOD[] = {0x4C, 0x41, 0x53, 0x54, 0x2D, 0x4D, 0x4F, 0x44, 0x49, 0x46, 0x49, 0x45, 0x44, 0}; /* "LAST-MODIFIED" */
60
61
static const UChar ICAL_FREQ[] = {0x46, 0x52, 0x45, 0x51, 0}; /* "FREQ" */
62
static const UChar ICAL_UNTIL[] = {0x55, 0x4E, 0x54, 0x49, 0x4C, 0}; /* "UNTIL" */
63
static const UChar ICAL_YEARLY[] = {0x59, 0x45, 0x41, 0x52, 0x4C, 0x59, 0}; /* "YEARLY" */
64
static const UChar ICAL_BYMONTH[] = {0x42, 0x59, 0x4D, 0x4F, 0x4E, 0x54, 0x48, 0}; /* "BYMONTH" */
65
static const UChar ICAL_BYDAY[] = {0x42, 0x59, 0x44, 0x41, 0x59, 0}; /* "BYDAY" */
66
static const UChar ICAL_BYMONTHDAY[] = {0x42, 0x59, 0x4D, 0x4F, 0x4E, 0x54, 0x48, 0x44, 0x41, 0x59, 0}; /* "BYMONTHDAY" */
67
68
static const UChar ICAL_NEWLINE[] = {0x0D, 0x0A, 0}; /* CRLF */
69
70
static const UChar ICAL_DOW_NAMES[7][3] = {
71
    {0x53, 0x55, 0}, /* "SU" */
72
    {0x4D, 0x4F, 0}, /* "MO" */
73
    {0x54, 0x55, 0}, /* "TU" */
74
    {0x57, 0x45, 0}, /* "WE" */
75
    {0x54, 0x48, 0}, /* "TH" */
76
    {0x46, 0x52, 0}, /* "FR" */
77
    {0x53, 0x41, 0}  /* "SA" */};
78
79
// Month length for non-leap year
80
static const int32_t MONTHLENGTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
81
82
// ICU custom property
83
static const UChar ICU_TZINFO_PROP[] = {0x58, 0x2D, 0x54, 0x5A, 0x49, 0x4E, 0x46, 0x4F, 0x3A, 0}; /* "X-TZINFO:" */
84
static const UChar ICU_TZINFO_PARTIAL[] = {0x2F, 0x50, 0x61, 0x72, 0x74, 0x69, 0x61, 0x6C, 0x40, 0}; /* "/Partial@" */
85
static const UChar ICU_TZINFO_SIMPLE[] = {0x2F, 0x53, 0x69, 0x6D, 0x70, 0x6C, 0x65, 0x40, 0}; /* "/Simple@" */
86
87
88
/*
89
 * Simple fixed digit ASCII number to integer converter
90
 */
91
0
static int32_t parseAsciiDigits(const UnicodeString& str, int32_t start, int32_t length, UErrorCode& status) {
92
0
    if (U_FAILURE(status)) {
93
0
        return 0;
94
0
    }
95
0
    if (length <= 0 || str.length() < start || (start + length) > str.length()) {
96
0
        status = U_INVALID_FORMAT_ERROR;
97
0
        return 0;
98
0
    }
99
0
    int32_t sign = 1;
100
0
    if (str.charAt(start) == PLUS) {
101
0
        start++;
102
0
        length--;
103
0
    } else if (str.charAt(start) == MINUS) {
104
0
        sign = -1;
105
0
        start++;
106
0
        length--;
107
0
    }
108
0
    int32_t num = 0;
109
0
    for (int32_t i = 0; i < length; i++) {
110
0
        int32_t digit = str.charAt(start + i) - 0x0030;
111
0
        if (digit < 0 || digit > 9) {
112
0
            status = U_INVALID_FORMAT_ERROR;
113
0
            return 0;
114
0
        }
115
0
        num = 10 * num + digit;
116
0
    }
117
0
    return sign * num;    
118
0
}
119
120
0
static UnicodeString& appendAsciiDigits(int32_t number, uint8_t length, UnicodeString& str) {
121
0
    UBool negative = FALSE;
122
0
    int32_t digits[10]; // max int32_t is 10 decimal digits
123
0
    int32_t i;
124
0
125
0
    if (number < 0) {
126
0
        negative = TRUE;
127
0
        number *= -1;
128
0
    }
129
0
130
0
    length = length > 10 ? 10 : length;
131
0
    if (length == 0) {
132
0
        // variable length
133
0
        i = 0;
134
0
        do {
135
0
            digits[i++] = number % 10;
136
0
            number /= 10;
137
0
        } while (number != 0);
138
0
        length = i;
139
0
    } else {
140
0
        // fixed digits
141
0
        for (i = 0; i < length; i++) {
142
0
           digits[i] = number % 10;
143
0
           number /= 10;
144
0
        }
145
0
    }
146
0
    if (negative) {
147
0
        str.append(MINUS);
148
0
    }
149
0
    for (i = length - 1; i >= 0; i--) {
150
0
        str.append((UChar)(digits[i] + 0x0030));
151
0
    }
152
0
    return str;
153
0
}
154
155
0
static UnicodeString& appendMillis(UDate date, UnicodeString& str) {
156
0
    UBool negative = FALSE;
157
0
    int32_t digits[20]; // max int64_t is 20 decimal digits
158
0
    int32_t i;
159
0
    int64_t number;
160
0
161
0
    if (date < MIN_MILLIS) {
162
0
        number = (int64_t)MIN_MILLIS;
163
0
    } else if (date > MAX_MILLIS) {
164
0
        number = (int64_t)MAX_MILLIS;
165
0
    } else {
166
0
        number = (int64_t)date;
167
0
    }
168
0
    if (number < 0) {
169
0
        negative = TRUE;
170
0
        number *= -1;
171
0
    }
172
0
    i = 0;
173
0
    do {
174
0
        digits[i++] = (int32_t)(number % 10);
175
0
        number /= 10;
176
0
    } while (number != 0);
177
0
178
0
    if (negative) {
179
0
        str.append(MINUS);
180
0
    }
181
0
    i--;
182
0
    while (i >= 0) {
183
0
        str.append((UChar)(digits[i--] + 0x0030));
184
0
    }
185
0
    return str;
186
0
}
187
188
/*
189
 * Convert date/time to RFC2445 Date-Time form #1 DATE WITH LOCAL TIME
190
 */
191
0
static UnicodeString& getDateTimeString(UDate time, UnicodeString& str) {
192
0
    int32_t year, month, dom, dow, doy, mid;
193
0
    Grego::timeToFields(time, year, month, dom, dow, doy, mid);
194
0
195
0
    str.remove();
196
0
    appendAsciiDigits(year, 4, str);
197
0
    appendAsciiDigits(month + 1, 2, str);
198
0
    appendAsciiDigits(dom, 2, str);
199
0
    str.append((UChar)0x0054 /*'T'*/);
200
0
201
0
    int32_t t = mid;
202
0
    int32_t hour = t / U_MILLIS_PER_HOUR;
203
0
    t %= U_MILLIS_PER_HOUR;
204
0
    int32_t min = t / U_MILLIS_PER_MINUTE;
205
0
    t %= U_MILLIS_PER_MINUTE;
206
0
    int32_t sec = t / U_MILLIS_PER_SECOND;
207
0
208
0
    appendAsciiDigits(hour, 2, str);
209
0
    appendAsciiDigits(min, 2, str);
210
0
    appendAsciiDigits(sec, 2, str);
211
0
    return str;
212
0
}
213
214
/*
215
 * Convert date/time to RFC2445 Date-Time form #2 DATE WITH UTC TIME
216
 */
217
0
static UnicodeString& getUTCDateTimeString(UDate time, UnicodeString& str) {
218
0
    getDateTimeString(time, str);
219
0
    str.append((UChar)0x005A /*'Z'*/);
220
0
    return str;
221
0
}
222
223
/*
224
 * Parse RFC2445 Date-Time form #1 DATE WITH LOCAL TIME and
225
 * #2 DATE WITH UTC TIME
226
 */
227
0
static UDate parseDateTimeString(const UnicodeString& str, int32_t offset, UErrorCode& status) {
228
0
    if (U_FAILURE(status)) {
229
0
        return 0.0;
230
0
    }
231
0
232
0
    int32_t year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0;
233
0
    UBool isUTC = FALSE;
234
0
    UBool isValid = FALSE;
235
0
    do {
236
0
        int length = str.length();
237
0
        if (length != 15 && length != 16) {
238
0
            // FORM#1 15 characters, such as "20060317T142115"
239
0
            // FORM#2 16 characters, such as "20060317T142115Z"
240
0
            break;
241
0
        }
242
0
        if (str.charAt(8) != 0x0054) {
243
0
            // charcter "T" must be used for separating date and time
244
0
            break;
245
0
        }
246
0
        if (length == 16) {
247
0
            if (str.charAt(15) != 0x005A) {
248
0
                // invalid format
249
0
                break;
250
0
            }
251
0
            isUTC = TRUE;
252
0
        }
253
0
254
0
        year = parseAsciiDigits(str, 0, 4, status);
255
0
        month = parseAsciiDigits(str, 4, 2, status) - 1;  // 0-based
256
0
        day = parseAsciiDigits(str, 6, 2, status);
257
0
        hour = parseAsciiDigits(str, 9, 2, status);
258
0
        min = parseAsciiDigits(str, 11, 2, status);
259
0
        sec = parseAsciiDigits(str, 13, 2, status);
260
0
261
0
        if (U_FAILURE(status)) {
262
0
            break;
263
0
        }
264
0
265
0
        // check valid range
266
0
        int32_t maxDayOfMonth = Grego::monthLength(year, month);
267
0
        if (year < 0 || month < 0 || month > 11 || day < 1 || day > maxDayOfMonth ||
268
0
                hour < 0 || hour >= 24 || min < 0 || min >= 60 || sec < 0 || sec >= 60) {
269
0
            break;
270
0
        }
271
0
272
0
        isValid = TRUE;
273
0
    } while(false);
274
0
275
0
    if (!isValid) {
276
0
        status = U_INVALID_FORMAT_ERROR;
277
0
        return 0.0;
278
0
    }
279
0
    // Calculate the time
280
0
    UDate time = Grego::fieldsToDay(year, month, day) * U_MILLIS_PER_DAY;
281
0
    time += (hour * U_MILLIS_PER_HOUR + min * U_MILLIS_PER_MINUTE + sec * U_MILLIS_PER_SECOND);
282
0
    if (!isUTC) {
283
0
        time -= offset;
284
0
    }
285
0
    return time;
286
0
}
287
288
/*
289
 * Convert RFC2445 utc-offset string to milliseconds
290
 */
291
0
static int32_t offsetStrToMillis(const UnicodeString& str, UErrorCode& status) {
292
0
    if (U_FAILURE(status)) {
293
0
        return 0;
294
0
    }
295
0
296
0
    UBool isValid = FALSE;
297
0
    int32_t sign = 0, hour = 0, min = 0, sec = 0;
298
0
299
0
    do {
300
0
        int length = str.length();
301
0
        if (length != 5 && length != 7) {
302
0
            // utf-offset must be 5 or 7 characters
303
0
            break;
304
0
        }
305
0
        // sign
306
0
        UChar s = str.charAt(0);
307
0
        if (s == PLUS) {
308
0
            sign = 1;
309
0
        } else if (s == MINUS) {
310
0
            sign = -1;
311
0
        } else {
312
0
            // utf-offset must start with "+" or "-"
313
0
            break;
314
0
        }
315
0
        hour = parseAsciiDigits(str, 1, 2, status);
316
0
        min = parseAsciiDigits(str, 3, 2, status);
317
0
        if (length == 7) {
318
0
            sec = parseAsciiDigits(str, 5, 2, status);
319
0
        }
320
0
        if (U_FAILURE(status)) {
321
0
            break;
322
0
        }
323
0
        isValid = true;
324
0
    } while(false);
325
0
326
0
    if (!isValid) {
327
0
        status = U_INVALID_FORMAT_ERROR;
328
0
        return 0;
329
0
    }
330
0
    int32_t millis = sign * ((hour * 60 + min) * 60 + sec) * 1000;
331
0
    return millis;
332
0
}
333
334
/*
335
 * Convert milliseconds to RFC2445 utc-offset string
336
 */
337
0
static void millisToOffset(int32_t millis, UnicodeString& str) {
338
0
    str.remove();
339
0
    if (millis >= 0) {
340
0
        str.append(PLUS);
341
0
    } else {
342
0
        str.append(MINUS);
343
0
        millis = -millis;
344
0
    }
345
0
    int32_t hour, min, sec;
346
0
    int32_t t = millis / 1000;
347
0
348
0
    sec = t % 60;
349
0
    t = (t - sec) / 60;
350
0
    min = t % 60;
351
0
    hour = t / 60;
352
0
353
0
    appendAsciiDigits(hour, 2, str);
354
0
    appendAsciiDigits(min, 2, str);
355
0
    appendAsciiDigits(sec, 2, str);
356
0
}
357
358
/*
359
 * Create a default TZNAME from TZID
360
 */
361
0
static void getDefaultTZName(const UnicodeString &tzid, UBool isDST, UnicodeString& zonename) {
362
0
    zonename = tzid;
363
0
    if (isDST) {
364
0
        zonename += UNICODE_STRING_SIMPLE("(DST)");
365
0
    } else {
366
0
        zonename += UNICODE_STRING_SIMPLE("(STD)");
367
0
    }
368
0
}
369
370
/*
371
 * Parse individual RRULE
372
 * 
373
 * On return -
374
 * 
375
 * month    calculated by BYMONTH-1, or -1 when not found
376
 * dow      day of week in BYDAY, or 0 when not found
377
 * wim      day of week ordinal number in BYDAY, or 0 when not found
378
 * dom      an array of day of month
379
 * domCount number of availble days in dom (domCount is specifying the size of dom on input)
380
 * until    time defined by UNTIL attribute or MIN_MILLIS if not available
381
 */
382
static void parseRRULE(const UnicodeString& rrule, int32_t& month, int32_t& dow, int32_t& wim,
383
0
                       int32_t* dom, int32_t& domCount, UDate& until, UErrorCode& status) {
384
0
    if (U_FAILURE(status)) {
385
0
        return;
386
0
    }
387
0
    int32_t numDom = 0;
388
0
389
0
    month = -1;
390
0
    dow = 0;
391
0
    wim = 0;
392
0
    until = MIN_MILLIS;
393
0
394
0
    UBool yearly = FALSE;
395
0
    //UBool parseError = FALSE;
396
0
397
0
    int32_t prop_start = 0;
398
0
    int32_t prop_end;
399
0
    UnicodeString prop, attr, value;
400
0
    UBool nextProp = TRUE;
401
0
402
0
    while (nextProp) {
403
0
        prop_end = rrule.indexOf(SEMICOLON, prop_start);
404
0
        if (prop_end == -1) {
405
0
            prop.setTo(rrule, prop_start);
406
0
            nextProp = FALSE;
407
0
        } else {
408
0
            prop.setTo(rrule, prop_start, prop_end - prop_start);
409
0
            prop_start = prop_end + 1;
410
0
        }
411
0
        int32_t eql = prop.indexOf(EQUALS_SIGN);
412
0
        if (eql != -1) {
413
0
            attr.setTo(prop, 0, eql);
414
0
            value.setTo(prop, eql + 1);
415
0
        } else {
416
0
            goto rruleParseError;
417
0
        }
418
0
419
0
        if (attr.compare(ICAL_FREQ, -1) == 0) {
420
0
            // only support YEARLY frequency type
421
0
            if (value.compare(ICAL_YEARLY, -1) == 0) {
422
0
                yearly = TRUE;
423
0
            } else {
424
0
                goto rruleParseError;
425
0
            }
426
0
        } else if (attr.compare(ICAL_UNTIL, -1) == 0) {
427
0
            // ISO8601 UTC format, for example, "20060315T020000Z"
428
0
            until = parseDateTimeString(value, 0, status);
429
0
            if (U_FAILURE(status)) {
430
0
                goto rruleParseError;
431
0
            }
432
0
        } else if (attr.compare(ICAL_BYMONTH, -1) == 0) {
433
0
            // Note: BYMONTH may contain multiple months, but only single month make sense for
434
0
            // VTIMEZONE property.
435
0
            if (value.length() > 2) {
436
0
                goto rruleParseError;
437
0
            }
438
0
            month = parseAsciiDigits(value, 0, value.length(), status) - 1;
439
0
            if (U_FAILURE(status) || month < 0 || month >= 12) {
440
0
                goto rruleParseError;
441
0
            }
442
0
        } else if (attr.compare(ICAL_BYDAY, -1) == 0) {
443
0
            // Note: BYDAY may contain multiple day of week separated by comma.  It is unlikely used for
444
0
            // VTIMEZONE property.  We do not support the case.
445
0
446
0
            // 2-letter format is used just for representing a day of week, for example, "SU" for Sunday
447
0
            // 3 or 4-letter format is used for represeinging Nth day of week, for example, "-1SA" for last Saturday
448
0
            int32_t length = value.length();
449
0
            if (length < 2 || length > 4) {
450
0
                goto rruleParseError;
451
0
            }
452
0
            if (length > 2) {
453
0
                // Nth day of week
454
0
                int32_t sign = 1;
455
0
                if (value.charAt(0) == PLUS) {
456
0
                    sign = 1;
457
0
                } else if (value.charAt(0) == MINUS) {
458
0
                    sign = -1;
459
0
                } else if (length == 4) {
460
0
                    goto rruleParseError;
461
0
                }
462
0
                int32_t n = parseAsciiDigits(value, length - 3, 1, status);
463
0
                if (U_FAILURE(status) || n == 0 || n > 4) {
464
0
                    goto rruleParseError;
465
0
                }
466
0
                wim = n * sign;
467
0
                value.remove(0, length - 2);
468
0
            }
469
0
            int32_t wday;
470
0
            for (wday = 0; wday < 7; wday++) {
471
0
                if (value.compare(ICAL_DOW_NAMES[wday], 2) == 0) {
472
0
                    break;
473
0
                }
474
0
            }
475
0
            if (wday < 7) {
476
0
                // Sunday(1) - Saturday(7)
477
0
                dow = wday + 1;
478
0
            } else {
479
0
                goto rruleParseError;
480
0
            }
481
0
        } else if (attr.compare(ICAL_BYMONTHDAY, -1) == 0) {
482
0
            // Note: BYMONTHDAY may contain multiple days delimitted by comma
483
0
            //
484
0
            // A value of BYMONTHDAY could be negative, for example, -1 means
485
0
            // the last day in a month
486
0
            int32_t dom_idx = 0;
487
0
            int32_t dom_start = 0;
488
0
            int32_t dom_end;
489
0
            UBool nextDOM = TRUE;
490
0
            while (nextDOM) {
491
0
                dom_end = value.indexOf(COMMA, dom_start);
492
0
                if (dom_end == -1) {
493
0
                    dom_end = value.length();
494
0
                    nextDOM = FALSE;
495
0
                }
496
0
                if (dom_idx < domCount) {
497
0
                    dom[dom_idx] = parseAsciiDigits(value, dom_start, dom_end - dom_start, status);
498
0
                    if (U_FAILURE(status)) {
499
0
                        goto rruleParseError;
500
0
                    }
501
0
                    dom_idx++;
502
0
                } else {
503
0
                    status = U_BUFFER_OVERFLOW_ERROR;
504
0
                    goto rruleParseError;
505
0
                }
506
0
                dom_start = dom_end + 1;
507
0
            }
508
0
            numDom = dom_idx;
509
0
        }
510
0
    }
511
0
    if (!yearly) {
512
0
        // FREQ=YEARLY must be set
513
0
        goto rruleParseError;
514
0
    }
515
0
    // Set actual number of parsed DOM (ICAL_BYMONTHDAY)
516
0
    domCount = numDom;
517
0
    return;
518
0
519
0
rruleParseError:
520
0
    if (U_SUCCESS(status)) {
521
0
        // Set error status
522
0
        status = U_INVALID_FORMAT_ERROR;
523
0
    }
524
0
}
525
526
static TimeZoneRule* createRuleByRRULE(const UnicodeString& zonename, int rawOffset, int dstSavings, UDate start,
527
0
                                       UVector* dates, int fromOffset, UErrorCode& status) {
528
0
    if (U_FAILURE(status)) {
529
0
        return NULL;
530
0
    }
531
0
    if (dates == NULL || dates->size() == 0) {
532
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
533
0
        return NULL;
534
0
    }
535
0
536
0
    int32_t i, j;
537
0
    DateTimeRule *adtr = NULL;
538
0
539
0
    // Parse the first rule
540
0
    UnicodeString rrule = *((UnicodeString*)dates->elementAt(0));
541
0
    int32_t month, dayOfWeek, nthDayOfWeek, dayOfMonth = 0;
542
0
    int32_t days[7];
543
0
    int32_t daysCount = UPRV_LENGTHOF(days);
544
0
    UDate until;
545
0
546
0
    parseRRULE(rrule, month, dayOfWeek, nthDayOfWeek, days, daysCount, until, status);
547
0
    if (U_FAILURE(status)) {
548
0
        return NULL;
549
0
    }
550
0
551
0
    if (dates->size() == 1) {
552
0
        // No more rules
553
0
        if (daysCount > 1) {
554
0
            // Multiple BYMONTHDAY values
555
0
            if (daysCount != 7 || month == -1 || dayOfWeek == 0) {
556
0
                // Only support the rule using 7 continuous days
557
0
                // BYMONTH and BYDAY must be set at the same time
558
0
                goto unsupportedRRule;
559
0
            }
560
0
            int32_t firstDay = 31; // max possible number of dates in a month
561
0
            for (i = 0; i < 7; i++) {
562
0
                // Resolve negative day numbers.  A negative day number should
563
0
                // not be used in February, but if we see such case, we use 28
564
0
                // as the base.
565
0
                if (days[i] < 0) {
566
0
                    days[i] = MONTHLENGTH[month] + days[i] + 1;
567
0
                }
568
0
                if (days[i] < firstDay) {
569
0
                    firstDay = days[i];
570
0
                }
571
0
            }
572
0
            // Make sure days are continuous
573
0
            for (i = 1; i < 7; i++) {
574
0
                UBool found = FALSE;
575
0
                for (j = 0; j < 7; j++) {
576
0
                    if (days[j] == firstDay + i) {
577
0
                        found = TRUE;
578
0
                        break;
579
0
                    }
580
0
                }
581
0
                if (!found) {
582
0
                    // days are not continuous
583
0
                    goto unsupportedRRule;
584
0
                }
585
0
            }
586
0
            // Use DOW_GEQ_DOM rule with firstDay as the start date
587
0
            dayOfMonth = firstDay;
588
0
        }
589
0
    } else {
590
0
        // Check if BYMONTH + BYMONTHDAY + BYDAY rule with multiple RRULE lines.
591
0
        // Otherwise, not supported.
592
0
        if (month == -1 || dayOfWeek == 0 || daysCount == 0) {
593
0
            // This is not the case
594
0
            goto unsupportedRRule;
595
0
        }
596
0
        // Parse the rest of rules if number of rules is not exceeding 7.
597
0
        // We can only support 7 continuous days starting from a day of month.
598
0
        if (dates->size() > 7) {
599
0
            goto unsupportedRRule;
600
0
        }
601
0
602
0
        // Note: To check valid date range across multiple rule is a little
603
0
        // bit complicated.  For now, this code is not doing strict range
604
0
        // checking across month boundary
605
0
606
0
        int32_t earliestMonth = month;
607
0
        int32_t earliestDay = 31;
608
0
        for (i = 0; i < daysCount; i++) {
609
0
            int32_t dom = days[i];
610
0
            dom = dom > 0 ? dom : MONTHLENGTH[month] + dom + 1;
611
0
            earliestDay = dom < earliestDay ? dom : earliestDay;
612
0
        }
613
0
614
0
        int32_t anotherMonth = -1;
615
0
        for (i = 1; i < dates->size(); i++) {
616
0
            rrule = *((UnicodeString*)dates->elementAt(i));
617
0
            UDate tmp_until;
618
0
            int32_t tmp_month, tmp_dayOfWeek, tmp_nthDayOfWeek;
619
0
            int32_t tmp_days[7];
620
0
            int32_t tmp_daysCount = UPRV_LENGTHOF(tmp_days);
621
0
            parseRRULE(rrule, tmp_month, tmp_dayOfWeek, tmp_nthDayOfWeek, tmp_days, tmp_daysCount, tmp_until, status);
622
0
            if (U_FAILURE(status)) {
623
0
                return NULL;
624
0
            }
625
0
            // If UNTIL is newer than previous one, use the one
626
0
            if (tmp_until > until) {
627
0
                until = tmp_until;
628
0
            }
629
0
            
630
0
            // Check if BYMONTH + BYMONTHDAY + BYDAY rule
631
0
            if (tmp_month == -1 || tmp_dayOfWeek == 0 || tmp_daysCount == 0) {
632
0
                goto unsupportedRRule;
633
0
            }
634
0
            // Count number of BYMONTHDAY
635
0
            if (daysCount + tmp_daysCount > 7) {
636
0
                // We cannot support BYMONTHDAY more than 7
637
0
                goto unsupportedRRule;
638
0
            }
639
0
            // Check if the same BYDAY is used.  Otherwise, we cannot
640
0
            // support the rule
641
0
            if (tmp_dayOfWeek != dayOfWeek) {
642
0
                goto unsupportedRRule;
643
0
            }
644
0
            // Check if the month is same or right next to the primary month
645
0
            if (tmp_month != month) {
646
0
                if (anotherMonth == -1) {
647
0
                    int32_t diff = tmp_month - month;
648
0
                    if (diff == -11 || diff == -1) {
649
0
                        // Previous month
650
0
                        anotherMonth = tmp_month;
651
0
                        earliestMonth = anotherMonth;
652
0
                        // Reset earliest day
653
0
                        earliestDay = 31;
654
0
                    } else if (diff == 11 || diff == 1) {
655
0
                        // Next month
656
0
                        anotherMonth = tmp_month;
657
0
                    } else {
658
0
                        // The day range cannot exceed more than 2 months
659
0
                        goto unsupportedRRule;
660
0
                    }
661
0
                } else if (tmp_month != month && tmp_month != anotherMonth) {
662
0
                    // The day range cannot exceed more than 2 months
663
0
                    goto unsupportedRRule;
664
0
                }
665
0
            }
666
0
            // If ealier month, go through days to find the earliest day
667
0
            if (tmp_month == earliestMonth) {
668
0
                for (j = 0; j < tmp_daysCount; j++) {
669
0
                    tmp_days[j] = tmp_days[j] > 0 ? tmp_days[j] : MONTHLENGTH[tmp_month] + tmp_days[j] + 1;
670
0
                    earliestDay = tmp_days[j] < earliestDay ? tmp_days[j] : earliestDay;
671
0
                }
672
0
            }
673
0
            daysCount += tmp_daysCount;
674
0
        }
675
0
        if (daysCount != 7) {
676
0
            // Number of BYMONTHDAY entries must be 7
677
0
            goto unsupportedRRule;
678
0
        }
679
0
        month = earliestMonth;
680
0
        dayOfMonth = earliestDay;
681
0
    }
682
0
683
0
    // Calculate start/end year and missing fields
684
0
    int32_t startYear, startMonth, startDOM, startDOW, startDOY, startMID;
685
0
    Grego::timeToFields(start + fromOffset, startYear, startMonth, startDOM,
686
0
        startDOW, startDOY, startMID);
687
0
    if (month == -1) {
688
0
        // If BYMONTH is not set, use the month of DTSTART
689
0
        month = startMonth;
690
0
    }
691
0
    if (dayOfWeek == 0 && nthDayOfWeek == 0 && dayOfMonth == 0) {
692
0
        // If only YEARLY is set, use the day of DTSTART as BYMONTHDAY
693
0
        dayOfMonth = startDOM;
694
0
    }
695
0
696
0
    int32_t endYear;
697
0
    if (until != MIN_MILLIS) {
698
0
        int32_t endMonth, endDOM, endDOW, endDOY, endMID;
699
0
        Grego::timeToFields(until, endYear, endMonth, endDOM, endDOW, endDOY, endMID);
700
0
    } else {
701
0
        endYear = AnnualTimeZoneRule::MAX_YEAR;
702
0
    }
703
0
704
0
    // Create the AnnualDateTimeRule
705
0
    if (dayOfWeek == 0 && nthDayOfWeek == 0 && dayOfMonth != 0) {
706
0
        // Day in month rule, for example, 15th day in the month
707
0
        adtr = new DateTimeRule(month, dayOfMonth, startMID, DateTimeRule::WALL_TIME);
708
0
    } else if (dayOfWeek != 0 && nthDayOfWeek != 0 && dayOfMonth == 0) {
709
0
        // Nth day of week rule, for example, last Sunday
710
0
        adtr = new DateTimeRule(month, nthDayOfWeek, dayOfWeek, startMID, DateTimeRule::WALL_TIME);
711
0
    } else if (dayOfWeek != 0 && nthDayOfWeek == 0 && dayOfMonth != 0) {
712
0
        // First day of week after day of month rule, for example,
713
0
        // first Sunday after 15th day in the month
714
0
        adtr = new DateTimeRule(month, dayOfMonth, dayOfWeek, TRUE, startMID, DateTimeRule::WALL_TIME);
715
0
    }
716
0
    if (adtr == NULL) {
717
0
        goto unsupportedRRule;
718
0
    }
719
0
    return new AnnualTimeZoneRule(zonename, rawOffset, dstSavings, adtr, startYear, endYear);
720
0
721
0
unsupportedRRule:
722
0
    status = U_INVALID_STATE_ERROR;
723
0
    return NULL;
724
0
}
725
726
/*
727
 * Create a TimeZoneRule by the RDATE definition
728
 */
729
static TimeZoneRule* createRuleByRDATE(const UnicodeString& zonename, int32_t rawOffset, int32_t dstSavings,
730
0
                                       UDate start, UVector* dates, int32_t fromOffset, UErrorCode& status) {
731
0
    if (U_FAILURE(status)) {
732
0
        return NULL;
733
0
    }
734
0
    TimeArrayTimeZoneRule *retVal = NULL;
735
0
    if (dates == NULL || dates->size() == 0) {
736
0
        // When no RDATE line is provided, use start (DTSTART)
737
0
        // as the transition time
738
0
        retVal = new TimeArrayTimeZoneRule(zonename, rawOffset, dstSavings,
739
0
            &start, 1, DateTimeRule::UTC_TIME);
740
0
    } else {
741
0
        // Create an array of transition times
742
0
        int32_t size = dates->size();
743
0
        UDate* times = (UDate*)uprv_malloc(sizeof(UDate) * size);
744
0
        if (times == NULL) {
745
0
            status = U_MEMORY_ALLOCATION_ERROR;
746
0
            return NULL;
747
0
        }
748
0
        for (int32_t i = 0; i < size; i++) {
749
0
            UnicodeString *datestr = (UnicodeString*)dates->elementAt(i);
750
0
            times[i] = parseDateTimeString(*datestr, fromOffset, status);
751
0
            if (U_FAILURE(status)) {
752
0
                uprv_free(times);
753
0
                return NULL;
754
0
            }
755
0
        }
756
0
        retVal = new TimeArrayTimeZoneRule(zonename, rawOffset, dstSavings,
757
0
            times, size, DateTimeRule::UTC_TIME);
758
0
        uprv_free(times);
759
0
    }
760
0
    return retVal;
761
0
}
762
763
/*
764
 * Check if the DOW rule specified by month, weekInMonth and dayOfWeek is equivalent
765
 * to the DateTimerule.
766
 */
767
0
static UBool isEquivalentDateRule(int32_t month, int32_t weekInMonth, int32_t dayOfWeek, const DateTimeRule *dtrule) {
768
0
    if (month != dtrule->getRuleMonth() || dayOfWeek != dtrule->getRuleDayOfWeek()) {
769
0
        return FALSE;
770
0
    }
771
0
    if (dtrule->getTimeRuleType() != DateTimeRule::WALL_TIME) {
772
0
        // Do not try to do more intelligent comparison for now.
773
0
        return FALSE;
774
0
    }
775
0
    if (dtrule->getDateRuleType() == DateTimeRule::DOW
776
0
            && dtrule->getRuleWeekInMonth() == weekInMonth) {
777
0
        return TRUE;
778
0
    }
779
0
    int32_t ruleDOM = dtrule->getRuleDayOfMonth();
780
0
    if (dtrule->getDateRuleType() == DateTimeRule::DOW_GEQ_DOM) {
781
0
        if (ruleDOM%7 == 1 && (ruleDOM + 6)/7 == weekInMonth) {
782
0
            return TRUE;
783
0
        }
784
0
        if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - ruleDOM)%7 == 6
785
0
                && weekInMonth == -1*((MONTHLENGTH[month]-ruleDOM+1)/7)) {
786
0
            return TRUE;
787
0
        }
788
0
    }
789
0
    if (dtrule->getDateRuleType() == DateTimeRule::DOW_LEQ_DOM) {
790
0
        if (ruleDOM%7 == 0 && ruleDOM/7 == weekInMonth) {
791
0
            return TRUE;
792
0
        }
793
0
        if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - ruleDOM)%7 == 0
794
0
                && weekInMonth == -1*((MONTHLENGTH[month] - ruleDOM)/7 + 1)) {
795
0
            return TRUE;
796
0
        }
797
0
    }
798
0
    return FALSE;
799
0
}
800
801
/*
802
 * Convert the rule to its equivalent rule using WALL_TIME mode.
803
 * This function returns NULL when the specified DateTimeRule is already
804
 * using WALL_TIME mode.
805
 */
806
0
static DateTimeRule* toWallTimeRule(const DateTimeRule* rule, int32_t rawOffset, int32_t dstSavings) {
807
0
    if (rule->getTimeRuleType() == DateTimeRule::WALL_TIME) {
808
0
        return NULL;
809
0
    }
810
0
    int32_t wallt = rule->getRuleMillisInDay();
811
0
    if (rule->getTimeRuleType() == DateTimeRule::UTC_TIME) {
812
0
        wallt += (rawOffset + dstSavings);
813
0
    } else if (rule->getTimeRuleType() == DateTimeRule::STANDARD_TIME) {
814
0
        wallt += dstSavings;
815
0
    }
816
0
817
0
    int32_t month = -1, dom = 0, dow = 0;
818
0
    DateTimeRule::DateRuleType dtype;
819
0
    int32_t dshift = 0;
820
0
    if (wallt < 0) {
821
0
        dshift = -1;
822
0
        wallt += U_MILLIS_PER_DAY;
823
0
    } else if (wallt >= U_MILLIS_PER_DAY) {
824
0
        dshift = 1;
825
0
        wallt -= U_MILLIS_PER_DAY;
826
0
    }
827
0
828
0
    month = rule->getRuleMonth();
829
0
    dom = rule->getRuleDayOfMonth();
830
0
    dow = rule->getRuleDayOfWeek();
831
0
    dtype = rule->getDateRuleType();
832
0
833
0
    if (dshift != 0) {
834
0
        if (dtype == DateTimeRule::DOW) {
835
0
            // Convert to DOW_GEW_DOM or DOW_LEQ_DOM rule first
836
0
            int32_t wim = rule->getRuleWeekInMonth();
837
0
            if (wim > 0) {
838
0
                dtype = DateTimeRule::DOW_GEQ_DOM;
839
0
                dom = 7 * (wim - 1) + 1;
840
0
            } else {
841
0
                dtype = DateTimeRule::DOW_LEQ_DOM;
842
0
                dom = MONTHLENGTH[month] + 7 * (wim + 1);
843
0
            }
844
0
        }
845
0
        // Shift one day before or after
846
0
        dom += dshift;
847
0
        if (dom == 0) {
848
0
            month--;
849
0
            month = month < UCAL_JANUARY ? UCAL_DECEMBER : month;
850
0
            dom = MONTHLENGTH[month];
851
0
        } else if (dom > MONTHLENGTH[month]) {
852
0
            month++;
853
0
            month = month > UCAL_DECEMBER ? UCAL_JANUARY : month;
854
0
            dom = 1;
855
0
        }
856
0
        if (dtype != DateTimeRule::DOM) {
857
0
            // Adjust day of week
858
0
            dow += dshift;
859
0
            if (dow < UCAL_SUNDAY) {
860
0
                dow = UCAL_SATURDAY;
861
0
            } else if (dow > UCAL_SATURDAY) {
862
0
                dow = UCAL_SUNDAY;
863
0
            }
864
0
        }
865
0
    }
866
0
    // Create a new rule
867
0
    DateTimeRule *modifiedRule;
868
0
    if (dtype == DateTimeRule::DOM) {
869
0
        modifiedRule = new DateTimeRule(month, dom, wallt, DateTimeRule::WALL_TIME);
870
0
    } else {
871
0
        modifiedRule = new DateTimeRule(month, dom, dow,
872
0
            (dtype == DateTimeRule::DOW_GEQ_DOM), wallt, DateTimeRule::WALL_TIME);
873
0
    }
874
0
    return modifiedRule;
875
0
}
876
877
/*
878
 * Minumum implementations of stream writer/reader, writing/reading
879
 * UnicodeString.  For now, we do not want to introduce the dependency
880
 * on the ICU I/O stream in this module.  But we want to keep the code
881
 * equivalent to the ICU4J implementation, which utilizes java.io.Writer/
882
 * Reader.
883
 */
884
class VTZWriter {
885
public:
886
    VTZWriter(UnicodeString& out);
887
    ~VTZWriter();
888
889
    void write(const UnicodeString& str);
890
    void write(UChar ch);
891
    void write(const UChar* str);
892
    //void write(const UChar* str, int32_t length);
893
private:
894
    UnicodeString* out;
895
};
896
897
0
VTZWriter::VTZWriter(UnicodeString& output) {
898
0
    out = &output;
899
0
}
900
901
0
VTZWriter::~VTZWriter() {
902
0
}
903
904
void
905
0
VTZWriter::write(const UnicodeString& str) {
906
0
    out->append(str);
907
0
}
908
909
void
910
0
VTZWriter::write(UChar ch) {
911
0
    out->append(ch);
912
0
}
913
914
void
915
0
VTZWriter::write(const UChar* str) {
916
0
    out->append(str, -1);
917
0
}
918
919
/*
920
void
921
VTZWriter::write(const UChar* str, int32_t length) {
922
    out->append(str, length);
923
}
924
*/
925
926
class VTZReader {
927
public:
928
    VTZReader(const UnicodeString& input);
929
    ~VTZReader();
930
931
    UChar read(void);
932
private:
933
    const UnicodeString* in;
934
    int32_t index;
935
};
936
937
0
VTZReader::VTZReader(const UnicodeString& input) {
938
0
    in = &input;
939
0
    index = 0;
940
0
}
941
942
0
VTZReader::~VTZReader() {
943
0
}
944
945
UChar
946
0
VTZReader::read(void) {
947
0
    UChar ch = 0xFFFF;
948
0
    if (index < in->length()) {
949
0
        ch = in->charAt(index);
950
0
    }
951
0
    index++;
952
0
    return ch;
953
0
}
954
955
956
UOBJECT_DEFINE_RTTI_IMPLEMENTATION(VTimeZone)
957
958
VTimeZone::VTimeZone()
959
:   BasicTimeZone(), tz(NULL), vtzlines(NULL),
960
0
    lastmod(MAX_MILLIS) {
961
0
}
962
963
VTimeZone::VTimeZone(const VTimeZone& source)
964
:   BasicTimeZone(source), tz(NULL), vtzlines(NULL),
965
    tzurl(source.tzurl), lastmod(source.lastmod),
966
0
    olsonzid(source.olsonzid), icutzver(source.icutzver) {
967
0
    if (source.tz != NULL) {
968
0
        tz = (BasicTimeZone*)source.tz->clone();
969
0
    }
970
0
    if (source.vtzlines != NULL) {
971
0
        UErrorCode status = U_ZERO_ERROR;
972
0
        int32_t size = source.vtzlines->size();
973
0
        vtzlines = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, size, status);
974
0
        if (U_SUCCESS(status)) {
975
0
            for (int32_t i = 0; i < size; i++) {
976
0
                UnicodeString *line = (UnicodeString*)source.vtzlines->elementAt(i);
977
0
                vtzlines->addElement(line->clone(), status);
978
0
                if (U_FAILURE(status)) {
979
0
                    break;
980
0
                }
981
0
            }
982
0
        }
983
0
        if (U_FAILURE(status) && vtzlines != NULL) {
984
0
            delete vtzlines;
985
0
        }
986
0
    }
987
0
}
988
989
0
VTimeZone::~VTimeZone() {
990
0
    if (tz != NULL) {
991
0
        delete tz;
992
0
    }
993
0
    if (vtzlines != NULL) {
994
0
        delete vtzlines;
995
0
    }
996
0
}
997
998
VTimeZone&
999
0
VTimeZone::operator=(const VTimeZone& right) {
1000
0
    if (this == &right) {
1001
0
        return *this;
1002
0
    }
1003
0
    if (*this != right) {
1004
0
        BasicTimeZone::operator=(right);
1005
0
        if (tz != NULL) {
1006
0
            delete tz;
1007
0
            tz = NULL;
1008
0
        }
1009
0
        if (right.tz != NULL) {
1010
0
            tz = (BasicTimeZone*)right.tz->clone();
1011
0
        }
1012
0
        if (vtzlines != NULL) {
1013
0
            delete vtzlines;
1014
0
        }
1015
0
        if (right.vtzlines != NULL) {
1016
0
            UErrorCode status = U_ZERO_ERROR;
1017
0
            int32_t size = right.vtzlines->size();
1018
0
            vtzlines = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, size, status);
1019
0
            if (U_SUCCESS(status)) {
1020
0
                for (int32_t i = 0; i < size; i++) {
1021
0
                    UnicodeString *line = (UnicodeString*)right.vtzlines->elementAt(i);
1022
0
                    vtzlines->addElement(line->clone(), status);
1023
0
                    if (U_FAILURE(status)) {
1024
0
                        break;
1025
0
                    }
1026
0
                }
1027
0
            }
1028
0
            if (U_FAILURE(status) && vtzlines != NULL) {
1029
0
                delete vtzlines;
1030
0
                vtzlines = NULL;
1031
0
            }
1032
0
        }
1033
0
        tzurl = right.tzurl;
1034
0
        lastmod = right.lastmod;
1035
0
        olsonzid = right.olsonzid;
1036
0
        icutzver = right.icutzver;
1037
0
    }
1038
0
    return *this;
1039
0
}
1040
1041
UBool
1042
0
VTimeZone::operator==(const TimeZone& that) const {
1043
0
    if (this == &that) {
1044
0
        return TRUE;
1045
0
    }
1046
0
    if (typeid(*this) != typeid(that) || !BasicTimeZone::operator==(that)) {
1047
0
        return FALSE;
1048
0
    }
1049
0
    VTimeZone *vtz = (VTimeZone*)&that;
1050
0
    if (*tz == *(vtz->tz)
1051
0
        && tzurl == vtz->tzurl
1052
0
        && lastmod == vtz->lastmod
1053
0
        /* && olsonzid = that.olsonzid */
1054
0
        /* && icutzver = that.icutzver */) {
1055
0
        return TRUE;
1056
0
    }
1057
0
    return FALSE;
1058
0
}
1059
1060
UBool
1061
0
VTimeZone::operator!=(const TimeZone& that) const {
1062
0
    return !operator==(that);
1063
0
}
1064
1065
VTimeZone*
1066
0
VTimeZone::createVTimeZoneByID(const UnicodeString& ID) {
1067
0
    VTimeZone *vtz = new VTimeZone();
1068
0
    vtz->tz = (BasicTimeZone*)TimeZone::createTimeZone(ID);
1069
0
    vtz->tz->getID(vtz->olsonzid);
1070
0
1071
0
    // Set ICU tzdata version
1072
0
    UErrorCode status = U_ZERO_ERROR;
1073
0
    UResourceBundle *bundle = NULL;
1074
0
    const UChar* versionStr = NULL;
1075
0
    int32_t len = 0;
1076
0
    bundle = ures_openDirect(NULL, "zoneinfo64", &status);
1077
0
    versionStr = ures_getStringByKey(bundle, "TZVersion", &len, &status);
1078
0
    if (U_SUCCESS(status)) {
1079
0
        vtz->icutzver.setTo(versionStr, len);
1080
0
    }
1081
0
    ures_close(bundle);
1082
0
    return vtz;
1083
0
}
1084
1085
VTimeZone*
1086
0
VTimeZone::createVTimeZoneFromBasicTimeZone(const BasicTimeZone& basic_time_zone, UErrorCode &status) {
1087
0
    if (U_FAILURE(status)) {
1088
0
        return NULL;
1089
0
    }
1090
0
    VTimeZone *vtz = new VTimeZone();
1091
0
    if (vtz == NULL) {
1092
0
        status = U_MEMORY_ALLOCATION_ERROR;
1093
0
        return NULL;
1094
0
    }
1095
0
    vtz->tz = (BasicTimeZone *)basic_time_zone.clone();
1096
0
    if (vtz->tz == NULL) {
1097
0
        status = U_MEMORY_ALLOCATION_ERROR;
1098
0
        delete vtz;
1099
0
        return NULL;
1100
0
    }
1101
0
    vtz->tz->getID(vtz->olsonzid);
1102
0
1103
0
    // Set ICU tzdata version
1104
0
    UResourceBundle *bundle = NULL;
1105
0
    const UChar* versionStr = NULL;
1106
0
    int32_t len = 0;
1107
0
    bundle = ures_openDirect(NULL, "zoneinfo64", &status);
1108
0
    versionStr = ures_getStringByKey(bundle, "TZVersion", &len, &status);
1109
0
    if (U_SUCCESS(status)) {
1110
0
        vtz->icutzver.setTo(versionStr, len);
1111
0
    }
1112
0
    ures_close(bundle);
1113
0
    return vtz;
1114
0
}
1115
1116
VTimeZone*
1117
0
VTimeZone::createVTimeZone(const UnicodeString& vtzdata, UErrorCode& status) {
1118
0
    if (U_FAILURE(status)) {
1119
0
        return NULL;
1120
0
    }
1121
0
    VTZReader reader(vtzdata);
1122
0
    VTimeZone *vtz = new VTimeZone();
1123
0
    vtz->load(reader, status);
1124
0
    if (U_FAILURE(status)) {
1125
0
        delete vtz;
1126
0
        return NULL;
1127
0
    }
1128
0
    return vtz;
1129
0
}
1130
1131
UBool
1132
0
VTimeZone::getTZURL(UnicodeString& url) const {
1133
0
    if (tzurl.length() > 0) {
1134
0
        url = tzurl;
1135
0
        return TRUE;
1136
0
    }
1137
0
    return FALSE;
1138
0
}
1139
1140
void
1141
0
VTimeZone::setTZURL(const UnicodeString& url) {
1142
0
    tzurl = url;
1143
0
}
1144
1145
UBool
1146
0
VTimeZone::getLastModified(UDate& lastModified) const {
1147
0
    if (lastmod != MAX_MILLIS) {
1148
0
        lastModified = lastmod;
1149
0
        return TRUE;
1150
0
    }
1151
0
    return FALSE;
1152
0
}
1153
1154
void
1155
0
VTimeZone::setLastModified(UDate lastModified) {
1156
0
    lastmod = lastModified;
1157
0
}
1158
1159
void
1160
0
VTimeZone::write(UnicodeString& result, UErrorCode& status) const {
1161
0
    result.remove();
1162
0
    VTZWriter writer(result);
1163
0
    write(writer, status);
1164
0
}
1165
1166
void
1167
0
VTimeZone::write(UDate start, UnicodeString& result, UErrorCode& status) const {
1168
0
    result.remove();
1169
0
    VTZWriter writer(result);
1170
0
    write(start, writer, status);
1171
0
}
1172
1173
void
1174
0
VTimeZone::writeSimple(UDate time, UnicodeString& result, UErrorCode& status) const {
1175
0
    result.remove();
1176
0
    VTZWriter writer(result);
1177
0
    writeSimple(time, writer, status);
1178
0
}
1179
1180
TimeZone*
1181
0
VTimeZone::clone(void) const {
1182
0
    return new VTimeZone(*this);
1183
0
}
1184
1185
int32_t
1186
VTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
1187
0
                     uint8_t dayOfWeek, int32_t millis, UErrorCode& status) const {
1188
0
    return tz->getOffset(era, year, month, day, dayOfWeek, millis, status);
1189
0
}
1190
1191
int32_t
1192
VTimeZone::getOffset(uint8_t era, int32_t year, int32_t month, int32_t day,
1193
                     uint8_t dayOfWeek, int32_t millis,
1194
0
                     int32_t monthLength, UErrorCode& status) const {
1195
0
    return tz->getOffset(era, year, month, day, dayOfWeek, millis, monthLength, status);
1196
0
}
1197
1198
void
1199
VTimeZone::getOffset(UDate date, UBool local, int32_t& rawOffset,
1200
0
                     int32_t& dstOffset, UErrorCode& status) const {
1201
0
    return tz->getOffset(date, local, rawOffset, dstOffset, status);
1202
0
}
1203
1204
void
1205
0
VTimeZone::setRawOffset(int32_t offsetMillis) {
1206
0
    tz->setRawOffset(offsetMillis);
1207
0
}
1208
1209
int32_t
1210
0
VTimeZone::getRawOffset(void) const {
1211
0
    return tz->getRawOffset();
1212
0
}
1213
1214
UBool
1215
0
VTimeZone::useDaylightTime(void) const {
1216
0
    return tz->useDaylightTime();
1217
0
}
1218
1219
UBool
1220
0
VTimeZone::inDaylightTime(UDate date, UErrorCode& status) const {
1221
0
    return tz->inDaylightTime(date, status);
1222
0
}
1223
1224
UBool
1225
0
VTimeZone::hasSameRules(const TimeZone& other) const {
1226
0
    return tz->hasSameRules(other);
1227
0
}
1228
1229
UBool
1230
0
VTimeZone::getNextTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
1231
0
    return tz->getNextTransition(base, inclusive, result);
1232
0
}
1233
1234
UBool
1235
0
VTimeZone::getPreviousTransition(UDate base, UBool inclusive, TimeZoneTransition& result) const {
1236
0
    return tz->getPreviousTransition(base, inclusive, result);
1237
0
}
1238
1239
int32_t
1240
0
VTimeZone::countTransitionRules(UErrorCode& status) const {
1241
0
    return tz->countTransitionRules(status);
1242
0
}
1243
1244
void
1245
VTimeZone::getTimeZoneRules(const InitialTimeZoneRule*& initial,
1246
                            const TimeZoneRule* trsrules[], int32_t& trscount,
1247
0
                            UErrorCode& status) const {
1248
0
    tz->getTimeZoneRules(initial, trsrules, trscount, status);
1249
0
}
1250
1251
void
1252
0
VTimeZone::load(VTZReader& reader, UErrorCode& status) {
1253
0
    vtzlines = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, DEFAULT_VTIMEZONE_LINES, status);
1254
0
    if (U_FAILURE(status)) {
1255
0
        return;
1256
0
    }
1257
0
    UBool eol = FALSE;
1258
0
    UBool start = FALSE;
1259
0
    UBool success = FALSE;
1260
0
    UnicodeString line;
1261
0
1262
0
    while (TRUE) {
1263
0
        UChar ch = reader.read();
1264
0
        if (ch == 0xFFFF) {
1265
0
            // end of file
1266
0
            if (start && line.startsWith(ICAL_END_VTIMEZONE, -1)) {
1267
0
                vtzlines->addElement(new UnicodeString(line), status);
1268
0
                if (U_FAILURE(status)) {
1269
0
                    goto cleanupVtzlines;
1270
0
                }
1271
0
                success = TRUE;
1272
0
            }
1273
0
            break;
1274
0
        }
1275
0
        if (ch == 0x000D) {
1276
0
            // CR, must be followed by LF according to the definition in RFC2445
1277
0
            continue;
1278
0
        }
1279
0
        if (eol) {
1280
0
            if (ch != 0x0009 && ch != 0x0020) {
1281
0
                // NOT followed by TAB/SP -> new line
1282
0
                if (start) {
1283
0
                    if (line.length() > 0) {
1284
0
                        vtzlines->addElement(new UnicodeString(line), status);
1285
0
                        if (U_FAILURE(status)) {
1286
0
                            goto cleanupVtzlines;
1287
0
                        }
1288
0
                    }
1289
0
                }
1290
0
                line.remove();
1291
0
                if (ch != 0x000A) {
1292
0
                    line.append(ch);
1293
0
                }
1294
0
            }
1295
0
            eol = FALSE;
1296
0
        } else {
1297
0
            if (ch == 0x000A) {
1298
0
                // LF
1299
0
                eol = TRUE;
1300
0
                if (start) {
1301
0
                    if (line.startsWith(ICAL_END_VTIMEZONE, -1)) {
1302
0
                        vtzlines->addElement(new UnicodeString(line), status);
1303
0
                        if (U_FAILURE(status)) {
1304
0
                            goto cleanupVtzlines;
1305
0
                        }
1306
0
                        success = TRUE;
1307
0
                        break;
1308
0
                    }
1309
0
                } else {
1310
0
                    if (line.startsWith(ICAL_BEGIN_VTIMEZONE, -1)) {
1311
0
                        vtzlines->addElement(new UnicodeString(line), status);
1312
0
                        if (U_FAILURE(status)) {
1313
0
                            goto cleanupVtzlines;
1314
0
                        }
1315
0
                        line.remove();
1316
0
                        start = TRUE;
1317
0
                        eol = FALSE;
1318
0
                    }
1319
0
                }
1320
0
            } else {
1321
0
                line.append(ch);
1322
0
            }
1323
0
        }
1324
0
    }
1325
0
    if (!success) {
1326
0
        if (U_SUCCESS(status)) {
1327
0
            status = U_INVALID_STATE_ERROR;
1328
0
        }
1329
0
        goto cleanupVtzlines;
1330
0
    }
1331
0
    parse(status);
1332
0
    return;
1333
0
1334
0
cleanupVtzlines:
1335
0
    delete vtzlines;
1336
0
    vtzlines = NULL;
1337
0
}
1338
1339
// parser state
1340
0
#define INI 0   // Initial state
1341
0
#define VTZ 1   // In VTIMEZONE
1342
0
#define TZI 2   // In STANDARD or DAYLIGHT
1343
1344
0
#define DEF_DSTSAVINGS (60*60*1000)
1345
0
#define DEF_TZSTARTTIME (0.0)
1346
1347
void
1348
0
VTimeZone::parse(UErrorCode& status) {
1349
0
    if (U_FAILURE(status)) {
1350
0
        return;
1351
0
    }
1352
0
    if (vtzlines == NULL || vtzlines->size() == 0) {
1353
0
        status = U_INVALID_STATE_ERROR;
1354
0
        return;
1355
0
    }
1356
0
    InitialTimeZoneRule *initialRule = NULL;
1357
0
    RuleBasedTimeZone *rbtz = NULL;
1358
0
1359
0
    // timezone ID
1360
0
    UnicodeString tzid;
1361
0
1362
0
    int32_t state = INI;
1363
0
    int32_t n = 0;
1364
0
    UBool dst = FALSE;      // current zone type
1365
0
    UnicodeString from;     // current zone from offset
1366
0
    UnicodeString to;       // current zone offset
1367
0
    UnicodeString zonename;   // current zone name
1368
0
    UnicodeString dtstart;  // current zone starts
1369
0
    UBool isRRULE = FALSE;  // true if the rule is described by RRULE
1370
0
    int32_t initialRawOffset = 0;   // initial offset
1371
0
    int32_t initialDSTSavings = 0;  // initial offset
1372
0
    UDate firstStart = MAX_MILLIS;  // the earliest rule start time
1373
0
    UnicodeString name;     // RFC2445 prop name
1374
0
    UnicodeString value;    // RFC2445 prop value
1375
0
1376
0
    UVector *dates = NULL;  // list of RDATE or RRULE strings
1377
0
    UVector *rules = NULL;  // list of TimeZoneRule instances
1378
0
1379
0
    int32_t finalRuleIdx = -1;
1380
0
    int32_t finalRuleCount = 0;
1381
0
1382
0
    rules = new UVector(status);
1383
0
    if (U_FAILURE(status)) {
1384
0
        goto cleanupParse;
1385
0
    }
1386
0
     // Set the deleter to remove TimeZoneRule vectors to avoid memory leaks due to unowned TimeZoneRules.
1387
0
    rules->setDeleter(deleteTimeZoneRule);
1388
0
    
1389
0
    dates = new UVector(uprv_deleteUObject, uhash_compareUnicodeString, status);
1390
0
    if (U_FAILURE(status)) {
1391
0
        goto cleanupParse;
1392
0
    }
1393
0
    if (rules == NULL || dates == NULL) {
1394
0
        status = U_MEMORY_ALLOCATION_ERROR;
1395
0
        goto cleanupParse;
1396
0
    }
1397
0
1398
0
    for (n = 0; n < vtzlines->size(); n++) {
1399
0
        UnicodeString *line = (UnicodeString*)vtzlines->elementAt(n);
1400
0
        int32_t valueSep = line->indexOf(COLON);
1401
0
        if (valueSep < 0) {
1402
0
            continue;
1403
0
        }
1404
0
        name.setTo(*line, 0, valueSep);
1405
0
        value.setTo(*line, valueSep + 1);
1406
0
1407
0
        switch (state) {
1408
0
        case INI:
1409
0
            if (name.compare(ICAL_BEGIN, -1) == 0
1410
0
                && value.compare(ICAL_VTIMEZONE, -1) == 0) {
1411
0
                state = VTZ;
1412
0
            }
1413
0
            break;
1414
0
1415
0
        case VTZ:
1416
0
            if (name.compare(ICAL_TZID, -1) == 0) {
1417
0
                tzid = value;
1418
0
            } else if (name.compare(ICAL_TZURL, -1) == 0) {
1419
0
                tzurl = value;
1420
0
            } else if (name.compare(ICAL_LASTMOD, -1) == 0) {
1421
0
                // Always in 'Z' format, so the offset argument for the parse method
1422
0
                // can be any value.
1423
0
                lastmod = parseDateTimeString(value, 0, status);
1424
0
                if (U_FAILURE(status)) {
1425
0
                    goto cleanupParse;
1426
0
                }
1427
0
            } else if (name.compare(ICAL_BEGIN, -1) == 0) {
1428
0
                UBool isDST = (value.compare(ICAL_DAYLIGHT, -1) == 0);
1429
0
                if (value.compare(ICAL_STANDARD, -1) == 0 || isDST) {
1430
0
                    // tzid must be ready at this point
1431
0
                    if (tzid.length() == 0) {
1432
0
                        goto cleanupParse;
1433
0
                    }
1434
0
                    // initialize current zone properties
1435
0
                    if (dates->size() != 0) {
1436
0
                        dates->removeAllElements();
1437
0
                    }
1438
0
                    isRRULE = FALSE;
1439
0
                    from.remove();
1440
0
                    to.remove();
1441
0
                    zonename.remove();
1442
0
                    dst = isDST;
1443
0
                    state = TZI;
1444
0
                } else {
1445
0
                    // BEGIN property other than STANDARD/DAYLIGHT
1446
0
                    // must not be there.
1447
0
                    goto cleanupParse;
1448
0
                }
1449
0
            } else if (name.compare(ICAL_END, -1) == 0) {
1450
0
                break;
1451
0
            }
1452
0
            break;
1453
0
        case TZI:
1454
0
            if (name.compare(ICAL_DTSTART, -1) == 0) {
1455
0
                dtstart = value;
1456
0
            } else if (name.compare(ICAL_TZNAME, -1) == 0) {
1457
0
                zonename = value;
1458
0
            } else if (name.compare(ICAL_TZOFFSETFROM, -1) == 0) {
1459
0
                from = value;
1460
0
            } else if (name.compare(ICAL_TZOFFSETTO, -1) == 0) {
1461
0
                to = value;
1462
0
            } else if (name.compare(ICAL_RDATE, -1) == 0) {
1463
0
                // RDATE mixed with RRULE is not supported
1464
0
                if (isRRULE) {
1465
0
                    goto cleanupParse;
1466
0
                }
1467
0
                // RDATE value may contain multiple date delimited
1468
0
                // by comma
1469
0
                UBool nextDate = TRUE;
1470
0
                int32_t dstart = 0;
1471
0
                UnicodeString *dstr;
1472
0
                while (nextDate) {
1473
0
                    int32_t dend = value.indexOf(COMMA, dstart);
1474
0
                    if (dend == -1) {
1475
0
                        dstr = new UnicodeString(value, dstart);
1476
0
                        nextDate = FALSE;
1477
0
                    } else {
1478
0
                        dstr = new UnicodeString(value, dstart, dend - dstart);
1479
0
                    }
1480
0
                    dates->addElement(dstr, status);
1481
0
                    if (U_FAILURE(status)) {
1482
0
                        goto cleanupParse;
1483
0
                    }
1484
0
                    dstart = dend + 1;
1485
0
                }
1486
0
            } else if (name.compare(ICAL_RRULE, -1) == 0) {
1487
0
                // RRULE mixed with RDATE is not supported
1488
0
                if (!isRRULE && dates->size() != 0) {
1489
0
                    goto cleanupParse;
1490
0
                }
1491
0
                isRRULE = true;
1492
0
                dates->addElement(new UnicodeString(value), status);
1493
0
                if (U_FAILURE(status)) {
1494
0
                    goto cleanupParse;
1495
0
                }
1496
0
            } else if (name.compare(ICAL_END, -1) == 0) {
1497
0
                // Mandatory properties
1498
0
                if (dtstart.length() == 0 || from.length() == 0 || to.length() == 0) {
1499
0
                    goto cleanupParse;
1500
0
                }
1501
0
                // if zonename is not available, create one from tzid
1502
0
                if (zonename.length() == 0) {
1503
0
                    getDefaultTZName(tzid, dst, zonename);
1504
0
                }
1505
0
1506
0
                // create a time zone rule
1507
0
                TimeZoneRule *rule = NULL;
1508
0
                int32_t fromOffset = 0;
1509
0
                int32_t toOffset = 0;
1510
0
                int32_t rawOffset = 0;
1511
0
                int32_t dstSavings = 0;
1512
0
                UDate start = 0;
1513
0
1514
0
                // Parse TZOFFSETFROM/TZOFFSETTO
1515
0
                fromOffset = offsetStrToMillis(from, status);
1516
0
                toOffset = offsetStrToMillis(to, status);
1517
0
                if (U_FAILURE(status)) {
1518
0
                    goto cleanupParse;
1519
0
                }
1520
0
1521
0
                if (dst) {
1522
0
                    // If daylight, use the previous offset as rawoffset if positive
1523
0
                    if (toOffset - fromOffset > 0) {
1524
0
                        rawOffset = fromOffset;
1525
0
                        dstSavings = toOffset - fromOffset;
1526
0
                    } else {
1527
0
                        // This is rare case..  just use 1 hour DST savings
1528
0
                        rawOffset = toOffset - DEF_DSTSAVINGS;
1529
0
                        dstSavings = DEF_DSTSAVINGS;                                
1530
0
                    }
1531
0
                } else {
1532
0
                    rawOffset = toOffset;
1533
0
                    dstSavings = 0;
1534
0
                }
1535
0
1536
0
                // start time
1537
0
                start = parseDateTimeString(dtstart, fromOffset, status);
1538
0
                if (U_FAILURE(status)) {
1539
0
                    goto cleanupParse;
1540
0
                }
1541
0
1542
0
                // Create the rule
1543
0
                UDate actualStart = MAX_MILLIS;
1544
0
                if (isRRULE) {
1545
0
                    rule = createRuleByRRULE(zonename, rawOffset, dstSavings, start, dates, fromOffset, status);
1546
0
                } else {
1547
0
                    rule = createRuleByRDATE(zonename, rawOffset, dstSavings, start, dates, fromOffset, status);
1548
0
                }
1549
0
                if (U_FAILURE(status) || rule == NULL) {
1550
0
                    goto cleanupParse;
1551
0
                } else {
1552
0
                    UBool startAvail = rule->getFirstStart(fromOffset, 0, actualStart);
1553
0
                    if (startAvail && actualStart < firstStart) {
1554
0
                        // save from offset information for the earliest rule
1555
0
                        firstStart = actualStart;
1556
0
                        // If this is STD, assume the time before this transtion
1557
0
                        // is DST when the difference is 1 hour.  This might not be
1558
0
                        // accurate, but VTIMEZONE data does not have such info.
1559
0
                        if (dstSavings > 0) {
1560
0
                            initialRawOffset = fromOffset;
1561
0
                            initialDSTSavings = 0;
1562
0
                        } else {
1563
0
                            if (fromOffset - toOffset == DEF_DSTSAVINGS) {
1564
0
                                initialRawOffset = fromOffset - DEF_DSTSAVINGS;
1565
0
                                initialDSTSavings = DEF_DSTSAVINGS;
1566
0
                            } else {
1567
0
                                initialRawOffset = fromOffset;
1568
0
                                initialDSTSavings = 0;
1569
0
                            }
1570
0
                        }
1571
0
                    }
1572
0
                }
1573
0
                rules->addElement(rule, status);
1574
0
                if (U_FAILURE(status)) {
1575
0
                    goto cleanupParse;
1576
0
                }
1577
0
                state = VTZ;
1578
0
            }
1579
0
            break;
1580
0
        }
1581
0
    }
1582
0
    // Must have at least one rule
1583
0
    if (rules->size() == 0) {
1584
0
        goto cleanupParse;
1585
0
    }
1586
0
1587
0
    // Create a initial rule
1588
0
    getDefaultTZName(tzid, FALSE, zonename);
1589
0
    initialRule = new InitialTimeZoneRule(zonename,
1590
0
        initialRawOffset, initialDSTSavings);
1591
0
    if (initialRule == NULL) {
1592
0
        status = U_MEMORY_ALLOCATION_ERROR;
1593
0
        goto cleanupParse;
1594
0
    }
1595
0
1596
0
    // Finally, create the RuleBasedTimeZone
1597
0
    rbtz = new RuleBasedTimeZone(tzid, initialRule);
1598
0
    if (rbtz == NULL) {
1599
0
        status = U_MEMORY_ALLOCATION_ERROR;
1600
0
        goto cleanupParse;
1601
0
    }
1602
0
    initialRule = NULL; // already adopted by RBTZ, no need to delete
1603
0
1604
0
    for (n = 0; n < rules->size(); n++) {
1605
0
        TimeZoneRule *r = (TimeZoneRule*)rules->elementAt(n);
1606
0
        AnnualTimeZoneRule *atzrule = dynamic_cast<AnnualTimeZoneRule *>(r);
1607
0
        if (atzrule != NULL) {
1608
0
            if (atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR) {
1609
0
                finalRuleCount++;
1610
0
                finalRuleIdx = n;
1611
0
            }
1612
0
        }
1613
0
    }
1614
0
    if (finalRuleCount > 2) {
1615
0
        // Too many final rules
1616
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
1617
0
        goto cleanupParse;
1618
0
    }
1619
0
1620
0
    if (finalRuleCount == 1) {
1621
0
        if (rules->size() == 1) {
1622
0
            // Only one final rule, only governs the initial rule,
1623
0
            // which is already initialized, thus, we do not need to
1624
0
            // add this transition rule
1625
0
            rules->removeAllElements();
1626
0
        } else {
1627
0
            // Normalize the final rule
1628
0
            AnnualTimeZoneRule *finalRule = (AnnualTimeZoneRule*)rules->elementAt(finalRuleIdx);
1629
0
            int32_t tmpRaw = finalRule->getRawOffset();
1630
0
            int32_t tmpDST = finalRule->getDSTSavings();
1631
0
1632
0
            // Find the last non-final rule
1633
0
            UDate finalStart, start;
1634
0
            finalRule->getFirstStart(initialRawOffset, initialDSTSavings, finalStart);
1635
0
            start = finalStart;
1636
0
            for (n = 0; n < rules->size(); n++) {
1637
0
                if (finalRuleIdx == n) {
1638
0
                    continue;
1639
0
                }
1640
0
                TimeZoneRule *r = (TimeZoneRule*)rules->elementAt(n);
1641
0
                UDate lastStart;
1642
0
                r->getFinalStart(tmpRaw, tmpDST, lastStart);
1643
0
                if (lastStart > start) {
1644
0
                    finalRule->getNextStart(lastStart,
1645
0
                        r->getRawOffset(),
1646
0
                        r->getDSTSavings(),
1647
0
                        FALSE,
1648
0
                        start);
1649
0
                }
1650
0
            }
1651
0
1652
0
            TimeZoneRule *newRule;
1653
0
            UnicodeString tznam;
1654
0
            if (start == finalStart) {
1655
0
                // Transform this into a single transition
1656
0
                newRule = new TimeArrayTimeZoneRule(
1657
0
                        finalRule->getName(tznam),
1658
0
                        finalRule->getRawOffset(),
1659
0
                        finalRule->getDSTSavings(),
1660
0
                        &finalStart,
1661
0
                        1,
1662
0
                        DateTimeRule::UTC_TIME);
1663
0
            } else {
1664
0
                // Update the end year
1665
0
                int32_t y, m, d, dow, doy, mid;
1666
0
                Grego::timeToFields(start, y, m, d, dow, doy, mid);
1667
0
                newRule = new AnnualTimeZoneRule(
1668
0
                        finalRule->getName(tznam),
1669
0
                        finalRule->getRawOffset(),
1670
0
                        finalRule->getDSTSavings(),
1671
0
                        *(finalRule->getRule()),
1672
0
                        finalRule->getStartYear(),
1673
0
                        y);
1674
0
            }
1675
0
            if (newRule == NULL) {
1676
0
                status = U_MEMORY_ALLOCATION_ERROR;
1677
0
                goto cleanupParse;
1678
0
            }
1679
0
            rules->removeElementAt(finalRuleIdx);
1680
0
            rules->addElement(newRule, status);
1681
0
            if (U_FAILURE(status)) {
1682
0
                delete newRule;
1683
0
                goto cleanupParse;
1684
0
            }
1685
0
        }
1686
0
    }
1687
0
1688
0
    while (!rules->isEmpty()) {
1689
0
        TimeZoneRule *tzr = (TimeZoneRule*)rules->orphanElementAt(0);
1690
0
        rbtz->addTransitionRule(tzr, status);
1691
0
        if (U_FAILURE(status)) {
1692
0
            goto cleanupParse;
1693
0
        }
1694
0
    }
1695
0
    rbtz->complete(status);
1696
0
    if (U_FAILURE(status)) {
1697
0
        goto cleanupParse;
1698
0
    }
1699
0
    delete rules;
1700
0
    delete dates;
1701
0
1702
0
    tz = rbtz;
1703
0
    setID(tzid);
1704
0
    return;
1705
0
1706
0
cleanupParse:
1707
0
    if (rules != NULL) {
1708
0
        while (!rules->isEmpty()) {
1709
0
            TimeZoneRule *r = (TimeZoneRule*)rules->orphanElementAt(0);
1710
0
            delete r;
1711
0
        }
1712
0
        delete rules;
1713
0
    }
1714
0
    if (dates != NULL) {
1715
0
        delete dates;
1716
0
    }
1717
0
    if (initialRule != NULL) {
1718
0
        delete initialRule;
1719
0
    }
1720
0
    if (rbtz != NULL) {
1721
0
        delete rbtz;
1722
0
    }
1723
0
    return;
1724
0
}
1725
1726
void
1727
0
VTimeZone::write(VTZWriter& writer, UErrorCode& status) const {
1728
0
    if (vtzlines != NULL) {
1729
0
        for (int32_t i = 0; i < vtzlines->size(); i++) {
1730
0
            UnicodeString *line = (UnicodeString*)vtzlines->elementAt(i);
1731
0
            if (line->startsWith(ICAL_TZURL, -1)
1732
0
                && line->charAt(u_strlen(ICAL_TZURL)) == COLON) {
1733
0
                writer.write(ICAL_TZURL);
1734
0
                writer.write(COLON);
1735
0
                writer.write(tzurl);
1736
0
                writer.write(ICAL_NEWLINE);
1737
0
            } else if (line->startsWith(ICAL_LASTMOD, -1)
1738
0
                && line->charAt(u_strlen(ICAL_LASTMOD)) == COLON) {
1739
0
                UnicodeString utcString;
1740
0
                writer.write(ICAL_LASTMOD);
1741
0
                writer.write(COLON);
1742
0
                writer.write(getUTCDateTimeString(lastmod, utcString));
1743
0
                writer.write(ICAL_NEWLINE);
1744
0
            } else {
1745
0
                writer.write(*line);
1746
0
                writer.write(ICAL_NEWLINE);
1747
0
            }
1748
0
        }
1749
0
    } else {
1750
0
        UnicodeString icutzprop;
1751
0
        UVector customProps(nullptr, uhash_compareUnicodeString, status);
1752
0
        if (olsonzid.length() > 0 && icutzver.length() > 0) {
1753
0
            icutzprop.append(olsonzid);
1754
0
            icutzprop.append(u'[');
1755
0
            icutzprop.append(icutzver);
1756
0
            icutzprop.append(u']');
1757
0
            customProps.addElement(&icutzprop, status);
1758
0
        }
1759
0
        writeZone(writer, *tz, &customProps, status);
1760
0
    }
1761
0
}
1762
1763
void
1764
0
VTimeZone::write(UDate start, VTZWriter& writer, UErrorCode& status) const {
1765
0
    if (U_FAILURE(status)) {
1766
0
        return;
1767
0
    }
1768
0
    InitialTimeZoneRule *initial = NULL;
1769
0
    UVector *transitionRules = NULL;
1770
0
    UVector customProps(uprv_deleteUObject, uhash_compareUnicodeString, status);
1771
0
    UnicodeString tzid;
1772
0
1773
0
    // Extract rules applicable to dates after the start time
1774
0
    getTimeZoneRulesAfter(start, initial, transitionRules, status);
1775
0
    if (U_FAILURE(status)) {
1776
0
        return;
1777
0
    }
1778
0
1779
0
    // Create a RuleBasedTimeZone with the subset rule
1780
0
    getID(tzid);
1781
0
    RuleBasedTimeZone rbtz(tzid, initial);
1782
0
    if (transitionRules != NULL) {
1783
0
        while (!transitionRules->isEmpty()) {
1784
0
            TimeZoneRule *tr = (TimeZoneRule*)transitionRules->orphanElementAt(0);
1785
0
            rbtz.addTransitionRule(tr, status);
1786
0
            if (U_FAILURE(status)) {
1787
0
                goto cleanupWritePartial;
1788
0
            }
1789
0
        }
1790
0
        delete transitionRules;
1791
0
        transitionRules = NULL;
1792
0
    }
1793
0
    rbtz.complete(status);
1794
0
    if (U_FAILURE(status)) {
1795
0
        goto cleanupWritePartial;
1796
0
    }
1797
0
1798
0
    if (olsonzid.length() > 0 && icutzver.length() > 0) {
1799
0
        UnicodeString *icutzprop = new UnicodeString(ICU_TZINFO_PROP);
1800
0
        icutzprop->append(olsonzid);
1801
0
        icutzprop->append((UChar)0x005B/*'['*/);
1802
0
        icutzprop->append(icutzver);
1803
0
        icutzprop->append(ICU_TZINFO_PARTIAL, -1);
1804
0
        appendMillis(start, *icutzprop);
1805
0
        icutzprop->append((UChar)0x005D/*']'*/);
1806
0
        customProps.addElement(icutzprop, status);
1807
0
        if (U_FAILURE(status)) {
1808
0
            delete icutzprop;
1809
0
            goto cleanupWritePartial;
1810
0
        }
1811
0
    }
1812
0
    writeZone(writer, rbtz, &customProps, status);
1813
0
    return;
1814
0
1815
0
cleanupWritePartial:
1816
0
    if (initial != NULL) {
1817
0
        delete initial;
1818
0
    }
1819
0
    if (transitionRules != NULL) {
1820
0
        while (!transitionRules->isEmpty()) {
1821
0
            TimeZoneRule *tr = (TimeZoneRule*)transitionRules->orphanElementAt(0);
1822
0
            delete tr;
1823
0
        }
1824
0
        delete transitionRules;
1825
0
    }
1826
0
}
1827
1828
void
1829
0
VTimeZone::writeSimple(UDate time, VTZWriter& writer, UErrorCode& status) const {
1830
0
    if (U_FAILURE(status)) {
1831
0
        return;
1832
0
    }
1833
0
1834
0
    UVector customProps(uprv_deleteUObject, uhash_compareUnicodeString, status);
1835
0
    UnicodeString tzid;
1836
0
1837
0
    // Extract simple rules
1838
0
    InitialTimeZoneRule *initial = NULL;
1839
0
    AnnualTimeZoneRule *std = NULL, *dst = NULL;
1840
0
    getSimpleRulesNear(time, initial, std, dst, status);
1841
0
    if (U_SUCCESS(status)) {
1842
0
        // Create a RuleBasedTimeZone with the subset rule
1843
0
        getID(tzid);
1844
0
        RuleBasedTimeZone rbtz(tzid, initial);
1845
0
        if (std != NULL && dst != NULL) {
1846
0
            rbtz.addTransitionRule(std, status);
1847
0
            rbtz.addTransitionRule(dst, status);
1848
0
        }
1849
0
        if (U_FAILURE(status)) {
1850
0
            goto cleanupWriteSimple;
1851
0
        }
1852
0
1853
0
        if (olsonzid.length() > 0 && icutzver.length() > 0) {
1854
0
            UnicodeString *icutzprop = new UnicodeString(ICU_TZINFO_PROP);
1855
0
            icutzprop->append(olsonzid);
1856
0
            icutzprop->append((UChar)0x005B/*'['*/);
1857
0
            icutzprop->append(icutzver);
1858
0
            icutzprop->append(ICU_TZINFO_SIMPLE, -1);
1859
0
            appendMillis(time, *icutzprop);
1860
0
            icutzprop->append((UChar)0x005D/*']'*/);
1861
0
            customProps.addElement(icutzprop, status);
1862
0
            if (U_FAILURE(status)) {
1863
0
                delete icutzprop;
1864
0
                goto cleanupWriteSimple;
1865
0
            }
1866
0
        }
1867
0
        writeZone(writer, rbtz, &customProps, status);
1868
0
    }
1869
0
    return;
1870
0
1871
0
cleanupWriteSimple:
1872
0
    if (initial != NULL) {
1873
0
        delete initial;
1874
0
    }
1875
0
    if (std != NULL) {
1876
0
        delete std;
1877
0
    }
1878
0
    if (dst != NULL) {
1879
0
        delete dst;
1880
0
    }
1881
0
}
1882
1883
void
1884
VTimeZone::writeZone(VTZWriter& w, BasicTimeZone& basictz,
1885
0
                     UVector* customProps, UErrorCode& status) const {
1886
0
    if (U_FAILURE(status)) {
1887
0
        return;
1888
0
    }
1889
0
    writeHeaders(w, status);
1890
0
    if (U_FAILURE(status)) {
1891
0
        return;
1892
0
    }
1893
0
1894
0
    if (customProps != NULL) {
1895
0
        for (int32_t i = 0; i < customProps->size(); i++) {
1896
0
            UnicodeString *custprop = (UnicodeString*)customProps->elementAt(i);
1897
0
            w.write(*custprop);
1898
0
            w.write(ICAL_NEWLINE);
1899
0
        }
1900
0
    }
1901
0
1902
0
    UDate t = MIN_MILLIS;
1903
0
    UnicodeString dstName;
1904
0
    int32_t dstFromOffset = 0;
1905
0
    int32_t dstFromDSTSavings = 0;
1906
0
    int32_t dstToOffset = 0;
1907
0
    int32_t dstStartYear = 0;
1908
0
    int32_t dstMonth = 0;
1909
0
    int32_t dstDayOfWeek = 0;
1910
0
    int32_t dstWeekInMonth = 0;
1911
0
    int32_t dstMillisInDay = 0;
1912
0
    UDate dstStartTime = 0.0;
1913
0
    UDate dstUntilTime = 0.0;
1914
0
    int32_t dstCount = 0;
1915
0
    AnnualTimeZoneRule *finalDstRule = NULL;
1916
0
1917
0
    UnicodeString stdName;
1918
0
    int32_t stdFromOffset = 0;
1919
0
    int32_t stdFromDSTSavings = 0;
1920
0
    int32_t stdToOffset = 0;
1921
0
    int32_t stdStartYear = 0;
1922
0
    int32_t stdMonth = 0;
1923
0
    int32_t stdDayOfWeek = 0;
1924
0
    int32_t stdWeekInMonth = 0;
1925
0
    int32_t stdMillisInDay = 0;
1926
0
    UDate stdStartTime = 0.0;
1927
0
    UDate stdUntilTime = 0.0;
1928
0
    int32_t stdCount = 0;
1929
0
    AnnualTimeZoneRule *finalStdRule = NULL;
1930
0
1931
0
    int32_t year, month, dom, dow, doy, mid;
1932
0
    UBool hasTransitions = FALSE;
1933
0
    TimeZoneTransition tzt;
1934
0
    UBool tztAvail;
1935
0
    UnicodeString name;
1936
0
    UBool isDst;
1937
0
1938
0
    // Going through all transitions
1939
0
    while (TRUE) {
1940
0
        tztAvail = basictz.getNextTransition(t, FALSE, tzt);
1941
0
        if (!tztAvail) {
1942
0
            break;
1943
0
        }
1944
0
        hasTransitions = TRUE;
1945
0
        t = tzt.getTime();
1946
0
        tzt.getTo()->getName(name);
1947
0
        isDst = (tzt.getTo()->getDSTSavings() != 0);
1948
0
        int32_t fromOffset = tzt.getFrom()->getRawOffset() + tzt.getFrom()->getDSTSavings();
1949
0
        int32_t fromDSTSavings = tzt.getFrom()->getDSTSavings();
1950
0
        int32_t toOffset = tzt.getTo()->getRawOffset() + tzt.getTo()->getDSTSavings();
1951
0
        Grego::timeToFields(tzt.getTime() + fromOffset, year, month, dom, dow, doy, mid);
1952
0
        int32_t weekInMonth = Grego::dayOfWeekInMonth(year, month, dom);
1953
0
        UBool sameRule = FALSE;
1954
0
        const AnnualTimeZoneRule *atzrule;
1955
0
        if (isDst) {
1956
0
            if (finalDstRule == NULL
1957
0
                && (atzrule = dynamic_cast<const AnnualTimeZoneRule *>(tzt.getTo())) != NULL
1958
0
                && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR
1959
0
            ) {
1960
0
                finalDstRule = (AnnualTimeZoneRule*)tzt.getTo()->clone();
1961
0
            }
1962
0
            if (dstCount > 0) {
1963
0
                if (year == dstStartYear + dstCount
1964
0
                        && name.compare(dstName) == 0
1965
0
                        && dstFromOffset == fromOffset
1966
0
                        && dstToOffset == toOffset
1967
0
                        && dstMonth == month
1968
0
                        && dstDayOfWeek == dow
1969
0
                        && dstWeekInMonth == weekInMonth
1970
0
                        && dstMillisInDay == mid) {
1971
0
                    // Update until time
1972
0
                    dstUntilTime = t;
1973
0
                    dstCount++;
1974
0
                    sameRule = TRUE;
1975
0
                }
1976
0
                if (!sameRule) {
1977
0
                    if (dstCount == 1) {
1978
0
                        writeZonePropsByTime(w, TRUE, dstName, dstFromOffset, dstToOffset, dstStartTime,
1979
0
                                TRUE, status);
1980
0
                    } else {
1981
0
                        writeZonePropsByDOW(w, TRUE, dstName, dstFromOffset, dstToOffset,
1982
0
                                dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime, status);
1983
0
                    }
1984
0
                    if (U_FAILURE(status)) {
1985
0
                        goto cleanupWriteZone;
1986
0
                    }
1987
0
                }
1988
0
            } 
1989
0
            if (!sameRule) {
1990
0
                // Reset this DST information
1991
0
                dstName = name;
1992
0
                dstFromOffset = fromOffset;
1993
0
                dstFromDSTSavings = fromDSTSavings;
1994
0
                dstToOffset = toOffset;
1995
0
                dstStartYear = year;
1996
0
                dstMonth = month;
1997
0
                dstDayOfWeek = dow;
1998
0
                dstWeekInMonth = weekInMonth;
1999
0
                dstMillisInDay = mid;
2000
0
                dstStartTime = dstUntilTime = t;
2001
0
                dstCount = 1;
2002
0
            }
2003
0
            if (finalStdRule != NULL && finalDstRule != NULL) {
2004
0
                break;
2005
0
            }
2006
0
        } else {
2007
0
            if (finalStdRule == NULL
2008
0
                && (atzrule = dynamic_cast<const AnnualTimeZoneRule *>(tzt.getTo())) != NULL
2009
0
                && atzrule->getEndYear() == AnnualTimeZoneRule::MAX_YEAR
2010
0
            ) {
2011
0
                finalStdRule = (AnnualTimeZoneRule*)tzt.getTo()->clone();
2012
0
            }
2013
0
            if (stdCount > 0) {
2014
0
                if (year == stdStartYear + stdCount
2015
0
                        && name.compare(stdName) == 0
2016
0
                        && stdFromOffset == fromOffset
2017
0
                        && stdToOffset == toOffset
2018
0
                        && stdMonth == month
2019
0
                        && stdDayOfWeek == dow
2020
0
                        && stdWeekInMonth == weekInMonth
2021
0
                        && stdMillisInDay == mid) {
2022
0
                    // Update until time
2023
0
                    stdUntilTime = t;
2024
0
                    stdCount++;
2025
0
                    sameRule = TRUE;
2026
0
                }
2027
0
                if (!sameRule) {
2028
0
                    if (stdCount == 1) {
2029
0
                        writeZonePropsByTime(w, FALSE, stdName, stdFromOffset, stdToOffset, stdStartTime,
2030
0
                                TRUE, status);
2031
0
                    } else {
2032
0
                        writeZonePropsByDOW(w, FALSE, stdName, stdFromOffset, stdToOffset,
2033
0
                                stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime, status);
2034
0
                    }
2035
0
                    if (U_FAILURE(status)) {
2036
0
                        goto cleanupWriteZone;
2037
0
                    }
2038
0
                }
2039
0
            }
2040
0
            if (!sameRule) {
2041
0
                // Reset this STD information
2042
0
                stdName = name;
2043
0
                stdFromOffset = fromOffset;
2044
0
                stdFromDSTSavings = fromDSTSavings;
2045
0
                stdToOffset = toOffset;
2046
0
                stdStartYear = year;
2047
0
                stdMonth = month;
2048
0
                stdDayOfWeek = dow;
2049
0
                stdWeekInMonth = weekInMonth;
2050
0
                stdMillisInDay = mid;
2051
0
                stdStartTime = stdUntilTime = t;
2052
0
                stdCount = 1;
2053
0
            }
2054
0
            if (finalStdRule != NULL && finalDstRule != NULL) {
2055
0
                break;
2056
0
            }
2057
0
        }
2058
0
    }
2059
0
    if (!hasTransitions) {
2060
0
        // No transition - put a single non transition RDATE
2061
0
        int32_t raw, dst, offset;
2062
0
        basictz.getOffset(0.0/*any time*/, FALSE, raw, dst, status);
2063
0
        if (U_FAILURE(status)) {
2064
0
            goto cleanupWriteZone;
2065
0
        }
2066
0
        offset = raw + dst;
2067
0
        isDst = (dst != 0);
2068
0
        UnicodeString tzid;
2069
0
        basictz.getID(tzid);
2070
0
        getDefaultTZName(tzid, isDst, name);        
2071
0
        writeZonePropsByTime(w, isDst, name,
2072
0
                offset, offset, DEF_TZSTARTTIME - offset, FALSE, status);    
2073
0
        if (U_FAILURE(status)) {
2074
0
            goto cleanupWriteZone;
2075
0
        }
2076
0
    } else {
2077
0
        if (dstCount > 0) {
2078
0
            if (finalDstRule == NULL) {
2079
0
                if (dstCount == 1) {
2080
0
                    writeZonePropsByTime(w, TRUE, dstName, dstFromOffset, dstToOffset, dstStartTime,
2081
0
                            TRUE, status);
2082
0
                } else {
2083
0
                    writeZonePropsByDOW(w, TRUE, dstName, dstFromOffset, dstToOffset,
2084
0
                            dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime, status);
2085
0
                }
2086
0
                if (U_FAILURE(status)) {
2087
0
                    goto cleanupWriteZone;
2088
0
                }
2089
0
            } else {
2090
0
                if (dstCount == 1) {
2091
0
                    writeFinalRule(w, TRUE, finalDstRule,
2092
0
                            dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, dstStartTime, status);
2093
0
                } else {
2094
0
                    // Use a single rule if possible
2095
0
                    if (isEquivalentDateRule(dstMonth, dstWeekInMonth, dstDayOfWeek, finalDstRule->getRule())) {
2096
0
                        writeZonePropsByDOW(w, TRUE, dstName, dstFromOffset, dstToOffset,
2097
0
                                dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, MAX_MILLIS, status);
2098
0
                    } else {
2099
0
                        // Not equivalent rule - write out two different rules
2100
0
                        writeZonePropsByDOW(w, TRUE, dstName, dstFromOffset, dstToOffset,
2101
0
                                dstMonth, dstWeekInMonth, dstDayOfWeek, dstStartTime, dstUntilTime, status);
2102
0
                        if (U_FAILURE(status)) {
2103
0
                            goto cleanupWriteZone;
2104
0
                        }
2105
0
                        UDate nextStart;
2106
0
                        UBool nextStartAvail = finalDstRule->getNextStart(dstUntilTime, dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, false, nextStart);
2107
0
                        U_ASSERT(nextStartAvail);
2108
0
                        if (nextStartAvail) {
2109
0
                            writeFinalRule(w, TRUE, finalDstRule,
2110
0
                                    dstFromOffset - dstFromDSTSavings, dstFromDSTSavings, nextStart, status);
2111
0
                        }
2112
0
                    }
2113
0
                }
2114
0
                if (U_FAILURE(status)) {
2115
0
                    goto cleanupWriteZone;
2116
0
                }
2117
0
            }
2118
0
        }
2119
0
        if (stdCount > 0) {
2120
0
            if (finalStdRule == NULL) {
2121
0
                if (stdCount == 1) {
2122
0
                    writeZonePropsByTime(w, FALSE, stdName, stdFromOffset, stdToOffset, stdStartTime,
2123
0
                            TRUE, status);
2124
0
                } else {
2125
0
                    writeZonePropsByDOW(w, FALSE, stdName, stdFromOffset, stdToOffset,
2126
0
                            stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime, status);
2127
0
                }
2128
0
                if (U_FAILURE(status)) {
2129
0
                    goto cleanupWriteZone;
2130
0
                }
2131
0
            } else {
2132
0
                if (stdCount == 1) {
2133
0
                    writeFinalRule(w, FALSE, finalStdRule,
2134
0
                            stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, stdStartTime, status);
2135
0
                } else {
2136
0
                    // Use a single rule if possible
2137
0
                    if (isEquivalentDateRule(stdMonth, stdWeekInMonth, stdDayOfWeek, finalStdRule->getRule())) {
2138
0
                        writeZonePropsByDOW(w, FALSE, stdName, stdFromOffset, stdToOffset,
2139
0
                                stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, MAX_MILLIS, status);
2140
0
                    } else {
2141
0
                        // Not equivalent rule - write out two different rules
2142
0
                        writeZonePropsByDOW(w, FALSE, stdName, stdFromOffset, stdToOffset,
2143
0
                                stdMonth, stdWeekInMonth, stdDayOfWeek, stdStartTime, stdUntilTime, status);
2144
0
                        if (U_FAILURE(status)) {
2145
0
                            goto cleanupWriteZone;
2146
0
                        }
2147
0
                        UDate nextStart;
2148
0
                        UBool nextStartAvail = finalStdRule->getNextStart(stdUntilTime, stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, false, nextStart);
2149
0
                        U_ASSERT(nextStartAvail);
2150
0
                        if (nextStartAvail) {
2151
0
                            writeFinalRule(w, FALSE, finalStdRule,
2152
0
                                    stdFromOffset - stdFromDSTSavings, stdFromDSTSavings, nextStart, status);
2153
0
                        }
2154
0
                    }
2155
0
                }
2156
0
                if (U_FAILURE(status)) {
2157
0
                    goto cleanupWriteZone;
2158
0
                }
2159
0
            }
2160
0
        }            
2161
0
    }
2162
0
    writeFooter(w, status);
2163
0
2164
0
cleanupWriteZone:
2165
0
2166
0
    if (finalStdRule != NULL) {
2167
0
        delete finalStdRule;
2168
0
    }
2169
0
    if (finalDstRule != NULL) {
2170
0
        delete finalDstRule;
2171
0
    }
2172
0
}
2173
2174
void
2175
0
VTimeZone::writeHeaders(VTZWriter& writer, UErrorCode& status) const {
2176
0
    if (U_FAILURE(status)) {
2177
0
        return;
2178
0
    }
2179
0
    UnicodeString tzid;
2180
0
    tz->getID(tzid);
2181
0
2182
0
    writer.write(ICAL_BEGIN);
2183
0
    writer.write(COLON);
2184
0
    writer.write(ICAL_VTIMEZONE);
2185
0
    writer.write(ICAL_NEWLINE);
2186
0
    writer.write(ICAL_TZID);
2187
0
    writer.write(COLON);
2188
0
    writer.write(tzid);
2189
0
    writer.write(ICAL_NEWLINE);
2190
0
    if (tzurl.length() != 0) {
2191
0
        writer.write(ICAL_TZURL);
2192
0
        writer.write(COLON);
2193
0
        writer.write(tzurl);
2194
0
        writer.write(ICAL_NEWLINE);
2195
0
    }
2196
0
    if (lastmod != MAX_MILLIS) {
2197
0
        UnicodeString lastmodStr;
2198
0
        writer.write(ICAL_LASTMOD);
2199
0
        writer.write(COLON);
2200
0
        writer.write(getUTCDateTimeString(lastmod, lastmodStr));
2201
0
        writer.write(ICAL_NEWLINE);
2202
0
    }
2203
0
}
2204
2205
/*
2206
 * Write the closing section of the VTIMEZONE definition block
2207
 */
2208
void
2209
0
VTimeZone::writeFooter(VTZWriter& writer, UErrorCode& status) const {
2210
0
    if (U_FAILURE(status)) {
2211
0
        return;
2212
0
    }
2213
0
    writer.write(ICAL_END);
2214
0
    writer.write(COLON);
2215
0
    writer.write(ICAL_VTIMEZONE);
2216
0
    writer.write(ICAL_NEWLINE);
2217
0
}
2218
2219
/*
2220
 * Write a single start time
2221
 */
2222
void
2223
VTimeZone::writeZonePropsByTime(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
2224
                                int32_t fromOffset, int32_t toOffset, UDate time, UBool withRDATE,
2225
0
                                UErrorCode& status) const {
2226
0
    if (U_FAILURE(status)) {
2227
0
        return;
2228
0
    }
2229
0
    beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, time, status);
2230
0
    if (U_FAILURE(status)) {
2231
0
        return;
2232
0
    }
2233
0
    if (withRDATE) {
2234
0
        writer.write(ICAL_RDATE);
2235
0
        writer.write(COLON);
2236
0
        UnicodeString timestr;
2237
0
        writer.write(getDateTimeString(time + fromOffset, timestr));
2238
0
        writer.write(ICAL_NEWLINE);
2239
0
    }
2240
0
    endZoneProps(writer, isDst, status);
2241
0
    if (U_FAILURE(status)) {
2242
0
        return;
2243
0
    }
2244
0
}
2245
2246
/*
2247
 * Write start times defined by a DOM rule using VTIMEZONE RRULE
2248
 */
2249
void
2250
VTimeZone::writeZonePropsByDOM(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
2251
                               int32_t fromOffset, int32_t toOffset,
2252
                               int32_t month, int32_t dayOfMonth, UDate startTime, UDate untilTime,
2253
0
                               UErrorCode& status) const {
2254
0
    if (U_FAILURE(status)) {
2255
0
        return;
2256
0
    }
2257
0
    beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, startTime, status);
2258
0
    if (U_FAILURE(status)) {
2259
0
        return;
2260
0
    }
2261
0
    beginRRULE(writer, month, status);
2262
0
    if (U_FAILURE(status)) {
2263
0
        return;
2264
0
    }
2265
0
    writer.write(ICAL_BYMONTHDAY);
2266
0
    writer.write(EQUALS_SIGN);
2267
0
    UnicodeString dstr;
2268
0
    appendAsciiDigits(dayOfMonth, 0, dstr);
2269
0
    writer.write(dstr);
2270
0
    if (untilTime != MAX_MILLIS) {
2271
0
        appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status);
2272
0
        if (U_FAILURE(status)) {
2273
0
            return;
2274
0
        }
2275
0
    }
2276
0
    writer.write(ICAL_NEWLINE);
2277
0
    endZoneProps(writer, isDst, status);
2278
0
}
2279
2280
/*
2281
 * Write start times defined by a DOW rule using VTIMEZONE RRULE
2282
 */
2283
void
2284
VTimeZone::writeZonePropsByDOW(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
2285
                               int32_t fromOffset, int32_t toOffset,
2286
                               int32_t month, int32_t weekInMonth, int32_t dayOfWeek,
2287
0
                               UDate startTime, UDate untilTime, UErrorCode& status) const {
2288
0
    if (U_FAILURE(status)) {
2289
0
        return;
2290
0
    }
2291
0
    beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, startTime, status);
2292
0
    if (U_FAILURE(status)) {
2293
0
        return;
2294
0
    }
2295
0
    beginRRULE(writer, month, status);
2296
0
    if (U_FAILURE(status)) {
2297
0
        return;
2298
0
    }
2299
0
    writer.write(ICAL_BYDAY);
2300
0
    writer.write(EQUALS_SIGN);
2301
0
    UnicodeString dstr;
2302
0
    appendAsciiDigits(weekInMonth, 0, dstr);
2303
0
    writer.write(dstr);    // -4, -3, -2, -1, 1, 2, 3, 4
2304
0
    writer.write(ICAL_DOW_NAMES[dayOfWeek - 1]);    // SU, MO, TU...
2305
0
2306
0
    if (untilTime != MAX_MILLIS) {
2307
0
        appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status);
2308
0
        if (U_FAILURE(status)) {
2309
0
            return;
2310
0
        }
2311
0
    }
2312
0
    writer.write(ICAL_NEWLINE);
2313
0
    endZoneProps(writer, isDst, status);
2314
0
}
2315
2316
/*
2317
 * Write start times defined by a DOW_GEQ_DOM rule using VTIMEZONE RRULE
2318
 */
2319
void
2320
VTimeZone::writeZonePropsByDOW_GEQ_DOM(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
2321
                                       int32_t fromOffset, int32_t toOffset,
2322
                                       int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
2323
0
                                       UDate startTime, UDate untilTime, UErrorCode& status) const {
2324
0
    if (U_FAILURE(status)) {
2325
0
        return;
2326
0
    }
2327
0
    // Check if this rule can be converted to DOW rule
2328
0
    if (dayOfMonth%7 == 1) {
2329
0
        // Can be represented by DOW rule
2330
0
        writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
2331
0
                month, (dayOfMonth + 6)/7, dayOfWeek, startTime, untilTime, status);
2332
0
        if (U_FAILURE(status)) {
2333
0
            return;
2334
0
        }
2335
0
    } else if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - dayOfMonth)%7 == 6) {
2336
0
        // Can be represented by DOW rule with negative week number
2337
0
        writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
2338
0
                month, -1*((MONTHLENGTH[month] - dayOfMonth + 1)/7), dayOfWeek, startTime, untilTime, status);
2339
0
        if (U_FAILURE(status)) {
2340
0
            return;
2341
0
        }
2342
0
    } else {
2343
0
        // Otherwise, use BYMONTHDAY to include all possible dates
2344
0
        beginZoneProps(writer, isDst, zonename, fromOffset, toOffset, startTime, status);
2345
0
        if (U_FAILURE(status)) {
2346
0
            return;
2347
0
        }
2348
0
        // Check if all days are in the same month
2349
0
        int32_t startDay = dayOfMonth;
2350
0
        int32_t currentMonthDays = 7;
2351
0
    
2352
0
        if (dayOfMonth <= 0) {
2353
0
            // The start day is in previous month
2354
0
            int32_t prevMonthDays = 1 - dayOfMonth;
2355
0
            currentMonthDays -= prevMonthDays;
2356
0
2357
0
            int32_t prevMonth = (month - 1) < 0 ? 11 : month - 1;
2358
0
2359
0
            // Note: When a rule is separated into two, UNTIL attribute needs to be
2360
0
            // calculated for each of them.  For now, we skip this, because we basically use this method
2361
0
            // only for final rules, which does not have the UNTIL attribute
2362
0
            writeZonePropsByDOW_GEQ_DOM_sub(writer, prevMonth, -prevMonthDays, dayOfWeek, prevMonthDays,
2363
0
                MAX_MILLIS /* Do not use UNTIL */, fromOffset, status);
2364
0
            if (U_FAILURE(status)) {
2365
0
                return;
2366
0
            }
2367
0
2368
0
            // Start from 1 for the rest
2369
0
            startDay = 1;
2370
0
        } else if (dayOfMonth + 6 > MONTHLENGTH[month]) {
2371
0
            // Note: This code does not actually work well in February.  For now, days in month in
2372
0
            // non-leap year.
2373
0
            int32_t nextMonthDays = dayOfMonth + 6 - MONTHLENGTH[month];
2374
0
            currentMonthDays -= nextMonthDays;
2375
0
2376
0
            int32_t nextMonth = (month + 1) > 11 ? 0 : month + 1;
2377
0
            
2378
0
            writeZonePropsByDOW_GEQ_DOM_sub(writer, nextMonth, 1, dayOfWeek, nextMonthDays,
2379
0
                MAX_MILLIS /* Do not use UNTIL */, fromOffset, status);
2380
0
            if (U_FAILURE(status)) {
2381
0
                return;
2382
0
            }
2383
0
        }
2384
0
        writeZonePropsByDOW_GEQ_DOM_sub(writer, month, startDay, dayOfWeek, currentMonthDays,
2385
0
            untilTime, fromOffset, status);
2386
0
        if (U_FAILURE(status)) {
2387
0
            return;
2388
0
        }
2389
0
        endZoneProps(writer, isDst, status);
2390
0
    }
2391
0
}
2392
2393
/*
2394
 * Called from writeZonePropsByDOW_GEQ_DOM
2395
 */
2396
void
2397
VTimeZone::writeZonePropsByDOW_GEQ_DOM_sub(VTZWriter& writer, int32_t month, int32_t dayOfMonth,
2398
                                           int32_t dayOfWeek, int32_t numDays,
2399
0
                                           UDate untilTime, int32_t fromOffset, UErrorCode& status) const {
2400
0
2401
0
    if (U_FAILURE(status)) {
2402
0
        return;
2403
0
    }
2404
0
    int32_t startDayNum = dayOfMonth;
2405
0
    UBool isFeb = (month == UCAL_FEBRUARY);
2406
0
    if (dayOfMonth < 0 && !isFeb) {
2407
0
        // Use positive number if possible
2408
0
        startDayNum = MONTHLENGTH[month] + dayOfMonth + 1;
2409
0
    }
2410
0
    beginRRULE(writer, month, status);
2411
0
    if (U_FAILURE(status)) {
2412
0
        return;
2413
0
    }
2414
0
    writer.write(ICAL_BYDAY);
2415
0
    writer.write(EQUALS_SIGN);
2416
0
    writer.write(ICAL_DOW_NAMES[dayOfWeek - 1]);    // SU, MO, TU...
2417
0
    writer.write(SEMICOLON);
2418
0
    writer.write(ICAL_BYMONTHDAY);
2419
0
    writer.write(EQUALS_SIGN);
2420
0
2421
0
    UnicodeString dstr;
2422
0
    appendAsciiDigits(startDayNum, 0, dstr);
2423
0
    writer.write(dstr);
2424
0
    for (int32_t i = 1; i < numDays; i++) {
2425
0
        writer.write(COMMA);
2426
0
        dstr.remove();
2427
0
        appendAsciiDigits(startDayNum + i, 0, dstr);
2428
0
        writer.write(dstr);
2429
0
    }
2430
0
2431
0
    if (untilTime != MAX_MILLIS) {
2432
0
        appendUNTIL(writer, getDateTimeString(untilTime + fromOffset, dstr), status);
2433
0
        if (U_FAILURE(status)) {
2434
0
            return;
2435
0
        }
2436
0
    }
2437
0
    writer.write(ICAL_NEWLINE);
2438
0
}
2439
2440
/*
2441
 * Write start times defined by a DOW_LEQ_DOM rule using VTIMEZONE RRULE
2442
 */
2443
void
2444
VTimeZone::writeZonePropsByDOW_LEQ_DOM(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
2445
                                       int32_t fromOffset, int32_t toOffset,
2446
                                       int32_t month, int32_t dayOfMonth, int32_t dayOfWeek,
2447
0
                                       UDate startTime, UDate untilTime, UErrorCode& status) const {
2448
0
    if (U_FAILURE(status)) {
2449
0
        return;
2450
0
    }
2451
0
    // Check if this rule can be converted to DOW rule
2452
0
    if (dayOfMonth%7 == 0) {
2453
0
        // Can be represented by DOW rule
2454
0
        writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
2455
0
                month, dayOfMonth/7, dayOfWeek, startTime, untilTime, status);
2456
0
    } else if (month != UCAL_FEBRUARY && (MONTHLENGTH[month] - dayOfMonth)%7 == 0){
2457
0
        // Can be represented by DOW rule with negative week number
2458
0
        writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
2459
0
                month, -1*((MONTHLENGTH[month] - dayOfMonth)/7 + 1), dayOfWeek, startTime, untilTime, status);
2460
0
    } else if (month == UCAL_FEBRUARY && dayOfMonth == 29) {
2461
0
        // Specical case for February
2462
0
        writeZonePropsByDOW(writer, isDst, zonename, fromOffset, toOffset,
2463
0
                UCAL_FEBRUARY, -1, dayOfWeek, startTime, untilTime, status);
2464
0
    } else {
2465
0
        // Otherwise, convert this to DOW_GEQ_DOM rule
2466
0
        writeZonePropsByDOW_GEQ_DOM(writer, isDst, zonename, fromOffset, toOffset,
2467
0
                month, dayOfMonth - 6, dayOfWeek, startTime, untilTime, status);
2468
0
    }
2469
0
}
2470
2471
/*
2472
 * Write the final time zone rule using RRULE, with no UNTIL attribute
2473
 */
2474
void
2475
VTimeZone::writeFinalRule(VTZWriter& writer, UBool isDst, const AnnualTimeZoneRule* rule,
2476
                          int32_t fromRawOffset, int32_t fromDSTSavings,
2477
0
                          UDate startTime, UErrorCode& status) const {
2478
0
    if (U_FAILURE(status)) {
2479
0
        return;
2480
0
    }
2481
0
    UBool modifiedRule = TRUE;
2482
0
    const DateTimeRule *dtrule = toWallTimeRule(rule->getRule(), fromRawOffset, fromDSTSavings);
2483
0
    if (dtrule == NULL) {
2484
0
        modifiedRule = FALSE;
2485
0
        dtrule = rule->getRule();
2486
0
    }
2487
0
2488
0
    // If the rule's mills in a day is out of range, adjust start time.
2489
0
    // Olson tzdata supports 24:00 of a day, but VTIMEZONE does not.
2490
0
    // See ticket#7008/#7518
2491
0
2492
0
    int32_t timeInDay = dtrule->getRuleMillisInDay();
2493
0
    if (timeInDay < 0) {
2494
0
        startTime = startTime + (0 - timeInDay);
2495
0
    } else if (timeInDay >= U_MILLIS_PER_DAY) {
2496
0
        startTime = startTime - (timeInDay - (U_MILLIS_PER_DAY - 1));
2497
0
    }
2498
0
2499
0
    int32_t toOffset = rule->getRawOffset() + rule->getDSTSavings();
2500
0
    UnicodeString name;
2501
0
    rule->getName(name);
2502
0
    switch (dtrule->getDateRuleType()) {
2503
0
    case DateTimeRule::DOM:
2504
0
        writeZonePropsByDOM(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset,
2505
0
                dtrule->getRuleMonth(), dtrule->getRuleDayOfMonth(), startTime, MAX_MILLIS, status);
2506
0
        break;
2507
0
    case DateTimeRule::DOW:
2508
0
        writeZonePropsByDOW(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset,
2509
0
                dtrule->getRuleMonth(), dtrule->getRuleWeekInMonth(), dtrule->getRuleDayOfWeek(), startTime, MAX_MILLIS, status);
2510
0
        break;
2511
0
    case DateTimeRule::DOW_GEQ_DOM:
2512
0
        writeZonePropsByDOW_GEQ_DOM(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset,
2513
0
                dtrule->getRuleMonth(), dtrule->getRuleDayOfMonth(), dtrule->getRuleDayOfWeek(), startTime, MAX_MILLIS, status);
2514
0
        break;
2515
0
    case DateTimeRule::DOW_LEQ_DOM:
2516
0
        writeZonePropsByDOW_LEQ_DOM(writer, isDst, name, fromRawOffset + fromDSTSavings, toOffset,
2517
0
                dtrule->getRuleMonth(), dtrule->getRuleDayOfMonth(), dtrule->getRuleDayOfWeek(), startTime, MAX_MILLIS, status);
2518
0
        break;
2519
0
    }
2520
0
    if (modifiedRule) {
2521
0
        delete dtrule;
2522
0
    }
2523
0
}
2524
2525
/*
2526
 * Write the opening section of zone properties
2527
 */
2528
void
2529
VTimeZone::beginZoneProps(VTZWriter& writer, UBool isDst, const UnicodeString& zonename,
2530
0
                          int32_t fromOffset, int32_t toOffset, UDate startTime, UErrorCode& status) const {
2531
0
    if (U_FAILURE(status)) {
2532
0
        return;
2533
0
    }
2534
0
    writer.write(ICAL_BEGIN);
2535
0
    writer.write(COLON);
2536
0
    if (isDst) {
2537
0
        writer.write(ICAL_DAYLIGHT);
2538
0
    } else {
2539
0
        writer.write(ICAL_STANDARD);
2540
0
    }
2541
0
    writer.write(ICAL_NEWLINE);
2542
0
2543
0
    UnicodeString dstr;
2544
0
2545
0
    // TZOFFSETTO
2546
0
    writer.write(ICAL_TZOFFSETTO);
2547
0
    writer.write(COLON);
2548
0
    millisToOffset(toOffset, dstr);
2549
0
    writer.write(dstr);
2550
0
    writer.write(ICAL_NEWLINE);
2551
0
2552
0
    // TZOFFSETFROM
2553
0
    writer.write(ICAL_TZOFFSETFROM);
2554
0
    writer.write(COLON);
2555
0
    millisToOffset(fromOffset, dstr);
2556
0
    writer.write(dstr);
2557
0
    writer.write(ICAL_NEWLINE);
2558
0
2559
0
    // TZNAME
2560
0
    writer.write(ICAL_TZNAME);
2561
0
    writer.write(COLON);
2562
0
    writer.write(zonename);
2563
0
    writer.write(ICAL_NEWLINE);
2564
0
    
2565
0
    // DTSTART
2566
0
    writer.write(ICAL_DTSTART);
2567
0
    writer.write(COLON);
2568
0
    writer.write(getDateTimeString(startTime + fromOffset, dstr));
2569
0
    writer.write(ICAL_NEWLINE);        
2570
0
}
2571
2572
/*
2573
 * Writes the closing section of zone properties
2574
 */
2575
void
2576
0
VTimeZone::endZoneProps(VTZWriter& writer, UBool isDst, UErrorCode& status) const {
2577
0
    if (U_FAILURE(status)) {
2578
0
        return;
2579
0
    }
2580
0
    // END:STANDARD or END:DAYLIGHT
2581
0
    writer.write(ICAL_END);
2582
0
    writer.write(COLON);
2583
0
    if (isDst) {
2584
0
        writer.write(ICAL_DAYLIGHT);
2585
0
    } else {
2586
0
        writer.write(ICAL_STANDARD);
2587
0
    }
2588
0
    writer.write(ICAL_NEWLINE);
2589
0
}
2590
2591
/*
2592
 * Write the beggining part of RRULE line
2593
 */
2594
void
2595
0
VTimeZone::beginRRULE(VTZWriter& writer, int32_t month, UErrorCode& status) const {
2596
0
    if (U_FAILURE(status)) {
2597
0
        return;
2598
0
    }
2599
0
    UnicodeString dstr;
2600
0
    writer.write(ICAL_RRULE);
2601
0
    writer.write(COLON);
2602
0
    writer.write(ICAL_FREQ);
2603
0
    writer.write(EQUALS_SIGN);
2604
0
    writer.write(ICAL_YEARLY);
2605
0
    writer.write(SEMICOLON);
2606
0
    writer.write(ICAL_BYMONTH);
2607
0
    writer.write(EQUALS_SIGN);
2608
0
    appendAsciiDigits(month + 1, 0, dstr);
2609
0
    writer.write(dstr);
2610
0
    writer.write(SEMICOLON);
2611
0
}
2612
2613
/*
2614
 * Append the UNTIL attribute after RRULE line
2615
 */
2616
void
2617
0
VTimeZone::appendUNTIL(VTZWriter& writer, const UnicodeString& until,  UErrorCode& status) const {
2618
0
    if (U_FAILURE(status)) {
2619
0
        return;
2620
0
    }
2621
0
    if (until.length() > 0) {
2622
0
        writer.write(SEMICOLON);
2623
0
        writer.write(ICAL_UNTIL);
2624
0
        writer.write(EQUALS_SIGN);
2625
0
        writer.write(until);
2626
0
    }
2627
0
}
2628
2629
U_NAMESPACE_END
2630
2631
#endif /* #if !UCONFIG_NO_FORMATTING */
2632
2633
//eof