64 |
*/ |
*/ |
65 |
public class SimpleDateFormat extends DateFormat |
public class SimpleDateFormat extends DateFormat |
66 |
{ |
{ |
67 |
/** A pair class used by SimpleDateFormat as a compiled representation |
/** |
68 |
* of a format string. |
* This class is used by <code>SimpleDateFormat</code> as a |
69 |
*/ |
* compiled representation of a format string. The field |
70 |
private class FieldSizePair |
* ID, size, and character used are stored for each sequence |
71 |
{ |
* of pattern characters or invalid characters in the format |
72 |
public int field; |
* pattern. |
73 |
public int size; |
*/ |
74 |
|
private class CompiledField |
75 |
|
{ |
76 |
|
/** |
77 |
|
* The ID of the field within the local pattern characters, |
78 |
|
* or -1 if the sequence is invalid. |
79 |
|
*/ |
80 |
|
private int field; |
81 |
|
|
82 |
|
/** |
83 |
|
* The size of the character sequence. |
84 |
|
*/ |
85 |
|
private int size; |
86 |
|
|
87 |
/** Constructs a pair with the given field and size values */ |
/** |
88 |
public FieldSizePair(int f, int s) { |
* The character used. |
89 |
|
*/ |
90 |
|
private char character; |
91 |
|
|
92 |
|
/** |
93 |
|
* Constructs a compiled field using the |
94 |
|
* the given field ID, size and character |
95 |
|
* values. |
96 |
|
* |
97 |
|
* @param f the field ID. |
98 |
|
* @param s the size of the field. |
99 |
|
* @param c the character used. |
100 |
|
*/ |
101 |
|
public CompiledField(int f, int s, char c) { |
102 |
field = f; |
field = f; |
103 |
size = s; |
size = s; |
104 |
|
character = c; |
105 |
|
} |
106 |
|
|
107 |
|
/** |
108 |
|
* Retrieves the ID of the field relative to |
109 |
|
* the local pattern characters, or -1 if |
110 |
|
* the sequence is invalid. |
111 |
|
*/ |
112 |
|
public int getField() |
113 |
|
{ |
114 |
|
return field; |
115 |
|
} |
116 |
|
|
117 |
|
/** |
118 |
|
* Retrieves the size of the character sequence. |
119 |
|
*/ |
120 |
|
public int getSize() |
121 |
|
{ |
122 |
|
return size; |
123 |
|
} |
124 |
|
|
125 |
|
/** |
126 |
|
* Retrieves the character used in the sequence. |
127 |
|
*/ |
128 |
|
public char getCharacter() |
129 |
|
{ |
130 |
|
return character; |
131 |
|
} |
132 |
|
|
133 |
|
/** |
134 |
|
* Returns a <code>String</code> representation |
135 |
|
* of the compiled field, primarily for debugging |
136 |
|
* purposes. |
137 |
|
* |
138 |
|
* @return a <code>String</code> representation. |
139 |
|
*/ |
140 |
|
public String toString() |
141 |
|
{ |
142 |
|
StringBuilder builder; |
143 |
|
|
144 |
|
builder = new StringBuilder(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 |
|
|
162 |
private int serialVersionOnStream = 1; // 0 indicates JDK1.1.3 or earlier |
private int serialVersionOnStream = 1; // 0 indicates JDK1.1.3 or earlier |
163 |
private static final long serialVersionUID = 4774881970558875024L; |
private static final long serialVersionUID = 4774881970558875024L; |
164 |
|
|
165 |
// This string is specified in the JCL. We set it here rather than |
// This string is specified in the root of the CLDR. We set it here |
166 |
// do a DateFormatSymbols(Locale.US).getLocalPatternChars() since |
// rather than doing a DateFormatSymbols(Locale.US).getLocalPatternChars() |
167 |
// someone could theoretically change those values (though unlikely). |
// since someone could theoretically change those values (though unlikely). |
168 |
private static final String standardChars = "GyMdkHmsSEDFwWahKzZ"; |
private static final String standardChars = "GyMdkHmsSEDFwWahKzYeugAZ"; |
169 |
|
|
170 |
private void readObject(ObjectInputStream stream) |
private void readObject(ObjectInputStream stream) |
171 |
throws IOException, ClassNotFoundException |
throws IOException, ClassNotFoundException |
193 |
char thisChar; |
char thisChar; |
194 |
int pos; |
int pos; |
195 |
int field; |
int field; |
196 |
FieldSizePair current = null; |
CompiledField current = null; |
197 |
|
|
198 |
for (int i=0; i<pattern.length(); i++) { |
for (int i=0; i<pattern.length(); i++) { |
199 |
thisChar = pattern.charAt(i); |
thisChar = pattern.charAt(i); |
203 |
if ((thisChar >= 'A' && thisChar <= 'Z') |
if ((thisChar >= 'A' && thisChar <= 'Z') |
204 |
|| (thisChar >= 'a' && thisChar <= 'z')) { |
|| (thisChar >= 'a' && thisChar <= 'z')) { |
205 |
// Not a valid letter |
// Not a valid letter |
206 |
tokens.add(new FieldSizePair(-1,0)); |
tokens.add(new CompiledField(-1,0,thisChar)); |
207 |
} else if (thisChar == '\'') { |
} else if (thisChar == '\'') { |
208 |
// Quoted text section; skip to next single quote |
// Quoted text section; skip to next single quote |
209 |
pos = pattern.indexOf('\'',i+1); |
pos = pattern.indexOf('\'',i+1); |
210 |
if (pos == -1) { |
if (pos == -1) { |
211 |
// This ought to be an exception, but spec does not |
// This ought to be an exception, but spec does not |
212 |
// let us throw one. |
// let us throw one. |
213 |
tokens.add(new FieldSizePair(-1,0)); |
tokens.add(new CompiledField(-1,0,thisChar)); |
214 |
} |
} |
215 |
if ((pos+1 < pattern.length()) && (pattern.charAt(pos+1) == '\'')) { |
if ((pos+1 < pattern.length()) && (pattern.charAt(pos+1) == '\'')) { |
216 |
tokens.add(pattern.substring(i+1,pos+1)); |
tokens.add(pattern.substring(i+1,pos+1)); |
227 |
if ((current != null) && (field == current.field)) { |
if ((current != null) && (field == current.field)) { |
228 |
current.size++; |
current.size++; |
229 |
} else { |
} else { |
230 |
current = new FieldSizePair(field,1); |
current = new CompiledField(field,1,thisChar); |
231 |
tokens.add(current); |
tokens.add(current); |
232 |
} |
} |
233 |
} |
} |
514 |
while (iter.hasNext()) |
while (iter.hasNext()) |
515 |
{ |
{ |
516 |
Object o = iter.next(); |
Object o = iter.next(); |
517 |
if (o instanceof FieldSizePair) |
if (o instanceof CompiledField) |
518 |
{ |
{ |
519 |
FieldSizePair p = (FieldSizePair) o; |
CompiledField cf = (CompiledField) o; |
520 |
int beginIndex = buffer.length(); |
int beginIndex = buffer.length(); |
521 |
|
|
522 |
switch (p.field) |
switch (cf.getField()) |
523 |
{ |
{ |
524 |
case ERA_FIELD: |
case ERA_FIELD: |
525 |
buffer.append (formatData.eras[calendar.get (Calendar.ERA)], DateFormat.Field.ERA); |
buffer.append (formatData.eras[calendar.get (Calendar.ERA)], DateFormat.Field.ERA); |
528 |
// If we have two digits, then we truncate. Otherwise, we |
// If we have two digits, then we truncate. Otherwise, we |
529 |
// use the size of the pattern, and zero pad. |
// use the size of the pattern, and zero pad. |
530 |
buffer.setDefaultAttribute (DateFormat.Field.YEAR); |
buffer.setDefaultAttribute (DateFormat.Field.YEAR); |
531 |
if (p.size == 2) |
if (cf.getSize() == 2) |
532 |
{ |
{ |
533 |
temp = String.valueOf (calendar.get (Calendar.YEAR)); |
temp = String.valueOf (calendar.get (Calendar.YEAR)); |
534 |
buffer.append (temp.substring (temp.length() - 2)); |
buffer.append (temp.substring (temp.length() - 2)); |
535 |
} |
} |
536 |
else |
else |
537 |
withLeadingZeros (calendar.get (Calendar.YEAR), p.size, buffer); |
withLeadingZeros (calendar.get (Calendar.YEAR), cf.getSize(), buffer); |
538 |
break; |
break; |
539 |
case MONTH_FIELD: |
case MONTH_FIELD: |
540 |
buffer.setDefaultAttribute (DateFormat.Field.MONTH); |
buffer.setDefaultAttribute (DateFormat.Field.MONTH); |
541 |
if (p.size < 3) |
if (cf.getSize() < 3) |
542 |
withLeadingZeros (calendar.get (Calendar.MONTH) + 1, p.size, buffer); |
withLeadingZeros (calendar.get (Calendar.MONTH) + 1, cf.getSize(), buffer); |
543 |
else if (p.size < 4) |
else if (cf.getSize() < 4) |
544 |
buffer.append (formatData.shortMonths[calendar.get (Calendar.MONTH)]); |
buffer.append (formatData.shortMonths[calendar.get (Calendar.MONTH)]); |
545 |
else |
else |
546 |
buffer.append (formatData.months[calendar.get (Calendar.MONTH)]); |
buffer.append (formatData.months[calendar.get (Calendar.MONTH)]); |
547 |
break; |
break; |
548 |
case DATE_FIELD: |
case DATE_FIELD: |
549 |
buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_MONTH); |
buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_MONTH); |
550 |
withLeadingZeros (calendar.get (Calendar.DATE), p.size, buffer); |
withLeadingZeros (calendar.get (Calendar.DATE), cf.getSize(), buffer); |
551 |
break; |
break; |
552 |
case HOUR_OF_DAY1_FIELD: // 1-24 |
case HOUR_OF_DAY1_FIELD: // 1-24 |
553 |
buffer.setDefaultAttribute(DateFormat.Field.HOUR_OF_DAY1); |
buffer.setDefaultAttribute(DateFormat.Field.HOUR_OF_DAY1); |
554 |
withLeadingZeros ( ((calendar.get (Calendar.HOUR_OF_DAY) + 23) % 24) + 1, |
withLeadingZeros ( ((calendar.get (Calendar.HOUR_OF_DAY) + 23) % 24) + 1, |
555 |
p.size, buffer); |
cf.getSize(), buffer); |
556 |
break; |
break; |
557 |
case HOUR_OF_DAY0_FIELD: // 0-23 |
case HOUR_OF_DAY0_FIELD: // 0-23 |
558 |
buffer.setDefaultAttribute (DateFormat.Field.HOUR_OF_DAY0); |
buffer.setDefaultAttribute (DateFormat.Field.HOUR_OF_DAY0); |
559 |
withLeadingZeros (calendar.get (Calendar.HOUR_OF_DAY), p.size, buffer); |
withLeadingZeros (calendar.get (Calendar.HOUR_OF_DAY), cf.getSize(), buffer); |
560 |
break; |
break; |
561 |
case MINUTE_FIELD: |
case MINUTE_FIELD: |
562 |
buffer.setDefaultAttribute (DateFormat.Field.MINUTE); |
buffer.setDefaultAttribute (DateFormat.Field.MINUTE); |
563 |
withLeadingZeros (calendar.get (Calendar.MINUTE), |
withLeadingZeros (calendar.get (Calendar.MINUTE), |
564 |
p.size, buffer); |
cf.getSize(), buffer); |
565 |
break; |
break; |
566 |
case SECOND_FIELD: |
case SECOND_FIELD: |
567 |
buffer.setDefaultAttribute (DateFormat.Field.SECOND); |
buffer.setDefaultAttribute (DateFormat.Field.SECOND); |
568 |
withLeadingZeros(calendar.get (Calendar.SECOND), |
withLeadingZeros(calendar.get (Calendar.SECOND), |
569 |
p.size, buffer); |
cf.getSize(), buffer); |
570 |
break; |
break; |
571 |
case MILLISECOND_FIELD: |
case MILLISECOND_FIELD: |
572 |
buffer.setDefaultAttribute (DateFormat.Field.MILLISECOND); |
buffer.setDefaultAttribute (DateFormat.Field.MILLISECOND); |
573 |
withLeadingZeros (calendar.get (Calendar.MILLISECOND), p.size, buffer); |
withLeadingZeros (calendar.get (Calendar.MILLISECOND), cf.getSize(), buffer); |
574 |
break; |
break; |
575 |
case DAY_OF_WEEK_FIELD: |
case DAY_OF_WEEK_FIELD: |
576 |
buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK); |
buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK); |
577 |
if (p.size < 4) |
if (cf.getSize() < 4) |
578 |
buffer.append (formatData.shortWeekdays[calendar.get (Calendar.DAY_OF_WEEK)]); |
buffer.append (formatData.shortWeekdays[calendar.get (Calendar.DAY_OF_WEEK)]); |
579 |
else |
else |
580 |
buffer.append (formatData.weekdays[calendar.get (Calendar.DAY_OF_WEEK)]); |
buffer.append (formatData.weekdays[calendar.get (Calendar.DAY_OF_WEEK)]); |
581 |
break; |
break; |
582 |
case DAY_OF_YEAR_FIELD: |
case DAY_OF_YEAR_FIELD: |
583 |
buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_YEAR); |
buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_YEAR); |
584 |
withLeadingZeros (calendar.get (Calendar.DAY_OF_YEAR), p.size, buffer); |
withLeadingZeros (calendar.get (Calendar.DAY_OF_YEAR), cf.getSize(), buffer); |
585 |
break; |
break; |
586 |
case DAY_OF_WEEK_IN_MONTH_FIELD: |
case DAY_OF_WEEK_IN_MONTH_FIELD: |
587 |
buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK_IN_MONTH); |
buffer.setDefaultAttribute (DateFormat.Field.DAY_OF_WEEK_IN_MONTH); |
588 |
withLeadingZeros (calendar.get (Calendar.DAY_OF_WEEK_IN_MONTH), |
withLeadingZeros (calendar.get (Calendar.DAY_OF_WEEK_IN_MONTH), |
589 |
p.size, buffer); |
cf.getSize(), buffer); |
590 |
break; |
break; |
591 |
case WEEK_OF_YEAR_FIELD: |
case WEEK_OF_YEAR_FIELD: |
592 |
buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_YEAR); |
buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_YEAR); |
593 |
withLeadingZeros (calendar.get (Calendar.WEEK_OF_YEAR), |
withLeadingZeros (calendar.get (Calendar.WEEK_OF_YEAR), |
594 |
p.size, buffer); |
cf.getSize(), buffer); |
595 |
break; |
break; |
596 |
case WEEK_OF_MONTH_FIELD: |
case WEEK_OF_MONTH_FIELD: |
597 |
buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_MONTH); |
buffer.setDefaultAttribute (DateFormat.Field.WEEK_OF_MONTH); |
598 |
withLeadingZeros (calendar.get (Calendar.WEEK_OF_MONTH), |
withLeadingZeros (calendar.get (Calendar.WEEK_OF_MONTH), |
599 |
p.size, buffer); |
cf.getSize(), buffer); |
600 |
break; |
break; |
601 |
case AM_PM_FIELD: |
case AM_PM_FIELD: |
602 |
buffer.setDefaultAttribute (DateFormat.Field.AM_PM); |
buffer.setDefaultAttribute (DateFormat.Field.AM_PM); |
604 |
break; |
break; |
605 |
case HOUR1_FIELD: // 1-12 |
case HOUR1_FIELD: // 1-12 |
606 |
buffer.setDefaultAttribute (DateFormat.Field.HOUR1); |
buffer.setDefaultAttribute (DateFormat.Field.HOUR1); |
607 |
withLeadingZeros (((calendar.get (Calendar.HOUR) + 11) % 12) + 1, p.size, buffer); |
withLeadingZeros (((calendar.get (Calendar.HOUR) + 11) % 12) + 1, |
608 |
|
cf.getSize(), buffer); |
609 |
break; |
break; |
610 |
case HOUR0_FIELD: // 0-11 |
case HOUR0_FIELD: // 0-11 |
611 |
buffer.setDefaultAttribute (DateFormat.Field.HOUR0); |
buffer.setDefaultAttribute (DateFormat.Field.HOUR0); |
612 |
withLeadingZeros (calendar.get (Calendar.HOUR), p.size, buffer); |
withLeadingZeros (calendar.get (Calendar.HOUR), cf.getSize(), buffer); |
613 |
break; |
break; |
614 |
case TIMEZONE_FIELD: |
case TIMEZONE_FIELD: |
615 |
buffer.setDefaultAttribute (DateFormat.Field.TIME_ZONE); |
buffer.setDefaultAttribute (DateFormat.Field.TIME_ZONE); |
616 |
TimeZone zone = calendar.getTimeZone(); |
TimeZone zone = calendar.getTimeZone(); |
617 |
boolean isDST = calendar.get (Calendar.DST_OFFSET) != 0; |
boolean isDST = calendar.get (Calendar.DST_OFFSET) != 0; |
618 |
// FIXME: XXX: This should be a localized time zone. |
// FIXME: XXX: This should be a localized time zone. |
619 |
String zoneID = zone.getDisplayName (isDST, p.size > 3 ? TimeZone.LONG : TimeZone.SHORT); |
String zoneID = zone.getDisplayName |
620 |
|
(isDST, cf.getSize() > 3 ? TimeZone.LONG : TimeZone.SHORT); |
621 |
buffer.append (zoneID); |
buffer.append (zoneID); |
622 |
break; |
break; |
623 |
|
case RFC822_TIMEZONE_FIELD: |
624 |
|
buffer.setDefaultAttribute(DateFormat.Field.RFC822_TIME_ZONE); |
625 |
|
int pureMinutes = (calendar.get(Calendar.ZONE_OFFSET) + |
626 |
|
calendar.get(Calendar.DST_OFFSET)) / (1000 * 60); |
627 |
|
String sign = (pureMinutes < 0) ? "-" : "+"; |
628 |
|
int hours = pureMinutes / 60; |
629 |
|
int minutes = pureMinutes % 60; |
630 |
|
buffer.append(sign); |
631 |
|
withLeadingZeros(hours, 2, buffer); |
632 |
|
withLeadingZeros(minutes, 2, buffer); |
633 |
|
break; |
634 |
default: |
default: |
635 |
throw new IllegalArgumentException ("Illegal pattern character " + p.field); |
throw new IllegalArgumentException ("Illegal pattern character " + |
636 |
|
cf.getCharacter()); |
637 |
} |
} |
638 |
if (pos != null && (buffer.getDefaultAttribute() == pos.getFieldAttribute() |
if (pos != null && (buffer.getDefaultAttribute() == pos.getFieldAttribute() |
639 |
|| p.field == pos.getField())) |
|| cf.getField() == pos.getField())) |
640 |
{ |
{ |
641 |
pos.setBeginIndex(beginIndex); |
pos.setBeginIndex(beginIndex); |
642 |
pos.setEndIndex(buffer.length()); |
pos.setEndIndex(buffer.length()); |