+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
/***********************************************************************
* COPYRIGHT:
- * Copyright (c) 1997-2004, International Business Machines Corporation
+ * Copyright (c) 1997-2015, International Business Machines Corporation
* and others. All Rights Reserved.
***********************************************************************/
#include "unicode/gregocal.h"
#include "dtfmtrtts.h"
#include "caltest.h"
+#include "cstring.h"
#include <stdio.h>
#include <string.h>
#endif
// Define this to test just a single locale
-//#define TEST_ONE_LOC "ja_JP_TRADITIONAL"
+//#define TEST_ONE_LOC "cs_CZ"
// If SPARSENESS is > 0, we don't run each exhaustive possibility.
// There are 24 total possible tests per each locale. A SPARSENESS
optionv = (par && *par=='v');
switch (index) {
CASE(0,TestDateFormatRoundTrip)
+ CASE(1, TestCentury)
default: name = ""; break;
}
}
return FALSE;
}
+void DateFormatRoundTripTest::TestCentury()
+{
+ UErrorCode status = U_ZERO_ERROR;
+ Locale locale("es_PA");
+ UnicodeString pattern = "MM/dd/yy hh:mm:ss a z";
+ SimpleDateFormat fmt(pattern, locale, status);
+ if (U_FAILURE(status)) {
+ dataerrln("Fail: construct SimpleDateFormat: %s", u_errorName(status));
+ return;
+ }
+ UDate date[] = {-55018555891590.05, 0, 0};
+ UnicodeString result[2];
+
+ fmt.format(date[0], result[0]);
+ date[1] = fmt.parse(result[0], status);
+ fmt.format(date[1], result[1]);
+ date[2] = fmt.parse(result[1], status);
+
+ /* This test case worked OK by accident before. date[1] != date[0],
+ * because we use -80/+20 year window for 2-digit year parsing.
+ * (date[0] is in year 1926, date[1] is in year 2026.) result[1] set
+ * by the first format call returns "07/13/26 07:48:28 p.m. PST",
+ * which is correct, because DST was not used in year 1926 in zone
+ * America/Los_Angeles. When this is parsed, date[1] becomes a time
+ * in 2026, which is "07/13/26 08:48:28 p.m. PDT". There was a zone
+ * offset calculation bug that observed DST in 1926, which was resolved.
+ * Before the bug was resolved, result[0] == result[1] was true,
+ * but after the bug fix, the expected result is actually
+ * result[0] != result[1]. -Yoshito
+ */
+ /* TODO: We need to review this code and clarify what we really
+ * want to test here.
+ */
+ //if (date[1] != date[2] || result[0] != result[1]) {
+ if (date[1] != date[2]) {
+ errln("Round trip failure: \"%S\" (%f), \"%S\" (%f)", result[0].getBuffer(), date[1], result[1].getBuffer(), date[2]);
+ }
+}
+
// ==
void DateFormatRoundTripTest::TestDateFormatRoundTrip()
UErrorCode status = U_ZERO_ERROR;
getFieldCal = Calendar::createInstance(status);
- failure(status, "Calendar::createInstance");
+ if (U_FAILURE(status)) {
+ dataerrln("Fail: Calendar::createInstance: %s", u_errorName(status));
+ return;
+ }
int32_t locCount = 0;
logln("Testing dstyle" + UnicodeString(styleName((DateFormat::EStyle)dstyle)) + ", tstyle" + UnicodeString(styleName((DateFormat::EStyle)tstyle)) );
DateFormat *df = DateFormat::createDateTimeInstance((DateFormat::EStyle)dstyle, (DateFormat::EStyle)tstyle, loc);
if(df == NULL) {
- errln(UnicodeString("Could not DF::createDateTimeInstance ") + UnicodeString(styleName((DateFormat::EStyle)dstyle)) + ", tstyle" + UnicodeString(styleName((DateFormat::EStyle)tstyle)) + "Locale: " + loc.getDisplayName(temp));
+ dataerrln(UnicodeString("Could not DF::createDateTimeInstance ") + UnicodeString(styleName((DateFormat::EStyle)dstyle)) + ", tstyle" + UnicodeString(styleName((DateFormat::EStyle)tstyle)) + "Locale: " + loc.getDisplayName(temp));
} else {
test(df, loc);
delete df;
UBool isGregorian = FALSE;
UErrorCode minStatus = U_ZERO_ERROR;
+ if(fmt->getCalendar() == NULL) {
+ errln((UnicodeString)"DateFormatRoundTripTest::test, DateFormat getCalendar() returns null for " + origLocale.getName());
+ return;
+ }
UDate minDate = CalendarTest::minDateOfCalendar(*fmt->getCalendar(), isGregorian, minStatus);
if(U_FAILURE(minStatus)) {
errln((UnicodeString)"Failure getting min date for " + origLocale.getName());
// patterns we have, but it may be a problem later.
UBool hasEra = (pat.indexOf(UnicodeString("G")) != -1);
- UBool hasZone = (pat.indexOf(UnicodeString("z")) != -1);
+ UBool hasZoneDisplayName = (pat.indexOf(UnicodeString("z")) != -1) || (pat.indexOf(UnicodeString("v")) != -1)
+ || (pat.indexOf(UnicodeString("V")) != -1);
// Because patterns contain incomplete data representing the Date,
// we must be careful of how we do the roundtrip. We start with
for(loop = 0; loop < DEPTH; ++loop) {
if (loop > 0) {
d[loop] = fmt->parse(s[loop-1], status);
- failure(status, "fmt->parse", s[loop-1]);
+ failure(status, "fmt->parse", s[loop-1]+" in locale: " + origLocale.getName() + " with pattern: " + pat);
status = U_ZERO_ERROR; /* any error would have been reported */
}
int maxSmatch = 1;
if (dmatch > maxDmatch) {
// Time-only pattern with zone information and a starting date in PST.
- if(timeOnly && hasZone && fmt->getTimeZone().inDaylightTime(d[0], status) && ! failure(status, "TimeZone::inDST()")) {
- maxDmatch = 3;
- maxSmatch = 2;
+ if(timeOnly && hasZoneDisplayName) {
+ int32_t startRaw, startDst;
+ fmt->getTimeZone().getOffset(d[0], FALSE, startRaw, startDst, status);
+ failure(status, "TimeZone::getOffset");
+ // if the start offset is greater than the offset on Jan 1, 1970
+ // in PST, then need one more round trip. There are two cases
+ // fall into this category. The start date is 1) DST or
+ // 2) LMT (GMT-07:52:58).
+ if (startRaw + startDst > -28800000) {
+ maxDmatch = 3;
+ maxSmatch = 2;
+ }
}
}
&& getField(d[0], UCAL_YEAR)
!= getField(d[dmatch], UCAL_YEAR)
&& !failure(status, "error status [smatch>maxSmatch]")
- && ((hasZone
+ && ((hasZoneDisplayName
&& (fmt->getTimeZone().inDaylightTime(d[0], status)
== fmt->getTimeZone().inDaylightTime(d[dmatch], status)
|| getField(d[0], UCAL_MONTH) == UCAL_APRIL
|| getField(d[0], UCAL_MONTH) == UCAL_OCTOBER))
- || !hasZone)
+ || !hasZoneDisplayName)
)
{
maxSmatch = 2;
- }
+ }
+ // If zone display name is used, fallback format might be used before 1970
+ else if (hasZoneDisplayName && d[0] < 0) {
+ maxSmatch = 2;
+ }
+ else if (timeOnly && !isGregorian && hasZoneDisplayName && maxSmatch == 1) {
+ int32_t startRaw, startDst;
+ fmt->getTimeZone().getOffset(d[1], FALSE, startRaw, startDst, status);
+ failure(status, "TimeZone::getOffset");
+ // If the calendar type is not Gregorian and the pattern is time only,
+ // the calendar implementation may use a date before 1970 as day 0.
+ // In this case, time zone offset of the default year might be
+ // different from the one at 1970-01-01 in PST and string match requires
+ // one more iteration.
+ if (startRaw + startDst != -28800000) {
+ maxSmatch = 2;
+ }
+ }
}
- if(dmatch > maxDmatch || smatch > maxSmatch) { // Special case for Japanese and Islamic (could have large negative years)
+ /*
+ * Special case for Japanese and Buddhist (could have large negative years)
+ * Also, Hebrew calendar need help handling leap month.
+ */
+ if(dmatch > maxDmatch || smatch > maxSmatch) {
const char *type = fmt->getCalendar()->getType();
- if(!strcmp(type,"japanese")) {
+ if(!strcmp(type,"japanese") || (!strcmp(type,"buddhist"))) {
maxSmatch = 4;
maxDmatch = 4;
- }
+ } else if(!strcmp(type,"hebrew")) {
+ maxSmatch = 3;
+ maxDmatch = 3;
+ }
}
// Use @v to see verbose results on successful cases