@@ -49,11 +49,14 @@ public class StdDateFormat
4949
5050 protected final static Pattern PATTERN_PLAIN = Pattern .compile (PATTERN_PLAIN_STR );
5151
52+ // [databind#5429]: Extended year format (4+ digits, optional +/- prefix)
53+ protected final static String PATTERN_YEAR_STR = "(?:[+-]?\\ d{4,})" ;
54+
5255 protected final static Pattern PATTERN_ISO8601 ;
5356 static {
5457 Pattern p = null ;
5558 try {
56- p = Pattern .compile (PATTERN_PLAIN_STR
59+ p = Pattern .compile (PATTERN_YEAR_STR + "[-] \\ d \\ d[-] \\ d \\ d"
5760 +"[T]\\ d\\ d[:]\\ d\\ d(?:[:]\\ d\\ d)?" // hours, minutes, optional seconds
5861 +"(\\ .\\ d+)?" // optional second fractions
5962 +"(Z|[+-]\\ d\\ d(?:[:]?\\ d\\ d)?)?" // optional timeoffset/Z
@@ -607,13 +610,17 @@ public int hashCode() {
607610 */
608611 protected boolean looksLikeISO8601 (String dateStr )
609612 {
610- if (dateStr .length () >= 7 // really need 10, but...
611- && Character .isDigit (dateStr .charAt (0 ))
612- && Character .isDigit (dateStr .charAt (3 ))
613- && dateStr .charAt (4 ) == '-'
614- && Character .isDigit (dateStr .charAt (5 ))
615- ) {
616- return true ;
613+ if (dateStr .length () >= 7 ) { // really need 10, but...
614+ final char c = dateStr .charAt (0 );
615+ // [databind#5429]: extended year may have +/- prefix
616+ if (c == '+' || c == '-' ) {
617+ return (dateStr .length () >= 11 )
618+ && Character .isDigit (dateStr .charAt (1 ));
619+ }
620+ return Character .isDigit (c )
621+ && Character .isDigit (dateStr .charAt (3 ))
622+ && dateStr .charAt (4 ) == '-'
623+ && Character .isDigit (dateStr .charAt (5 ));
617624 }
618625 return false ;
619626 }
@@ -670,8 +677,20 @@ protected Date _parseAsISO8601(String dateStr, ParsePosition bogus)
670677 } else {
671678 Matcher m = PATTERN_ISO8601 .matcher (dateStr );
672679 if (m .matches ()) {
673- // Important! START with optional time zone; otherwise Calendar will explode
680+ // [databind#5429]: handle extended year (5+ digits with optional +/- prefix)
681+ // by locating where year ends (first hyphen for year-month separator)
682+ int yearEnd = (dateStr .charAt (0 ) == '-' ) ? dateStr .indexOf ('-' , 1 ) : dateStr .indexOf ('-' );
683+ int year = (yearEnd <= 4 ) ? _parse4D (dateStr , 0 )
684+ : Integer .parseInt (dateStr .substring (0 , yearEnd ));
685+ final int offset = yearEnd - 4 ; // adjustment for extended year
686+ int month = _parse2D (dateStr , 5 + offset )-1 ;
687+ int day = _parse2D (dateStr , 8 + offset );
688+ int hour = _parse2D (dateStr , 11 + offset );
689+ int minute = _parse2D (dateStr , 14 + offset );
690+ int seconds = ((totalLen > (16 + offset )) && dateStr .charAt (16 + offset ) == ':' )
691+ ? _parse2D (dateStr , 17 + offset ) : 0 ;
674692
693+ // Important! START with optional time zone; otherwise Calendar will explode
675694 int start = m .start (2 );
676695 int end = m .end (2 );
677696 int len = end -start ;
@@ -691,21 +710,6 @@ protected Date _parseAsISO8601(String dateStr, ParsePosition bogus)
691710 cal .set (Calendar .DST_OFFSET , 0 );
692711 }
693712
694- int year = _parse4D (dateStr , 0 );
695- int month = _parse2D (dateStr , 5 )-1 ;
696- int day = _parse2D (dateStr , 8 );
697-
698- // So: 10 chars for date, then `T`, so starts at 11
699- int hour = _parse2D (dateStr , 11 );
700- int minute = _parse2D (dateStr , 14 );
701-
702- // Seconds are actually optional... so
703- int seconds ;
704- if ((totalLen > 16 ) && dateStr .charAt (16 ) == ':' ) {
705- seconds = _parse2D (dateStr , 17 );
706- } else {
707- seconds = 0 ;
708- }
709713 cal .set (year , month , day , hour , minute , seconds );
710714
711715 // Optional milliseconds
0 commit comments