001: /*
002: *******************************************************************************
003: * Copyright (C) 2001-2005, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */
007:
008: /**
009: * Port From: ICU4C v1.8.1 : format : DateFormatRoundTripTest
010: * Source File: $ICU4CRoot/source/test/intltest/dtfmtrtts.cpp
011: **/package com.ibm.icu.dev.test.format;
012:
013: import com.ibm.icu.text.*;
014: import com.ibm.icu.util.*;
015: import java.util.Locale;
016: import java.util.Date;
017: import java.util.Random;
018: import java.text.FieldPosition;
019: import java.text.ParseException;
020:
021: /**
022: * Performs round-trip tests for DateFormat
023: **/
024: public class DateFormatRoundTripTest extends
025: com.ibm.icu.dev.test.TestFmwk {
026: public boolean INFINITE = false;
027: public boolean quick = true;
028: private SimpleDateFormat dateFormat;
029: private Calendar getFieldCal;
030: private int SPARSENESS = 18;
031: private int TRIALS = 4;
032: private int DEPTH = 5;
033: private Random ran;
034:
035: public static void main(String[] args) throws Exception {
036: new DateFormatRoundTripTest().run(args);
037: }
038:
039: public void TestDateFormatRoundTrip() {
040: dateFormat = new SimpleDateFormat(
041: "EEE MMM dd HH:mm:ss.SSS zzz yyyy G");
042: getFieldCal = Calendar.getInstance();
043: ran = createRandom(); // use test framework's random seed
044:
045: final Locale[] avail = DateFormat.getAvailableLocales();
046: int locCount = avail.length;
047: logln("DateFormat available locales: " + locCount);
048: if (quick) {
049: if (locCount > 5)
050: locCount = 5;
051: logln("Quick mode: only testing first 5 Locales");
052: }
053: TimeZone tz = TimeZone.getDefault();
054: logln("Default TimeZone: " + tz.getID());
055:
056: if (INFINITE) {
057: // Special infinite loop test mode for finding hard to reproduce errors
058: Locale loc = Locale.getDefault();
059: logln("ENTERING INFINITE TEST LOOP FOR Locale: "
060: + loc.getDisplayName());
061: for (;;) {
062: _test(loc);
063: }
064: } else {
065: _test(Locale.getDefault());
066: for (int i = 0; i < locCount; ++i) {
067: _test(avail[i]);
068: }
069: }
070: }
071:
072: public String styleName(int s) {
073: switch (s) {
074: case DateFormat.SHORT:
075: return "SHORT";
076: case DateFormat.MEDIUM:
077: return "MEDIUM";
078: case DateFormat.LONG:
079: return "LONG";
080: case DateFormat.FULL:
081: return "FULL";
082: default:
083: return "Unknown";
084: }
085: }
086:
087: public void _test(Locale loc) {
088: if (!INFINITE) {
089: logln("Locale: " + loc.getDisplayName());
090: }
091: // Total possibilities = 24
092: // 4 date
093: // 4 time
094: // 16 date-time
095: boolean[] TEST_TABLE = new boolean[24];
096: int i = 0;
097: for (i = 0; i < 24; ++i)
098: TEST_TABLE[i] = true;
099:
100: // If we have some sparseness, implement it here. Sparseness decreases
101: // test time by eliminating some tests, up to 23.
102: for (i = 0; i < SPARSENESS; i++) {
103: int random = (int) (ran.nextDouble() * 24);
104: if (random >= 0 && random < 24 && TEST_TABLE[i]) {
105: TEST_TABLE[random] = false;
106: }
107: }
108:
109: int itable = 0;
110: int style = 0;
111: for (style = DateFormat.FULL; style <= DateFormat.SHORT; ++style) {
112: if (TEST_TABLE[itable++]) {
113: logln("Testing style " + styleName(style));
114: DateFormat df = DateFormat.getDateInstance(style, loc);
115: _test(df, false);
116: }
117: }
118:
119: for (style = DateFormat.FULL; style <= DateFormat.SHORT; ++style) {
120: if (TEST_TABLE[itable++]) {
121: logln("Testing style " + styleName(style));
122: DateFormat df = DateFormat.getTimeInstance(style, loc);
123: _test(df, true);
124: }
125: }
126:
127: for (int dstyle = DateFormat.FULL; dstyle <= DateFormat.SHORT; ++dstyle) {
128: for (int tstyle = DateFormat.FULL; tstyle <= DateFormat.SHORT; ++tstyle) {
129: if (TEST_TABLE[itable++]) {
130: logln("Testing dstyle " + styleName(dstyle)
131: + ", tstyle " + styleName(tstyle));
132: DateFormat df = DateFormat.getDateTimeInstance(
133: dstyle, tstyle, loc);
134: _test(df, false);
135: }
136: }
137: }
138: }
139:
140: public void _test(DateFormat fmt, boolean timeOnly) {
141:
142: if (!(fmt instanceof SimpleDateFormat)) {
143: errln("DateFormat wasn't a SimpleDateFormat");
144: return;
145: }
146:
147: String pat = ((SimpleDateFormat) fmt).toPattern();
148: logln(pat);
149:
150: // NOTE TO MAINTAINER
151: // This indexOf check into the pattern needs to be refined to ignore
152: // quoted characters. Currently, this isn't a problem with the locale
153: // patterns we have, but it may be a problem later.
154:
155: boolean hasEra = (pat.indexOf("G") != -1);
156: boolean hasZone = (pat.indexOf("Z") != -1)
157: || (pat.indexOf("z") != -1);
158:
159: // Because patterns contain incomplete data representing the Date,
160: // we must be careful of how we do the roundtrip. We start with
161: // a randomly generated Date because they're easier to generate.
162: // From this we get a string. The string is our real starting point,
163: // because this string should parse the same way all the time. Note
164: // that it will not necessarily parse back to the original date because
165: // of incompleteness in patterns. For example, a time-only pattern won't
166: // parse back to the same date.
167:
168: try {
169: for (int i = 0; i < TRIALS; ++i) {
170: Date[] d = new Date[DEPTH];
171: String[] s = new String[DEPTH];
172:
173: d[0] = generateDate();
174:
175: // We go through this loop until we achieve a match or until
176: // the maximum loop count is reached. We record the points at
177: // which the date and the string starts to match. Once matching
178: // starts, it should continue.
179: int loop;
180: int dmatch = 0; // d[dmatch].getTime() == d[dmatch-1].getTime()
181: int smatch = 0; // s[smatch].equals(s[smatch-1])
182: for (loop = 0; loop < DEPTH; ++loop) {
183: if (loop > 0) {
184: d[loop] = fmt.parse(s[loop - 1]);
185: }
186:
187: s[loop] = fmt.format(d[loop]);
188:
189: if (loop > 0) {
190: if (smatch == 0) {
191: boolean match = s[loop].equals(s[loop - 1]);
192: if (smatch == 0) {
193: if (match)
194: smatch = loop;
195: } else if (!match)
196: errln("FAIL: String mismatch after match");
197: }
198:
199: if (dmatch == 0) {
200: // {sfb} watch out here, this might not work
201: boolean match = d[loop].getTime() == d[loop - 1]
202: .getTime();
203: if (dmatch == 0) {
204: if (match)
205: dmatch = loop;
206: } else if (!match)
207: errln("FAIL: Date mismatch after match");
208: }
209:
210: if (smatch != 0 && dmatch != 0)
211: break;
212: }
213: }
214: // At this point loop == DEPTH if we've failed, otherwise loop is the
215: // max(smatch, dmatch), that is, the index at which we have string and
216: // date matching.
217:
218: // Date usually matches in 2. Exceptions handled below.
219: int maxDmatch = 2;
220: int maxSmatch = 1;
221: if (dmatch > maxDmatch || smatch > maxSmatch) {
222: //If the Date is BC
223: if (!timeOnly
224: && !hasEra
225: && getField(d[0], Calendar.ERA) == GregorianCalendar.BC) {
226: maxDmatch = 3;
227: maxSmatch = 2;
228: }
229: if (hasZone
230: && (fmt.getTimeZone().inDaylightTime(d[0]) || fmt
231: .getTimeZone().inDaylightTime(d[1]))) {
232: maxSmatch = 2;
233: if (timeOnly) {
234: maxDmatch = 3;
235: }
236: }
237: }
238:
239: if (dmatch > maxDmatch || smatch > maxSmatch) {
240: SimpleDateFormat sdf = new SimpleDateFormat(
241: "EEEE, MMMM d, yyyy HH:mm:ss, z G",
242: Locale.US);
243: logln("Date = " + sdf.format(d[0]) + "; ms = "
244: + d[0].getTime());
245: logln("Dmatch: " + dmatch + " maxD: " + maxDmatch
246: + " Smatch:" + smatch + " maxS:"
247: + maxSmatch);
248: errln("Pattern: " + pat + " failed to match"
249: + "; ms = " + d[0].getTime());
250: for (int j = 0; j <= loop && j < DEPTH; ++j) {
251: StringBuffer temp = new StringBuffer("");
252: FieldPosition pos = new FieldPosition(0);
253: logln((j > 0 ? " P> " : " ")
254: + dateFormat.format(d[j], temp, pos)
255: + " F> "
256: + s[j]
257: + (j > 0
258: && d[j].getTime() == d[j - 1]
259: .getTime() ? " d=="
260: : "")
261: + (j > 0 && s[j].equals(s[j - 1]) ? " s=="
262: : ""));
263: }
264: }
265: }
266: } catch (ParseException e) {
267: errln("Exception: " + e.getMessage());
268: logln(e.toString());
269: }
270: }
271:
272: public int getField(Date d, int f) {
273: getFieldCal.setTime(d);
274: int ret = getFieldCal.get(f);
275: return ret;
276: }
277:
278: public Date generateDate() {
279: double a = ran.nextDouble();
280: // Now 'a' ranges from 0..1; scale it to range from 0 to 8000 years
281: a *= 8000;
282: // Range from (4000-1970) BC to (8000-1970) AD
283: a -= 4000;
284: // Now scale up to ms
285: a *= 365.25 * 24 * 60 * 60 * 1000;
286: return new Date((long) a);
287: }
288: }
|