+typedef struct {
+ const char* zone;
+ const CalFields base;
+ int32_t deltaDays;
+ UCalendarWallTimeOption skippedWTOpt;
+ const CalFields expected;
+} TestAddAcrossZoneTransitionData;
+
+static const TestAddAcrossZoneTransitionData AAZTDATA[] =
+{
+ // Time zone Base wall time day(s) Skipped time options
+ // Expected wall time
+
+ // Add 1 day, from the date before DST transition
+ {"America/Los_Angeles", CalFields(2014,3,8,1,59,59,999), 1, UCAL_WALLTIME_FIRST,
+ CalFields(2014,3,9,1,59,59,999)},
+
+ {"America/Los_Angeles", CalFields(2014,3,8,1,59,59,999), 1, UCAL_WALLTIME_LAST,
+ CalFields(2014,3,9,1,59,59,999)},
+
+ {"America/Los_Angeles", CalFields(2014,3,8,1,59,59,999), 1, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2014,3,9,1,59,59,999)},
+
+
+ {"America/Los_Angeles", CalFields(2014,3,8,2,0,0,0), 1, UCAL_WALLTIME_FIRST,
+ CalFields(2014,3,9,1,0,0,0)},
+
+ {"America/Los_Angeles", CalFields(2014,3,8,2,0,0,0), 1, UCAL_WALLTIME_LAST,
+ CalFields(2014,3,9,3,0,0,0)},
+
+ {"America/Los_Angeles", CalFields(2014,3,8,2,0,0,0), 1, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2014,3,9,3,0,0,0)},
+
+
+ {"America/Los_Angeles", CalFields(2014,3,8,2,30,0,0), 1, UCAL_WALLTIME_FIRST,
+ CalFields(2014,3,9,1,30,0,0)},
+
+ {"America/Los_Angeles", CalFields(2014,3,8,2,30,0,0), 1, UCAL_WALLTIME_LAST,
+ CalFields(2014,3,9,3,30,0,0)},
+
+ {"America/Los_Angeles", CalFields(2014,3,8,2,30,0,0), 1, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2014,3,9,3,0,0,0)},
+
+
+ {"America/Los_Angeles", CalFields(2014,3,8,3,0,0,0), 1, UCAL_WALLTIME_FIRST,
+ CalFields(2014,3,9,3,0,0,0)},
+
+ {"America/Los_Angeles", CalFields(2014,3,8,3,0,0,0), 1, UCAL_WALLTIME_LAST,
+ CalFields(2014,3,9,3,0,0,0)},
+
+ {"America/Los_Angeles", CalFields(2014,3,8,3,0,0,0), 1, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2014,3,9,3,0,0,0)},
+
+ // Subtract 1 day, from one day after DST transition
+ {"America/Los_Angeles", CalFields(2014,3,10,1,59,59,999), -1, UCAL_WALLTIME_FIRST,
+ CalFields(2014,3,9,1,59,59,999)},
+
+ {"America/Los_Angeles", CalFields(2014,3,10,1,59,59,999), -1, UCAL_WALLTIME_LAST,
+ CalFields(2014,3,9,1,59,59,999)},
+
+ {"America/Los_Angeles", CalFields(2014,3,10,1,59,59,999), -1, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2014,3,9,1,59,59,999)},
+
+
+ {"America/Los_Angeles", CalFields(2014,3,10,2,0,0,0), -1, UCAL_WALLTIME_FIRST,
+ CalFields(2014,3,9,1,0,0,0)},
+
+ {"America/Los_Angeles", CalFields(2014,3,10,2,0,0,0), -1, UCAL_WALLTIME_LAST,
+ CalFields(2014,3,9,3,0,0,0)},
+
+ {"America/Los_Angeles", CalFields(2014,3,10,2,0,0,0), -1, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2014,3,9,3,0,0,0)},
+
+
+ {"America/Los_Angeles", CalFields(2014,3,10,2,30,0,0), -1, UCAL_WALLTIME_FIRST,
+ CalFields(2014,3,9,1,30,0,0)},
+
+ {"America/Los_Angeles", CalFields(2014,3,10,2,30,0,0), -1, UCAL_WALLTIME_LAST,
+ CalFields(2014,3,9,3,30,0,0)},
+
+ {"America/Los_Angeles", CalFields(2014,3,10,2,30,0,0), -1, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2014,3,9,3,0,0,0)},
+
+
+ {"America/Los_Angeles", CalFields(2014,3,10,3,0,0,0), -1, UCAL_WALLTIME_FIRST,
+ CalFields(2014,3,9,3,0,0,0)},
+
+ {"America/Los_Angeles", CalFields(2014,3,10,3,0,0,0), -1, UCAL_WALLTIME_LAST,
+ CalFields(2014,3,9,3,0,0,0)},
+
+ {"America/Los_Angeles", CalFields(2014,3,10,3,0,0,0), -1, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2014,3,9,3,0,0,0)},
+
+
+ // Test case for ticket#10544
+ {"America/Santiago", CalFields(2013,4,27,0,0,0,0), 134, UCAL_WALLTIME_FIRST,
+ CalFields(2013,9,7,23,0,0,0)},
+
+ {"America/Santiago", CalFields(2013,4,27,0,0,0,0), 134, UCAL_WALLTIME_LAST,
+ CalFields(2013,9,8,1,0,0,0)},
+
+ {"America/Santiago", CalFields(2013,4,27,0,0,0,0), 134, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2013,9,8,1,0,0,0)},
+
+
+ {"America/Santiago", CalFields(2013,4,27,0,30,0,0), 134, UCAL_WALLTIME_FIRST,
+ CalFields(2013,9,7,23,30,0,0)},
+
+ {"America/Santiago", CalFields(2013,4,27,0,30,0,0), 134, UCAL_WALLTIME_LAST,
+ CalFields(2013,9,8,1,30,0,0)},
+
+ {"America/Santiago", CalFields(2013,4,27,0,30,0,0), 134, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2013,9,8,1,0,0,0)},
+
+
+ // Extreme transition - Pacific/Apia completely skips 2011-12-30
+ {"Pacific/Apia", CalFields(2011,12,29,0,0,0,0), 1, UCAL_WALLTIME_FIRST,
+ CalFields(2011,12,31,0,0,0,0)},
+
+ {"Pacific/Apia", CalFields(2011,12,29,0,0,0,0), 1, UCAL_WALLTIME_LAST,
+ CalFields(2011,12,31,0,0,0,0)},
+
+ {"Pacific/Apia", CalFields(2011,12,29,0,0,0,0), 1, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2011,12,31,0,0,0,0)},
+
+
+ {"Pacific/Apia", CalFields(2011,12,31,12,0,0,0), -1, UCAL_WALLTIME_FIRST,
+ CalFields(2011,12,29,12,0,0,0)},
+
+ {"Pacific/Apia", CalFields(2011,12,31,12,0,0,0), -1, UCAL_WALLTIME_LAST,
+ CalFields(2011,12,29,12,0,0,0)},
+
+ {"Pacific/Apia", CalFields(2011,12,31,12,0,0,0), -1, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2011,12,29,12,0,0,0)},
+
+
+ // 30 minutes DST - Australia/Lord_Howe
+ {"Australia/Lord_Howe", CalFields(2013,10,5,2,15,0,0), 1, UCAL_WALLTIME_FIRST,
+ CalFields(2013,10,6,1,45,0,0)},
+
+ {"Australia/Lord_Howe", CalFields(2013,10,5,2,15,0,0), 1, UCAL_WALLTIME_LAST,
+ CalFields(2013,10,6,2,45,0,0)},
+
+ {"Australia/Lord_Howe", CalFields(2013,10,5,2,15,0,0), 1, UCAL_WALLTIME_NEXT_VALID,
+ CalFields(2013,10,6,2,30,0,0)},
+
+ {NULL, CalFields(0,0,0,0,0,0,0), 0, UCAL_WALLTIME_LAST, CalFields(0,0,0,0,0,0,0)}
+};
+
+void CalendarTest::TestAddAcrossZoneTransition() {
+ UErrorCode status = U_ZERO_ERROR;
+ GregorianCalendar cal(status);
+ TEST_CHECK_STATUS;
+
+ for (int32_t i = 0; AAZTDATA[i].zone; i++) {
+ status = U_ZERO_ERROR;
+ TimeZone *tz = TimeZone::createTimeZone(AAZTDATA[i].zone);
+ cal.adoptTimeZone(tz);
+ cal.setSkippedWallTimeOption(AAZTDATA[i].skippedWTOpt);
+ AAZTDATA[i].base.setTo(cal);
+ cal.add(UCAL_DATE, AAZTDATA[i].deltaDays, status);
+ TEST_CHECK_STATUS;
+
+ if (!AAZTDATA[i].expected.isEquivalentTo(cal, status)) {
+ CalFields res(cal, status);
+ TEST_CHECK_STATUS;
+ char buf[32];
+ const char *optDisp = AAZTDATA[i].skippedWTOpt == UCAL_WALLTIME_FIRST ? "FIRST" :
+ AAZTDATA[i].skippedWTOpt == UCAL_WALLTIME_LAST ? "LAST" : "NEXT_VALID";
+ errln(UnicodeString("Error: base:") + AAZTDATA[i].base.toString(buf, sizeof(buf)) + ", tz:" + AAZTDATA[i].zone
+ + ", delta:" + AAZTDATA[i].deltaDays + " day(s), opt:" + optDisp
+ + ", result:" + res.toString(buf, sizeof(buf))
+ + " - expected:" + AAZTDATA[i].expected.toString(buf, sizeof(buf)));
+ }
+ }
+}
+