@@ -38,11 +38,14 @@ public class StdDateFormat
3838
3939 protected final static Pattern PATTERN_PLAIN = Pattern .compile (PATTERN_PLAIN_STR );
4040
41+ // [databind#5429]: Extended year format (4+ digits, optional +/- prefix)
42+ protected final static String PATTERN_YEAR_STR = "(?:[+-]?\\ d{4,})" ;
43+
4144 protected final static Pattern PATTERN_ISO8601 ;
4245 static {
4346 Pattern p = null ;
4447 try {
45- p = Pattern .compile (PATTERN_PLAIN_STR
48+ p = Pattern .compile (PATTERN_YEAR_STR + "[-] \\ d \\ d[-] \\ d \\ d"
4649 +"[T]\\ d\\ d[:]\\ d\\ d(?:[:]\\ d\\ d)?" // hours, minutes, optional seconds
4750 +"(\\ .\\ d+)?" // optional second fractions
4851 +"(Z|[+-]\\ d\\ d(?:[:]?\\ d\\ d)?)?" // optional timeoffset/Z
@@ -529,13 +532,17 @@ public int hashCode() {
529532 */
530533 protected boolean looksLikeISO8601 (String dateStr )
531534 {
532- if (dateStr .length () >= 7 // really need 10, but...
533- && Character .isDigit (dateStr .charAt (0 ))
534- && Character .isDigit (dateStr .charAt (3 ))
535- && dateStr .charAt (4 ) == '-'
536- && Character .isDigit (dateStr .charAt (5 ))
537- ) {
538- return true ;
535+ if (dateStr .length () >= 7 ) { // really need 10, but...
536+ final char c = dateStr .charAt (0 );
537+ // [databind#5429]: extended year may have +/- prefix
538+ if (c == '+' || c == '-' ) {
539+ return (dateStr .length () >= 11 )
540+ && Character .isDigit (dateStr .charAt (1 ));
541+ }
542+ return Character .isDigit (c )
543+ && Character .isDigit (dateStr .charAt (3 ))
544+ && dateStr .charAt (4 ) == '-'
545+ && Character .isDigit (dateStr .charAt (5 ));
539546 }
540547 return false ;
541548 }
@@ -592,8 +599,20 @@ protected Date _parseAsISO8601(String dateStr, ParsePosition bogus)
592599 } else {
593600 Matcher m = PATTERN_ISO8601 .matcher (dateStr );
594601 if (m .matches ()) {
595- // Important! START with optional time zone; otherwise Calendar will explode
602+ // [databind#5429]: handle extended year (5+ digits with optional +/- prefix)
603+ // by locating where year ends (first hyphen for year-month separator)
604+ int yearEnd = (dateStr .charAt (0 ) == '-' ) ? dateStr .indexOf ('-' , 1 ) : dateStr .indexOf ('-' );
605+ int year = (yearEnd <= 4 ) ? _parse4D (dateStr , 0 )
606+ : Integer .parseInt (dateStr .substring (0 , yearEnd ));
607+ final int offset = yearEnd - 4 ; // adjustment for extended year
608+ int month = _parse2D (dateStr , 5 + offset )-1 ;
609+ int day = _parse2D (dateStr , 8 + offset );
610+ int hour = _parse2D (dateStr , 11 + offset );
611+ int minute = _parse2D (dateStr , 14 + offset );
612+ int seconds = ((totalLen > (16 + offset )) && dateStr .charAt (16 + offset ) == ':' )
613+ ? _parse2D (dateStr , 17 + offset ) : 0 ;
596614
615+ // Important! START with optional time zone; otherwise Calendar will explode
597616 int start = m .start (2 );
598617 int end = m .end (2 );
599618 int len = end -start ;
@@ -613,21 +632,6 @@ protected Date _parseAsISO8601(String dateStr, ParsePosition bogus)
613632 cal .set (Calendar .DST_OFFSET , 0 );
614633 }
615634
616- int year = _parse4D (dateStr , 0 );
617- int month = _parse2D (dateStr , 5 )-1 ;
618- int day = _parse2D (dateStr , 8 );
619-
620- // So: 10 chars for date, then `T`, so starts at 11
621- int hour = _parse2D (dateStr , 11 );
622- int minute = _parse2D (dateStr , 14 );
623-
624- // Seconds are actually optional... so
625- int seconds ;
626- if ((totalLen > 16 ) && dateStr .charAt (16 ) == ':' ) {
627- seconds = _parse2D (dateStr , 17 );
628- } else {
629- seconds = 0 ;
630- }
631635 cal .set (year , month , day , hour , minute , seconds );
632636
633637 // Optional milliseconds
0 commit comments