001: package net.sf.saxon.number;
002:
003: import net.sf.saxon.om.FastStringBuffer;
004: import net.sf.saxon.value.DateTimeValue;
005:
006: import java.io.Serializable;
007: import java.util.HashMap;
008: import java.util.List;
009: import java.util.ArrayList;
010:
011: /**
012: * Class Numberer_en does number formatting for language="en".
013: * This supports the xsl:number element.
014: * Methods and data are declared as protected, and static is avoided, to allow easy subclassing.
015: * @author Michael H. Kay
016: */
017:
018: public class Numberer_en implements Numberer, Serializable {
019:
020: private String country;
021:
022: public static final int UPPER_CASE = 0;
023: public static final int LOWER_CASE = 1;
024: public static final int TITLE_CASE = 2;
025:
026: /**
027: * Set the country used by this numberer (currenly used only for names of timezones)
028: */
029:
030: public void setCountry(String country) {
031: this .country = country;
032: }
033:
034: /**
035: * Format a number into a string
036: * @param number The number to be formatted
037: * @param picture The format token. This is a single component of the format attribute
038: * of xsl:number, e.g. "1", "01", "i", or "a"
039: * @param groupSize number of digits per group (0 implies no grouping)
040: * @param groupSeparator string to appear between groups of digits
041: * @param letterValue The letter-value specified to xsl:number: "alphabetic" or
042: * "traditional". Can also be an empty string or null.
043: * @param ordinal The value of the ordinal attribute specified to xsl:number
044: * The value "yes" indicates that ordinal numbers should be used; "" or null indicates
045: * that cardinal numbers
046: * @return the formatted number. Note that no errors are reported; if the request
047: * is invalid, the number is formatted as if the string() function were used.
048: */
049:
050: public String format(long number, String picture, int groupSize,
051: String groupSeparator, String letterValue, String ordinal) {
052:
053: if (number < 0) {
054: return "" + number;
055: }
056: if (picture == null || picture.length() == 0) {
057: return "" + number;
058: }
059:
060: FastStringBuffer sb = new FastStringBuffer(16);
061: char formchar = picture.charAt(0);
062:
063: switch (formchar) {
064:
065: case '0':
066: case '1':
067: sb.append(toRadical(number, westernDigits, picture,
068: groupSize, groupSeparator));
069: if (ordinal != null && ordinal.length() > 0) {
070: sb.append(ordinalSuffix(ordinal, number));
071: }
072: break;
073:
074: case 'A':
075: if (number == 0)
076: return "0";
077: sb.append(toAlphaSequence(number, latinUpper));
078: break;
079:
080: case 'a':
081: if (number == 0)
082: return "0";
083: sb.append(toAlphaSequence(number, latinLower));
084: break;
085:
086: case 'w':
087: case 'W':
088: int wordCase;
089: if (picture.equals("W")) {
090: wordCase = UPPER_CASE;
091: } else if (picture.equals("w")) {
092: wordCase = LOWER_CASE;
093: } else {
094: wordCase = TITLE_CASE;
095: }
096: if (ordinal != null && ordinal.length() > 0) {
097: sb.append(toOrdinalWords(ordinal, number, wordCase));
098:
099: } else {
100: sb.append(toWords(number, wordCase));
101: }
102: break;
103:
104: case 'i':
105: if (number == 0)
106: return "0";
107: if (letterValue == null || letterValue.equals("")
108: || letterValue.equals("traditional")) {
109: sb.append(toRoman(number));
110: } else {
111: alphaDefault(number, formchar, sb);
112: }
113: break;
114:
115: case 'I':
116: if (number == 0)
117: return "0";
118: if (letterValue == null || letterValue.equals("")
119: || letterValue.equals("traditional")) {
120: sb.append(toRoman(number).toUpperCase());
121: } else {
122: alphaDefault(number, formchar, sb);
123: }
124: break;
125:
126: case '\u0391':
127: if (number == 0)
128: return "0";
129: sb.append(toAlphaSequence(number, greekUpper));
130: break;
131:
132: case '\u03b1':
133: if (number == 0)
134: return "0";
135: sb.append(toAlphaSequence(number, greekLower));
136: break;
137:
138: case '\u0410':
139: if (number == 0)
140: return "0";
141: sb.append(toAlphaSequence(number, cyrillicUpper));
142: break;
143:
144: case '\u0430':
145: if (number == 0)
146: return "0";
147: sb.append(toAlphaSequence(number, cyrillicLower));
148: break;
149:
150: case '\u05d0':
151: if (number == 0)
152: return "0";
153: sb.append(toAlphaSequence(number, hebrew));
154: break;
155:
156: case '\u3042':
157: if (number == 0)
158: return "0";
159: sb.append(toAlphaSequence(number, hiraganaA));
160: break;
161:
162: case '\u30a2':
163: if (number == 0)
164: return "0";
165: sb.append(toAlphaSequence(number, katakanaA));
166: break;
167:
168: case '\u3044':
169: if (number == 0)
170: return "0";
171: sb.append(toAlphaSequence(number, hiraganaI));
172: break;
173:
174: case '\u30a4':
175: if (number == 0)
176: return "0";
177: sb.append(toAlphaSequence(number, katakanaI));
178: break;
179:
180: case '\u4e00':
181: if (number == 0)
182: return "0";
183: sb.append(toRadical(number, kanjiDigits, picture,
184: groupSize, groupSeparator));
185: break;
186:
187: default:
188:
189: if (Character.isDigit(formchar)) {
190:
191: int zero = (int) formchar
192: - Character.getNumericValue(formchar);
193: String digits = "" + (char) (zero) + (char) (zero + 1)
194: + (char) (zero + 2) + (char) (zero + 3)
195: + (char) (zero + 4) + (char) (zero + 5)
196: + (char) (zero + 6) + (char) (zero + 7)
197: + (char) (zero + 8) + (char) (zero + 9);
198:
199: sb.append(toRadical(number, digits, picture, groupSize,
200: groupSeparator));
201: break;
202:
203: } else {
204: if (number == 0)
205: return "0";
206: if (formchar < '\u1100') {
207: alphaDefault(number, formchar, sb);
208: } else {
209: // fallback to western numbering
210: sb.append(toRadical(number, westernDigits, picture,
211: groupSize, groupSeparator));
212: }
213: break;
214:
215: }
216: }
217:
218: return sb.toString();
219: }
220:
221: /**
222: * Construct the ordinal suffix for a number, for example "st", "nd", "rd"
223: * @param ordinalParam the value of the ordinal attribute (used in non-English
224: * language implementations)
225: * @param number the number being formatted
226: * @return the ordinal suffix to be appended to the formatted number
227: */
228:
229: protected String ordinalSuffix(String ordinalParam, long number) {
230: int penult = ((int) (number % 100)) / 10;
231: int ult = (int) (number % 10);
232: if (penult == 1) {
233: // e.g. 11th, 12th, 13th
234: return "th";
235: } else {
236: if (ult == 1) {
237: return "st";
238: } else if (ult == 2) {
239: return "nd";
240: } else if (ult == 3) {
241: return "rd";
242: } else {
243: return "th";
244: }
245: }
246: }
247:
248: protected static final String westernDigits = "0123456789";
249:
250: protected static final String latinUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
251:
252: protected static final String latinLower = "abcdefghijklmnopqrstuvwxyz";
253:
254: protected static final String greekUpper = "\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a"
255: + "\u039b\u039c\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4"
256: + "\u03a5\u03a6\u03a7\u03a8\u03a9";
257:
258: protected static final String greekLower = "\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba"
259: + "\u03bb\u03bc\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c3\u03c4"
260: + "\u03c5\u03c6\u03c7\u03c8\u03c9";
261:
262: // Cyrillic information from Dmitry Kirsanov [dmitry@kirsanov.com]
263: // (based on his personal knowledge of Russian texts, not any authoritative source)
264:
265: protected static final String cyrillicUpper = "\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418"
266: + "\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0421\u0423"
267: + "\u0424\u0425\u0426\u0427\u0428\u0429\u042b\u042d\u042e\u042f";
268:
269: protected static final String cyrillicLower = "\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438"
270: + "\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0441\u0443"
271: + "\u0444\u0445\u0446\u0447\u0448\u0449\u044b\u044d\u044e\u044f";
272:
273: protected static final String hebrew = "\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05db\u05dc"
274: + "\u05de\u05e0\u05e1\u05e2\u05e4\u05e6\u05e7\u05e8\u05e9\u05ea";
275:
276: // The following Japanese sequences were supplied by
277: // MURAKAMI Shinyu [murakami@nadita.com]
278:
279: protected static final String hiraganaA = "\u3042\u3044\u3046\u3048\u304a\u304b\u304d\u304f\u3051\u3053"
280: + "\u3055\u3057\u3059\u305b\u305d\u305f\u3061\u3064\u3066\u3068"
281: + "\u306a\u306b\u306c\u306d\u306e\u306f\u3072\u3075\u3078\u307b"
282: + "\u307e\u307f\u3080\u3081\u3082\u3084\u3086\u3088\u3089\u308a"
283: + "\u308b\u308c\u308d\u308f\u3092\u3093";
284:
285: protected static final String katakanaA =
286:
287: "\u30a2\u30a4\u30a6\u30a8\u30aa\u30ab\u30ad\u30af\u30b1\u30b3"
288: + "\u30b5\u30b7\u30b9\u30bb\u30bd\u30bf\u30c1\u30c4\u30c6\u30c8"
289: + "\u30ca\u30cb\u30cc\u30cd\u30ce\u30cf\u30d2\u30d5\u30d8\u30db"
290: + "\u30de\u30df\u30e0\u30e1\u30e2\u30e4\u30e6\u30e8\u30e9\u30ea"
291: + "\u30eb\u30ec\u30ed\u30ef\u30f2\u30f3";
292:
293: protected static final String hiraganaI =
294:
295: "\u3044\u308d\u306f\u306b\u307b\u3078\u3068\u3061\u308a\u306c"
296: + "\u308b\u3092\u308f\u304b\u3088\u305f\u308c\u305d\u3064\u306d"
297: + "\u306a\u3089\u3080\u3046\u3090\u306e\u304a\u304f\u3084\u307e"
298: + "\u3051\u3075\u3053\u3048\u3066\u3042\u3055\u304d\u3086\u3081"
299: + "\u307f\u3057\u3091\u3072\u3082\u305b\u3059";
300:
301: protected static final String katakanaI =
302:
303: "\u30a4\u30ed\u30cf\u30cb\u30db\u30d8\u30c8\u30c1\u30ea\u30cc"
304: + "\u30eb\u30f2\u30ef\u30ab\u30e8\u30bf\u30ec\u30bd\u30c4\u30cd"
305: + "\u30ca\u30e9\u30e0\u30a6\u30f0\u30ce\u30aa\u30af\u30e4\u30de"
306: + "\u30b1\u30d5\u30b3\u30a8\u30c6\u30a2\u30b5\u30ad\u30e6\u30e1"
307: + "\u30df\u30b7\u30f1\u30d2\u30e2\u30bb\u30b9";
308:
309: protected static final String kanjiDigits = "\u3007\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d";
310:
311: /**
312: * Default processing with an alphabetic format token: use the contiguous
313: * range of Unicode letters starting with that token.
314: */
315:
316: protected void alphaDefault(long number, char formchar,
317: FastStringBuffer sb) {
318: int min = (int) formchar;
319: int max = (int) formchar;
320: // use the contiguous range of letters starting with the specified one
321: while (Character.isLetterOrDigit((char) (max + 1))) {
322: max++;
323: }
324: sb.append(toAlpha(number, min, max));
325: }
326:
327: /**
328: * Format the number as an alphabetic label using the alphabet consisting
329: * of consecutive Unicode characters from min to max
330: */
331:
332: protected String toAlpha(long number, int min, int max) {
333: if (number <= 0)
334: return "" + number;
335: int range = max - min + 1;
336: char last = (char) (((number - 1) % range) + min);
337: if (number > range) {
338: return toAlpha((number - 1) / range, min, max) + last;
339: } else {
340: return "" + last;
341: }
342: }
343:
344: /**
345: * Convert the number into an alphabetic label using a given alphabet.
346: * For example, if the alphabet is "xyz" the sequence is x, y, z, xx, xy, xz, ....
347: */
348:
349: protected String toAlphaSequence(long number, String alphabet) {
350: if (number <= 0)
351: return "" + number;
352: int range = alphabet.length();
353: char last = alphabet.charAt((int) ((number - 1) % range));
354: if (number > range) {
355: return toAlphaSequence((number - 1) / range, alphabet)
356: + last;
357: } else {
358: return "" + last;
359: }
360: }
361:
362: /**
363: * Convert the number into a decimal or other representation using the given set of
364: * digits.
365: * For example, if the digits are "01" the sequence is 1, 10, 11, 100, 101, 110, 111, ...
366: * More commonly, the digits will be "0123456789", giving the usual decimal numbering.
367: * @param number the number to be formatted
368: * @param digits the set of digits to be used
369: * @param picture the formatting token, for example 001 means include leading zeroes to give at least
370: * three decimal places. In practice, it is only the length of the picture that is significant: a
371: * picture of "999" gives the same results as "001". (This isn't what a strict reading of the spec
372: * suggests should happen, but it seems a reasonable fallback in practice.)
373: * @param groupSize the number of digits in each group
374: * @param groupSeparator the separator to use between groups of digits.
375: */
376:
377: private String toRadical(long number, String digits,
378: String picture, int groupSize, String groupSeparator) {
379:
380: FastStringBuffer sb = new FastStringBuffer(16);
381: FastStringBuffer temp = new FastStringBuffer(16);
382: int base = digits.length();
383:
384: String s = "";
385: long n = number;
386: while (n > 0) {
387: s = digits.charAt((int) (n % base)) + s;
388: n = n / base;
389: }
390:
391: for (int i = 0; i < (picture.length() - s.length()); i++) {
392: temp.append(digits.charAt(0));
393: }
394: temp.append(s);
395:
396: if (groupSize > 0) {
397: for (int i = 0; i < temp.length(); i++) {
398: if (i != 0 && ((temp.length() - i) % groupSize) == 0) {
399: sb.append(groupSeparator);
400: }
401: sb.append(temp.charAt(i));
402: }
403: } else {
404: sb = temp;
405: }
406:
407: return sb.toString();
408: }
409:
410: /**
411: * Generate a Roman numeral (in lower case)
412: */
413:
414: public static String toRoman(long n) {
415: if (n <= 0 || n > 9999)
416: return "" + n;
417: return romanThousands[(int) n / 1000]
418: + romanHundreds[((int) n / 100) % 10]
419: + romanTens[((int) n / 10) % 10]
420: + romanUnits[(int) n % 10];
421: }
422:
423: // Roman numbers beyond 4000 use overlining and other conventions which we won't
424: // attempt to reproduce. We'll go high enough to handle present-day Gregorian years.
425:
426: private static String[] romanThousands = { "", "m", "mm", "mmm",
427: "mmmm", "mmmmm", "mmmmmm", "mmmmmmm", "mmmmmmmm",
428: "mmmmmmmmm" };
429: private static String[] romanHundreds = { "", "c", "cc", "ccc",
430: "cd", "d", "dc", "dcc", "dccc", "cm" };
431: private static String[] romanTens = { "", "x", "xx", "xxx", "xl",
432: "l", "lx", "lxx", "lxxx", "xc" };
433: private static String[] romanUnits = { "", "i", "ii", "iii", "iv",
434: "v", "vi", "vii", "viii", "ix" };
435:
436: /**
437: * Show the number as words in title case. (We choose title case because
438: * the result can then be converted algorithmically to lower case or upper case).
439: */
440:
441: public String toWords(long number) {
442: if (number >= 1000000000) {
443: long rem = number % 1000000000;
444: return toWords(number / 1000000000)
445: + " Billion"
446: + (rem == 0 ? "" : (rem < 100 ? " and " : " ")
447: + toWords(rem));
448: } else if (number >= 1000000) {
449: long rem = number % 1000000;
450: return toWords(number / 1000000)
451: + " Million"
452: + (rem == 0 ? "" : (rem < 100 ? " and " : " ")
453: + toWords(rem));
454: } else if (number >= 1000) {
455: long rem = number % 1000;
456: return toWords(number / 1000)
457: + " Thousand"
458: + (rem == 0 ? "" : (rem < 100 ? " and " : " ")
459: + toWords(rem));
460: } else if (number >= 100) {
461: long rem = number % 100;
462: return toWords(number / 100) + " Hundred"
463: + (rem == 0 ? "" : " and " + toWords(rem));
464: } else {
465: if (number < 20)
466: return englishUnits[(int) number];
467: int rem = (int) (number % 10);
468: return englishTens[(int) number / 10]
469: + (rem == 0 ? "" : ' ' + englishUnits[rem]);
470: }
471: }
472:
473: public String toWords(long number, int wordCase) {
474: String s;
475: if (number == 0) {
476: s = "Zero";
477: } else {
478: s = toWords(number);
479: }
480: if (wordCase == UPPER_CASE) {
481: return s.toUpperCase();
482: } else if (wordCase == LOWER_CASE) {
483: return s.toLowerCase();
484: } else {
485: return s;
486: }
487: }
488:
489: /**
490: * Show an ordinal number as English words in a requested case (for example, Twentyfirst)
491: */
492:
493: public String toOrdinalWords(String ordinalParam, long number,
494: int wordCase) {
495: String s;
496: if (number >= 1000000000) {
497: long rem = number % 1000000000;
498: s = toWords(number / 1000000000)
499: + " Billion"
500: + (rem == 0 ? "th" : (rem < 100 ? " and " : " ")
501: + toOrdinalWords(ordinalParam, rem,
502: wordCase));
503: } else if (number >= 1000000) {
504: long rem = number % 1000000;
505: s = toWords(number / 1000000)
506: + " Million"
507: + (rem == 0 ? "th" : (rem < 100 ? " and " : " ")
508: + toOrdinalWords(ordinalParam, rem,
509: wordCase));
510: } else if (number >= 1000) {
511: long rem = number % 1000;
512: s = toWords(number / 1000)
513: + " Thousand"
514: + (rem == 0 ? "th" : (rem < 100 ? " and " : " ")
515: + toOrdinalWords(ordinalParam, rem,
516: wordCase));
517: } else if (number >= 100) {
518: long rem = number % 100;
519: s = toWords(number / 100)
520: + " Hundred"
521: + (rem == 0 ? "th" : " and "
522: + toOrdinalWords(ordinalParam, rem,
523: wordCase));
524: } else {
525: if (number < 20) {
526: s = englishOrdinalUnits[(int) number];
527: } else {
528: int rem = (int) (number % 10);
529: if (rem == 0) {
530: s = englishOrdinalTens[(int) number / 10];
531: } else {
532: s = englishTens[(int) number / 10] + '-'
533: + englishOrdinalUnits[rem];
534: }
535: }
536: }
537: if (wordCase == UPPER_CASE) {
538: return s.toUpperCase();
539: } else if (wordCase == LOWER_CASE) {
540: return s.toLowerCase();
541: } else {
542: return s;
543: }
544: }
545:
546: private static String[] englishUnits = { "", "One", "Two", "Three",
547: "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten",
548: "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen",
549: "Sixteen", "Seventeen", "Eighteen", "Nineteen" };
550:
551: private static String[] englishTens = { "", "Ten", "Twenty",
552: "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty",
553: "Ninety" };
554:
555: private static String[] englishOrdinalUnits = { "", "First",
556: "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh",
557: "Eighth", "Ninth", "Tenth", "Eleventh", "Twelfth",
558: "Thirteenth", "Fourteenth", "Fifteenth", "Sixteenth",
559: "Seventeenth", "Eighteenth", "Nineteenth" };
560:
561: private static String[] englishOrdinalTens = { "", "Tenth",
562: "Twentieth", "Thirtieth", "Fortieth", "Fiftieth",
563: "Sixtieth", "Seventieth", "Eightieth", "Ninetieth" };
564:
565: /**
566: * Get a month name or abbreviation
567: * @param month The month number (1=January, 12=December)
568: * @param minWidth The minimum number of characters
569: * @param maxWidth The maximum number of characters
570: */
571:
572: public String monthName(int month, int minWidth, int maxWidth) {
573: String name = englishMonths[month - 1];
574: if (maxWidth < 3) {
575: maxWidth = 3;
576: }
577: if (name.length() > maxWidth) {
578: name = name.substring(0, maxWidth);
579: }
580: while (name.length() < minWidth) {
581: name = name + ' ';
582: }
583: return name;
584: }
585:
586: private static String[] englishMonths = { "January", "February",
587: "March", "April", "May", "June", "July", "August",
588: "September", "October", "November", "December" };
589:
590: /**
591: * Get a day name or abbreviation
592: * @param day The day of the week (1=Monday, 7=Sunday)
593: * @param minWidth The minimum number of characters
594: * @param maxWidth The maximum number of characters
595: */
596:
597: public String dayName(int day, int minWidth, int maxWidth) {
598: String name = englishDays[day - 1];
599: if (maxWidth < 2) {
600: maxWidth = 2;
601: }
602: if (name.length() > maxWidth) {
603: name = englishDayAbbreviations[day - 1];
604: if (name.length() > maxWidth) {
605: name = name.substring(0, maxWidth);
606: }
607: }
608: while (name.length() < minWidth) {
609: name = name + ' ';
610: }
611: if (minWidth == 1 && maxWidth == 2) {
612: // special case
613: name = name.substring(0, minUniqueDayLength[day - 1]);
614: }
615: return name;
616: }
617:
618: private static String[] englishDays = { "Monday", "Tuesday",
619: "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
620:
621: private static String[] englishDayAbbreviations = { "Mon", "Tues",
622: "Weds", "Thurs", "Fri", "Sat", "Sun" };
623:
624: private static int[] minUniqueDayLength = { 2, 1, 2, 1, 2, 2, 1 };
625:
626: /**
627: * Get an am/pm indicator
628: * @param minutes the minutes within the day
629: * @param minWidth minimum width of output
630: * @param maxWidth maximum width of output
631: * @return the AM or PM indicator
632: */
633:
634: public String halfDayName(int minutes, int minWidth, int maxWidth) {
635: String s;
636: if (minutes < 12 * 60) {
637: switch (maxWidth) {
638: case 1:
639: s = "A";
640: break;
641: case 2:
642: case 3:
643: s = "Am";
644: break;
645: default:
646: s = "A.M.";
647: }
648: } else {
649: switch (maxWidth) {
650: case 1:
651: s = "P";
652: break;
653: case 2:
654: case 3:
655: s = "Pm";
656: break;
657: default:
658: s = "P.M.";
659: }
660: }
661: return s;
662: }
663:
664: /**
665: * Get an ordinal suffix for a particular component of a date/time.
666: *
667: * @param component the component specifier from a format-dateTime picture, for
668: * example "M" for the month or "D" for the day.
669: * @return a string that is acceptable in the ordinal attribute of xsl:number
670: * to achieve the required ordinal representation. For example, "-e" for the day component
671: * in German, to have the day represented as "dritte August".
672: */
673:
674: public String getOrdinalSuffixForDateTime(String component) {
675: return "yes";
676: }
677:
678: /**
679: * Get the name for an era (e.g. "BC" or "AD")
680: *
681: * @param year: the proleptic gregorian year, using "0" for the year before 1AD
682: */
683:
684: public String getEraName(int year) {
685: return (year > 0 ? "AD" : "BC");
686: }
687:
688: /**
689: * Get the name of a timezone
690: *
691: * @param tz: the offset of the timezone from GMT in minutes
692: */
693:
694: public String getTimezoneName(int tz) {
695: List list = (List) timezones.get(new Integer(tz));
696: if (list == null) {
697: FastStringBuffer sb = new FastStringBuffer(10);
698: DateTimeValue.appendTimezone(tz, sb);
699: return sb.toString();
700: } else {
701: String c = (country == null ? "us" : country);
702: if (list.size() == 1) {
703: return ((String[]) list.get(0))[0];
704: } else {
705: for (int i = 0; i < list.size(); i++) {
706: String[] entry = (String[]) list.get(i);
707: if (entry[1].equals(c)) {
708: return entry[0];
709: }
710: }
711: return ((String[]) list.get(0))[0];
712: }
713: }
714: }
715:
716: static HashMap timezones = new HashMap(50);
717:
718: static void tz(double offset, String name, String country) {
719: Integer key = new Integer((int) (offset * 60));
720: String[] val = { name, country };
721: List list = (List) timezones.get(key);
722: if (list == null) {
723: list = new ArrayList(3);
724: }
725: list.add(val);
726: timezones.put(key, list);
727: }
728:
729: static {
730:
731: tz(9.5, "ACST", "au"); // Australian central standard time
732: tz(10, "AEST", "au"); // Australian eastern standard time
733: tz(-9, "AKST", "us"); // Alaska standard time
734: tz(8, "AWST", "au"); // Australian Western standard time
735: tz(1, "BST", "gb"); // British Summer Time
736: tz(1, "CET", "eu"); // Central European Time
737: tz(9.5, "CST", "au"); // Central Standard Time
738: tz(-6, "CST", "us"); // Central Standard Time
739: tz(7, "CXT", "au"); // Christmas Island Time
740: tz(2, "EET", "eu"); // Eastern European Time
741: tz(10, "EST", "au"); // Eastern Standard Time
742: tz(-5, "EST", "us"); // Eastern Standard Time
743: tz(0, "GMT", "gb"); // Greenwich Mean Time
744: tz(-10, "HAST", "us"); // Hawaii-Aleutian
745: tz(+1, "MEZ", "de"); // Mitteleuropaische Zeit
746: tz(-7, "MST", "us"); // Mountain Standard
747: tz(+11.5, "NFT", "au"); // Norfolk Island
748: tz(-3.5, "NST", "us"); // Newfoundland Standard
749: tz(-8, "PST", "us"); // Pacific Standard
750: tz(0, "UTC", "eu"); // UTC
751: tz(0, "WET", "eu"); // Western European
752: tz(+8, "WST", "au"); // Western Standard
753:
754: tz(10.5, "ACDT", "au*"); // Australian central daylight time
755: tz(-3, "ADT", "us*"); // Atlantic Daylight time
756: tz(11, "AEDT", "au*"); // Australian eastern daylight time
757: tz(-8, "AKDT", "us*"); // Alaska daylight time
758: tz(-4, "AST", "us*"); // Atlantic Standard time
759: tz(10.5, "CDT", "au*"); // Australia Central Daylight Time
760: tz(-5, "CDT", "us*"); // Central Daylight Time
761: tz(2, "CEDT", "eu*"); // Central Europe Daylight Time
762: tz(11, "EDT", "au*"); // Eastern Daylight Time
763: tz(-4, "EDT", "us*"); // Eastern Daylight Time
764: tz(3, "EEDT", "eu*"); // Eastern European Daylight Time
765: tz(-6, "MDT", "us*"); // Mountain Daylight
766: tz(+2, "MESZ", "de*"); // Mitteleuropaische Sommerzeit
767: tz(-2.5, "NDT", "us*"); // Newfoundland Daylight
768: tz(-7, "PDT", "us*"); // Pacific Daylight
769: tz(+1, "WEDT", "eu*"); // Western European Daylight
770:
771: }
772:
773: }
774:
775: //
776: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
777: // you may not use this file except in compliance with the License. You may obtain a copy of the
778: // License at http://www.mozilla.org/MPL/
779: //
780: // Software distributed under the License is distributed on an "AS IS" basis,
781: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
782: // See the License for the specific language governing rights and limitations under the License.
783: //
784: // The Original Code is: all this file.
785: //
786: // The Initial Developer of the Original Code is Michael H. Kay.
787: //
788: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
789: //
790: // Contributor(s): none.
791: //
|