Skip to content

Commit

Permalink
Fix #570
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Oct 3, 2014
1 parent a4849c1 commit 5e799a2
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 13 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Version: 2.4.3 (02-Oct-2014)
#541: @JsonProperty in @JsonCreator is conflicting with POJOs getters/attributes
(reported by fabienrenaud@github)
#543: Problem resolving self-referential generic types
#570: Add Support for Parsing All Compliant ISO-8601 Date Formats
(requested by pfconrey@github)
- Fixed a problem with `acceptJsonFormatVisitor` with Collection/array types that
are marked with `@JsonValue`; could cause NPE in JSON Schema generator module.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public class StdDateFormat
* JDK date parsing is awfully brittle, and ISO-8601 is quite
* permissive. The two don't mix, need to write a better one.
*/
// 02-Oct-2014, tatu: Alas. While spit'n'polished a few times, still
// not really robust

/**
* Defines a commonly used date format that conforms
Expand Down Expand Up @@ -398,31 +400,49 @@ protected Date parseAsISO8601(String dateStr, ParsePosition pos)
// let's just append '00'
dateStr += "00";
}
// [JACKSON-334]: may be missing milliseconds... if so, add
// Milliseconds partial or missing; and even seconds are optional
len = dateStr.length();
// '+0000' (5 chars); should come after '.000' (4 chars) of milliseconds, so:
c = dateStr.charAt(len-9);
if (Character.isDigit(c)) {
// remove 'T', '+'/'-' and 4-digit timezone-offset
int timeLen = len - dateStr.lastIndexOf('T') - 6;
if (timeLen < 12) { // 8 for hh:mm:ss, 4 for .sss
int offset = len - 5; // insertion offset, before tz-offset
StringBuilder sb = new StringBuilder(dateStr);
sb.insert(len-5, ".000");
switch (timeLen) {
case 11:
sb.insert(offset, '0'); break;
case 10:
sb.insert(offset, "00"); break;
case 9: // is this legal? (just second fraction marker)
sb.insert(offset, "000"); break;
case 8:
sb.insert(offset, ".000"); break;
case 7: // not legal to have single-digit second
break;
case 6: // probably not legal, but let's allow
sb.insert(offset, "00.000");
case 5: // is legal to omit seconds
sb.insert(offset, ":00.000");
}
dateStr = sb.toString();
}

df = _formatISO8601;
if (_formatISO8601 == null) {
df = _formatISO8601 = _cloneFormat(DATE_FORMAT_ISO8601, DATE_FORMAT_STR_ISO8601, _timezone, _locale);
}
} else {
/* 24-Nov-2009, tatu: Ugh. This is getting pretty
* ugly. Need to rewrite!
*/

// If not, plain date. Easiest to just patch 'Z' in the end?
StringBuilder sb = new StringBuilder(dateStr);
// And possible also millisecond part if missing
int timeLen = len - dateStr.lastIndexOf('T') - 1;
if (timeLen <= 8) {
sb.append(".000");
if (timeLen < 12) { // missing, or partial
switch (timeLen) {
case 11: sb.append('0');
case 10: sb.append('0');
case 9: sb.append('0');
break;
default:
sb.append(".000");
}
}
sb.append('Z');
dateStr = sb.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,61 @@ public void testDateUtilISO8601() throws Exception
assertEquals(30, c.get(Calendar.DAY_OF_MONTH));
}

// [Databind#570]
public void testISO8601PartialMilliseconds() throws Exception
{
String inputStr;
Date inputDate;
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

inputStr = "2014-10-03T18:00:00.6-05:00";
inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
c.setTime(inputDate);
assertEquals(2014, c.get(Calendar.YEAR));
assertEquals(Calendar.OCTOBER, c.get(Calendar.MONTH));
assertEquals(3, c.get(Calendar.DAY_OF_MONTH));
assertEquals(600, c.get(Calendar.MILLISECOND));

inputStr = "2014-10-03T18:00:00.61-05:00";
inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
c.setTime(inputDate);
assertEquals(2014, c.get(Calendar.YEAR));
assertEquals(Calendar.OCTOBER, c.get(Calendar.MONTH));
assertEquals(3, c.get(Calendar.DAY_OF_MONTH));
assertEquals(18 + 5, c.get(Calendar.HOUR_OF_DAY));
assertEquals(0, c.get(Calendar.MINUTE));
assertEquals(0, c.get(Calendar.SECOND));
assertEquals(610, c.get(Calendar.MILLISECOND));

inputStr = "1997-07-16T19:20:30.45+01:00";
inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
c.setTime(inputDate);
assertEquals(1997, c.get(Calendar.YEAR));
assertEquals(Calendar.JULY, c.get(Calendar.MONTH));
assertEquals(16, c.get(Calendar.DAY_OF_MONTH));
assertEquals(19 - 1, c.get(Calendar.HOUR_OF_DAY));
assertEquals(20, c.get(Calendar.MINUTE));
assertEquals(30, c.get(Calendar.SECOND));
assertEquals(450, c.get(Calendar.MILLISECOND));
}

public void testISO8601MissingSeconds() throws Exception
{
String inputStr;
Date inputDate;
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

inputStr = "1997-07-16T19:20+01:00";
inputDate = MAPPER.readValue(quote(inputStr), java.util.Date.class);
c.setTime(inputDate);
assertEquals(1997, c.get(Calendar.YEAR));
assertEquals(Calendar.JULY, c.get(Calendar.MONTH));
assertEquals(16, c.get(Calendar.DAY_OF_MONTH));
assertEquals(19 - 1, c.get(Calendar.HOUR_OF_DAY));
assertEquals(0, c.get(Calendar.SECOND));
assertEquals(0, c.get(Calendar.MILLISECOND));
}

public void testDateUtilISO8601NoTimezone() throws Exception
{
// Timezone itself is optional as well...
Expand Down Expand Up @@ -180,7 +235,7 @@ public void testDateUtilISO8601NoMilliseconds() throws Exception
assertEquals(0, c.get(Calendar.MILLISECOND));

// 03-Nov-2013, tatu: This wouldn't work, and is the nominal reason
// for #338 I thinl
// for #338 I think
/*
inputDate = ISO8601Utils.parse(INPUT_STR);
c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
Expand Down

0 comments on commit 5e799a2

Please sign in to comment.