@@ -118,6 +118,18 @@ public class SimpleDateFormat extends DateFormat {
118118 * Pattern character for RFC 822-style timezone.
119119 */
120120 private static final char TIMEZONE822_LETTER = 'Z' ;
121+ /**
122+ * Pattern character for ISO 8601 timezone.
123+ */
124+ private static final char ISO_TIMEZONE_LETTER = 'X' ;
125+ /**
126+ * Pattern character for week year.
127+ */
128+ private static final char WEEK_YEAR_LETTER = 'Y' ;
129+ /**
130+ * Pattern character for day number of week.
131+ */
132+ private static final char DAY_NUMBER_OF_WEEK_LETTER = 'u' ;
121133 /**
122134 * Internally used character for literal text.
123135 */
@@ -141,7 +153,7 @@ public class SimpleDateFormat extends DateFormat {
141153 /**
142154 * Pattern characters recognized by this implementation (same as JDK 1.6).
143155 */
144- private static final String PATTERN_LETTERS = "adDEFGHhKkMmsSwWyzZ " ;
156+ private static final String PATTERN_LETTERS = "adDEFGHhKkMmsSwWyzZXYu " ;
145157 /**
146158 * TimeZone ID for Greenwich Mean Time
147159 */
@@ -371,7 +383,24 @@ String format(Date source, StringBuilder toAppendTo) {
371383 toAppendTo .append (leftPad (v / 60 , 2 ));
372384 toAppendTo .append (leftPad (v % 60 , 2 ));
373385 break ;
386+ case ISO_TIMEZONE_LETTER :
387+ v = getOffsetInMinutes (calendar , calendar .getTimeZone ());
388+ if (v < 0 ) {
389+ toAppendTo .append (SIGN_NEGATIVE );
390+ v = -v ;
391+ } else {
392+ toAppendTo .append (SIGN_POSITIVE );
393+ }
394+ toAppendTo .append (leftPad (v / 60 , 2 ));
395+ if (len >= 2 ) {
396+ if (len == 3 ) {
397+ toAppendTo .append (':' );
398+ }
399+ toAppendTo .append (leftPad (v % 60 , 2 ));
400+ }
401+ break ;
374402 case YEAR_LETTER :
403+ case WEEK_YEAR_LETTER :
375404 v = calendar .get (Calendar .YEAR );
376405 if (len == 2 ) {
377406 v %= 100 ;
@@ -441,6 +470,10 @@ String format(Date source, StringBuilder toAppendTo) {
441470 v = calendar .get (DAY_OF_WEEK_IN_MONTH );
442471 toAppendTo .append (leftPad (v , len ));
443472 break ;
473+ case DAY_NUMBER_OF_WEEK_LETTER :
474+ v = ((calendar .get (Calendar .DAY_OF_WEEK ) + 5 ) % 7 ) + 1 ;
475+ toAppendTo .append (leftPad (v , len ));
476+ break ;
444477 }
445478 }
446479 return toAppendTo .toString ();
@@ -515,6 +548,7 @@ public Date parse(String source) throws ParseException {
515548 break ;
516549 case TIMEZONE_LETTER :
517550 case TIMEZONE822_LETTER :
551+ case ISO_TIMEZONE_LETTER :
518552 s = readTimeZone (source , startIndex );
519553 TimeZoneResult res = new TimeZoneResult ();
520554 if (s == null || (v = parseTimeZone (s , startIndex , res )) == -1 ) {
@@ -525,6 +559,7 @@ public Date parse(String source) throws ParseException {
525559 tzMinutes = ((tzMinutes == -1 ) ? 0 : tzMinutes ) + v ;
526560 break ;
527561 case YEAR_LETTER :
562+ case WEEK_YEAR_LETTER :
528563 s = readNumber (source , startIndex , token , adjacent );
529564 calendar .set (Calendar .YEAR , parseYear (s , token , startIndex ));
530565 break ;
@@ -572,6 +607,14 @@ public Date parse(String source) throws ParseException {
572607 calendar .set (DAY_OF_WEEK_IN_MONTH ,
573608 parseNumber (s , startIndex , "day of week in month" , -5 , 5 ));
574609 break ;
610+ case DAY_NUMBER_OF_WEEK_LETTER :
611+ s = readNumber (source , startIndex , token , adjacent );
612+ v = parseNumber (s , startIndex , "day number of week" , 1 , 7 );
613+ // Map 1(Mon)-7(Sun) to Calendar 2(Mon)..1(Sun)
614+ // Mon=1 -> 2. Tue=2 -> 3. ... Sat=6 -> 7. Sun=7 -> 1.
615+ // v % 7 + 1
616+ calendar .set (Calendar .DAY_OF_WEEK , (v % 7 ) + 1 );
617+ break ;
575618 }
576619 if (s != null ) {
577620 startIndex += s .length ();
@@ -1186,14 +1229,27 @@ List<String> parseDatePattern(String pattern) {
11861229 char ch = pattern .charAt (i );
11871230 // Handle literal text enclosed in quotes
11881231 if (ch == EXPLICIT_LITERAL ) {
1189- int n = pattern .indexOf (EXPLICIT_LITERAL , i + 1 );
1190- if (n != -1 ) {
1191- if (tmp != null ) {
1192- tokens .add (tmp .charAt (0 ) + tmp );
1193- tmp = null ;
1232+ if (tmp != null ) {
1233+ tokens .add (tmp .charAt (0 ) + tmp );
1234+ tmp = null ;
1235+ }
1236+ StringBuilder sb = new StringBuilder ();
1237+ int n = i + 1 ;
1238+ while (n < plen ) {
1239+ char c = pattern .charAt (n );
1240+ if (c == EXPLICIT_LITERAL ) {
1241+ if (n + 1 < plen && pattern .charAt (n + 1 ) == EXPLICIT_LITERAL ) {
1242+ sb .append (EXPLICIT_LITERAL );
1243+ n += 2 ;
1244+ continue ;
1245+ } else {
1246+ break ;
1247+ }
11941248 }
1195- tokens .add (LITERAL_LETTER + pattern .substring (i + 1 , n ));
1249+ sb .append (c );
1250+ n ++;
11961251 }
1252+ tokens .add (LITERAL_LETTER + sb .toString ());
11971253 i = n ;
11981254 continue ;
11991255 }
@@ -1208,7 +1264,7 @@ List<String> parseDatePattern(String pattern) {
12081264 int n ;
12091265 for (n = i ; n < plen ; n ++) {
12101266 ch = pattern .charAt (n );
1211- if (PATTERN_LETTERS .indexOf (ch ) != -1 ) {
1267+ if (PATTERN_LETTERS .indexOf (ch ) != -1 || ch == EXPLICIT_LITERAL ) {
12121268 break ;
12131269 }
12141270 if (isAlpha (ch )) {
0 commit comments