1:
39:
40:
41: package ;
42:
43: import ;
44: import ;
45: import ;
46: import ;
47:
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60:
61:
65: public class SimpleDateFormat extends DateFormat
66: {
67:
73: private class CompiledField
74: {
75:
79: int field;
80:
81:
85: int size;
86:
87:
90: private char character;
91:
92:
101: public CompiledField(int f, int s, char c)
102: {
103: field = f;
104: size = s;
105: character = c;
106: }
107:
108:
112: public int getField()
113: {
114: return field;
115: }
116:
117:
120: public int getSize()
121: {
122: return size;
123: }
124:
125:
128: public char getCharacter()
129: {
130: return character;
131: }
132:
133:
140: public String toString()
141: {
142: StringBuffer builder;
143:
144: builder = new StringBuffer(getClass().getName());
145: builder.append("[field=");
146: builder.append(field);
147: builder.append(", size=");
148: builder.append(size);
149: builder.append(", character=");
150: builder.append(character);
151: builder.append("]");
152:
153: return builder.toString();
154: }
155: }
156:
157:
164: private transient ArrayList tokens;
165:
166:
174: private DateFormatSymbols formatData;
175:
176:
189: private Date defaultCenturyStart;
190:
191:
199: private transient int defaultCentury;
200:
201:
213: private String pattern;
214:
215:
227: private int serialVersionOnStream = 1;
228:
229:
232: private static final long serialVersionUID = 4774881970558875024L;
233:
234:
235:
236:
237: private static final String standardChars = "GyMdkHmsSEDFwWahKzYeugAZ";
238:
239:
253: private void readObject(ObjectInputStream stream)
254: throws IOException, ClassNotFoundException
255: {
256: stream.defaultReadObject();
257: if (serialVersionOnStream < 1)
258: {
259: computeCenturyStart ();
260: serialVersionOnStream = 1;
261: }
262: else
263:
264: set2DigitYearStart(defaultCenturyStart);
265:
266:
267: tokens = new ArrayList();
268: try
269: {
270: compileFormat(pattern);
271: }
272: catch (IllegalArgumentException e)
273: {
274: throw new InvalidObjectException("The stream pattern was invalid.");
275: }
276: }
277:
278:
287: private void compileFormat(String pattern)
288: {
289:
290:
291:
292: char thisChar;
293: int pos;
294: int field;
295: CompiledField current = null;
296:
297: for (int i = 0; i < pattern.length(); i++)
298: {
299: thisChar = pattern.charAt(i);
300: field = standardChars.indexOf(thisChar);
301: if (field == -1)
302: {
303: current = null;
304: if ((thisChar >= 'A' && thisChar <= 'Z')
305: || (thisChar >= 'a' && thisChar <= 'z'))
306: {
307:
308: throw new IllegalArgumentException("Invalid letter "
309: + thisChar +
310: " encountered at character "
311: + i + ".");
312: }
313: else if (thisChar == '\'')
314: {
315:
316: pos = pattern.indexOf('\'', i + 1);
317:
318: if (pos == i + 1)
319: tokens.add("'");
320: else
321: {
322:
323:
324:
325: StringBuffer buf = new StringBuffer();
326: int oldPos = i + 1;
327: do
328: {
329: if (pos == -1)
330: throw new IllegalArgumentException("Quotes starting at character "
331: + i +
332: " not closed.");
333: buf.append(pattern.substring(oldPos, pos));
334: if (pos + 1 >= pattern.length()
335: || pattern.charAt(pos + 1) != '\'')
336: break;
337: buf.append('\'');
338: oldPos = pos + 2;
339: pos = pattern.indexOf('\'', pos + 2);
340: }
341: while (true);
342: tokens.add(buf.toString());
343: }
344: i = pos;
345: }
346: else
347: {
348:
349: tokens.add(new Character(thisChar));
350: }
351: }
352: else
353: {
354:
355: if ((current != null) && (field == current.field))
356: current.size++;
357: else
358: {
359: current = new CompiledField(field, 1, thisChar);
360: tokens.add(current);
361: }
362: }
363: }
364: }
365:
366:
373: public String toString()
374: {
375: StringBuffer output = new StringBuffer(getClass().getName());
376: output.append("[tokens=");
377: output.append(tokens);
378: output.append(", formatData=");
379: output.append(formatData);
380: output.append(", defaultCenturyStart=");
381: output.append(defaultCenturyStart);
382: output.append(", defaultCentury=");
383: output.append(defaultCentury);
384: output.append(", pattern=");
385: output.append(pattern);
386: output.append(", serialVersionOnStream=");
387: output.append(serialVersionOnStream);
388: output.append(", standardChars=");
389: output.append(standardChars);
390: output.append("]");
391: return output.toString();
392: }
393:
394:
398: public SimpleDateFormat()
399: {
400:
405: super();
406: Locale locale = Locale.getDefault();
407: calendar = new GregorianCalendar(locale);
408: computeCenturyStart();
409: tokens = new ArrayList();
410: formatData = new DateFormatSymbols(locale);
411: pattern = (formatData.dateFormats[DEFAULT] + ' '
412: + formatData.timeFormats[DEFAULT]);
413: compileFormat(pattern);
414: numberFormat = NumberFormat.getInstance(locale);
415: numberFormat.setGroupingUsed (false);
416: numberFormat.setParseIntegerOnly (true);
417: numberFormat.setMaximumFractionDigits (0);
418: }
419:
420:
428: public SimpleDateFormat(String pattern)
429: {
430: this(pattern, Locale.getDefault());
431: }
432:
433:
442: public SimpleDateFormat(String pattern, Locale locale)
443: {
444: super();
445: calendar = new GregorianCalendar(locale);
446: computeCenturyStart();
447: tokens = new ArrayList();
448: formatData = new DateFormatSymbols(locale);
449: compileFormat(pattern);
450: this.pattern = pattern;
451: numberFormat = NumberFormat.getInstance(locale);
452: numberFormat.setGroupingUsed (false);
453: numberFormat.setParseIntegerOnly (true);
454: numberFormat.setMaximumFractionDigits (0);
455: }
456:
457:
467: public SimpleDateFormat(String pattern, DateFormatSymbols formatData)
468: {
469: super();
470: calendar = new GregorianCalendar();
471: computeCenturyStart ();
472: tokens = new ArrayList();
473: if (formatData == null)
474: throw new NullPointerException("formatData");
475: this.formatData = formatData;
476: compileFormat(pattern);
477: this.pattern = pattern;
478: numberFormat = NumberFormat.getInstance();
479: numberFormat.setGroupingUsed (false);
480: numberFormat.setParseIntegerOnly (true);
481: numberFormat.setMaximumFractionDigits (0);
482: }
483:
484:
490: public String toPattern()
491: {
492: return pattern;
493: }
494:
495:
501: public String toLocalizedPattern()
502: {
503: String localChars = formatData.getLocalPatternChars();
504: return translateLocalizedPattern(pattern, standardChars, localChars);
505: }
506:
507:
515: public void applyPattern(String pattern)
516: {
517: tokens = new ArrayList();
518: compileFormat(pattern);
519: this.pattern = pattern;
520: }
521:
522:
530: public void applyLocalizedPattern(String pattern)
531: {
532: String localChars = formatData.getLocalPatternChars();
533: pattern = translateLocalizedPattern(pattern, localChars, standardChars);
534: applyPattern(pattern);
535: }
536:
537:
553: private String translateLocalizedPattern(String pattern,
554: String oldChars, String newChars)
555: {
556: int len = pattern.length();
557: StringBuffer buf = new StringBuffer(len);
558: boolean quoted = false;
559: for (int i = 0; i < len; i++)
560: {
561: char ch = pattern.charAt(i);
562: if (ch == '\'')
563: quoted = ! quoted;
564: if (! quoted)
565: {
566: int j = oldChars.indexOf(ch);
567: if (j >= 0)
568: ch = newChars.charAt(j);
569: }
570: buf.append(ch);
571: }
572: return buf.toString();
573: }
574:
575:
581: public Date get2DigitYearStart()
582: {
583: return defaultCenturyStart;
584: }
585:
586:
592: public void set2DigitYearStart(Date date)
593: {
594: defaultCenturyStart = date;
595: calendar.clear();
596: calendar.setTime(date);
597: int year = calendar.get(Calendar.YEAR);
598: defaultCentury = year - (year % 100);
599: }
600:
601:
607: public DateFormatSymbols getDateFormatSymbols()
608: {
609: return (DateFormatSymbols) formatData.clone();
610: }
611:
612:
619: public void setDateFormatSymbols(DateFormatSymbols formatData)
620: {
621: if (formatData == null)
622: {
623: throw new
624: NullPointerException("The supplied format data was null.");
625: }
626: this.formatData = formatData;
627: }
628:
629:
648: public boolean equals(Object o)
649: {
650: if (!super.equals(o))
651: return false;
652:
653: if (!(o instanceof SimpleDateFormat))
654: return false;
655:
656: SimpleDateFormat sdf = (SimpleDateFormat)o;
657:
658: if (defaultCentury != sdf.defaultCentury)
659: return false;
660:
661: if (!toPattern().equals(sdf.toPattern()))
662: return false;
663:
664: if (!getDateFormatSymbols().equals(sdf.getDateFormatSymbols()))
665: return false;
666:
667: return true;
668: }
669:
670:
675: public int hashCode()
676: {
677: return super.hashCode() ^ toPattern().hashCode() ^ defaultCentury ^
678: getDateFormatSymbols().hashCode();
679: }
680:
681:
682:
687: private void formatWithAttribute(Date date, FormatBuffer buffer, FieldPosition pos)
688: {
689: String temp;
690: AttributedCharacterIterator.Attribute attribute;
691: calendar.setTime(date);
692:
693:
694: Iterator iter = tokens.iterator();
695: while (iter.hasNext())
696: {
697: Object o = iter.next();
698: if (o instanceof CompiledField)
699: {
700: CompiledField cf = (CompiledField) o;
701: int beginIndex = buffer.length();
702:
703: switch (cf.getField())
704: {
705: case ERA_FIELD:
706: buffer.append (formatData.eras[calendar.get (Calendar.ERA)], DateFormat.Field.ERA);
707: break;
708: case YEAR_FIELD:
709:
710:
711: buffer.setDefaultAttribute (DateFormat.Field.YEAR);
712: if (cf.getSize() == 2)
713: {
714: temp = "00"+String.valueOf (calendar.get (Calendar.YEAR));
715: buffer.append (temp.substring (temp.length() - 2));
716: }
717: else
718: withLeadingZeros (calendar.get (Calendar.YEAR), cf.getSize(), buffer);
719: break;
720: case MONTH_FIELD:
721: buffer.setDefaultAttribute (DateFormat.Field.MONTH);
722: if (cf.getSize() < 3)
723: withLeadingZeros (calendar.get (Calendar.MONTH) + 1, cf.getSize(), buffer);
724: else if (cf.getSize() < 4)
725: buffer.append (formatData.shortMonths[calendar.get (Calendar.MONTH)]);
726: else
727: buffer.append (formatData.months[calendar.get (Calendar.MONTH)]);
728: break;
729: case DATE_FIELD:
730: buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_MONTH);
731: withLeadingZeros (calendar.get (Calendar.DATE), cf.getSize(), buffer);
732: break;
733: case HOUR_OF_DAY1_FIELD:
734: buffer.setDefaultAttribute(DateFormat.Field.HOUR_OF_DAY1);
735: withLeadingZeros ( ((calendar.get (Calendar.HOUR_OF_DAY) + 23) % 24) + 1,
736: cf.getSize(), buffer);
737: break;
738: case HOUR_OF_DAY0_FIELD:
739: buffer.setDefaultAttribute (DateFormat.Field.HOUR_OF_DAY0);
740: withLeadingZeros (calendar.get (Calendar.HOUR_OF_DAY), cf.getSize(), buffer);
741: break;
742: case MINUTE_FIELD:
743: buffer.setDefaultAttribute (DateFormat.Field.MINUTE);
744: withLeadingZeros (calendar.get (Calendar.MINUTE),
745: cf.getSize(), buffer);
746: break;
747: case SECOND_FIELD:
748: buffer.setDefaultAttribute (DateFormat.Field.SECOND);
749: withLeadingZeros(calendar.get (Calendar.SECOND),
750: cf.getSize(), buffer);
751: break;
752: case MILLISECOND_FIELD:
753: buffer.setDefaultAttribute (DateFormat.Field.MILLISECOND);
754: withLeadingZeros (calendar.get (Calendar.MILLISECOND), cf.getSize(), buffer);
755: break;
756: case DAY_OF_WEEK_FIELD:
757: buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK);
758: if (cf.getSize() < 4)
759: buffer.append (formatData.shortWeekdays[calendar.get (Calendar.DAY_OF_WEEK)]);
760: else
761: buffer.append (formatData.weekdays[calendar.get (Calendar.DAY_OF_WEEK)]);
762: break;
763: case DAY_OF_YEAR_FIELD:
764: buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_YEAR);
765: withLeadingZeros (calendar.get (Calendar.DAY_OF_YEAR), cf.getSize(), buffer);
766: break;
767: case DAY_OF_WEEK_IN_MONTH_FIELD:
768: buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK_IN_MONTH);
769: withLeadingZeros (calendar.get (Calendar.DAY_OF_WEEK_IN_MONTH),
770: cf.getSize(), buffer);
771: break;
772: case WEEK_OF_YEAR_FIELD:
773: buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_YEAR);
774: withLeadingZeros (calendar.get (Calendar.WEEK_OF_YEAR),
775: cf.getSize(), buffer);
776: break;
777: case WEEK_OF_MONTH_FIELD:
778: buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_MONTH);
779: withLeadingZeros (calendar.get (Calendar.WEEK_OF_MONTH),
780: cf.getSize(), buffer);
781: break;
782: case AM_PM_FIELD:
783: buffer.setDefaultAttribute (DateFormat.Field.AM_PM);
784: buffer.append (formatData.ampms[calendar.get (Calendar.AM_PM)]);
785: break;
786: case HOUR1_FIELD:
787: buffer.setDefaultAttribute (DateFormat.Field.HOUR1);
788: withLeadingZeros (((calendar.get (Calendar.HOUR) + 11) % 12) + 1,
789: cf.getSize(), buffer);
790: break;
791: case HOUR0_FIELD:
792: buffer.setDefaultAttribute (DateFormat.Field.HOUR0);
793: withLeadingZeros (calendar.get (Calendar.HOUR), cf.getSize(), buffer);
794: break;
795: case TIMEZONE_FIELD:
796: buffer.setDefaultAttribute (DateFormat.Field.TIME_ZONE);
797: TimeZone zone = calendar.getTimeZone();
798: boolean isDST = calendar.get (Calendar.DST_OFFSET) != 0;
799:
800: String zoneID = zone.getDisplayName
801: (isDST, cf.getSize() > 3 ? TimeZone.LONG : TimeZone.SHORT);
802: buffer.append (zoneID);
803: break;
804: case RFC822_TIMEZONE_FIELD:
805: buffer.setDefaultAttribute(DateFormat.Field.RFC822_TIME_ZONE);
806: int pureMinutes = (calendar.get(Calendar.ZONE_OFFSET) +
807: calendar.get(Calendar.DST_OFFSET)) / (1000 * 60);
808: String sign = (pureMinutes < 0) ? "-" : "+";
809: pureMinutes = Math.abs(pureMinutes);
810: int hours = pureMinutes / 60;
811: int minutes = pureMinutes % 60;
812: buffer.append(sign);
813: withLeadingZeros(hours, 2, buffer);
814: withLeadingZeros(minutes, 2, buffer);
815: break;
816: default:
817: throw new IllegalArgumentException ("Illegal pattern character " +
818: cf.getCharacter());
819: }
820: if (pos != null && (buffer.getDefaultAttribute() == pos.getFieldAttribute()
821: || cf.getField() == pos.getField()))
822: {
823: pos.setBeginIndex(beginIndex);
824: pos.setEndIndex(buffer.length());
825: }
826: }
827: else
828: {
829: buffer.append(o.toString(), null);
830: }
831: }
832: }
833:
834: public StringBuffer format(Date date, StringBuffer buffer, FieldPosition pos)
835: {
836: formatWithAttribute(date, new StringFormatBuffer (buffer), pos);
837:
838: return buffer;
839: }
840:
841: public AttributedCharacterIterator formatToCharacterIterator(Object date)
842: throws IllegalArgumentException
843: {
844: if (date == null)
845: throw new NullPointerException("null argument");
846: if (!(date instanceof Date))
847: throw new IllegalArgumentException("argument should be an instance of java.util.Date");
848:
849: AttributedFormatBuffer buf = new AttributedFormatBuffer();
850: formatWithAttribute((Date)date, buf,
851: null);
852: buf.sync();
853:
854: return new FormatCharacterIterator(buf.getBuffer().toString(),
855: buf.getRanges(),
856: buf.getAttributes());
857: }
858:
859: private void withLeadingZeros(int value, int length, FormatBuffer buffer)
860: {
861: String valStr = String.valueOf(value);
862: for (length -= valStr.length(); length > 0; length--)
863: