Improve datetime parser

This commit is contained in:
Martin Fietz 2015-07-29 18:30:29 +02:00
parent af2ded8fe0
commit 66e5c4fdf1
2 changed files with 42 additions and 5 deletions

View File

@ -13,6 +13,7 @@ public class DateUtilsTest extends AndroidTestCase {
public void testParseDateWithMicroseconds() throws Exception { public void testParseDateWithMicroseconds() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4); GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4);
exp.setTimeZone(TimeZone.getTimeZone("UTC"));
Date expected = new Date(exp.getTimeInMillis() + 963); Date expected = new Date(exp.getTimeInMillis() + 963);
Date actual = DateUtils.parse("2015-03-28T13:31:04.963870"); Date actual = DateUtils.parse("2015-03-28T13:31:04.963870");
assertEquals(expected, actual); assertEquals(expected, actual);
@ -20,6 +21,7 @@ public class DateUtilsTest extends AndroidTestCase {
public void testParseDateWithCentiseconds() throws Exception { public void testParseDateWithCentiseconds() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4); GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4);
exp.setTimeZone(TimeZone.getTimeZone("UTC"));
Date expected = new Date(exp.getTimeInMillis() + 960); Date expected = new Date(exp.getTimeInMillis() + 960);
Date actual = DateUtils.parse("2015-03-28T13:31:04.96"); Date actual = DateUtils.parse("2015-03-28T13:31:04.96");
assertEquals(expected, actual); assertEquals(expected, actual);
@ -27,6 +29,7 @@ public class DateUtilsTest extends AndroidTestCase {
public void testParseDateWithDeciseconds() throws Exception { public void testParseDateWithDeciseconds() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4); GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4);
exp.setTimeZone(TimeZone.getTimeZone("UTC"));
Date expected = new Date(exp.getTimeInMillis() + 900); Date expected = new Date(exp.getTimeInMillis() + 900);
Date actual = DateUtils.parse("2015-03-28T13:31:04.9"); Date actual = DateUtils.parse("2015-03-28T13:31:04.9");
assertEquals(expected.getTime()/1000, actual.getTime()/1000); assertEquals(expected.getTime()/1000, actual.getTime()/1000);
@ -82,4 +85,20 @@ public class DateUtilsTest extends AndroidTestCase {
assertEquals(expected, actual); assertEquals(expected, actual);
} }
public void testAsctime() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2011, 4, 25, 12, 33, 00);
exp.setTimeZone(TimeZone.getTimeZone("UTC"));
Date expected = new Date(exp.getTimeInMillis());
Date actual = DateUtils.parse("Wed, 25 May 2011 12:33:00");
assertEquals(expected, actual);
}
public void testMultipleConsecutiveSpaces() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2010, 2, 23, 6, 6, 26);
exp.setTimeZone(TimeZone.getTimeZone("UTC"));
Date expected = new Date(exp.getTimeInMillis());
Date actual = DateUtils.parse("Tue, 23 Mar 2010 01:06:26 -0500");
assertEquals(expected, actual);
}
} }

View File

@ -8,6 +8,7 @@ import java.text.ParsePosition;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone;
/** /**
* Parses several date formats. * Parses several date formats.
@ -17,50 +18,66 @@ public class DateUtils {
private static final String TAG = "DateUtils"; private static final String TAG = "DateUtils";
private static final SimpleDateFormat parser = new SimpleDateFormat("", Locale.US); private static final SimpleDateFormat parser = new SimpleDateFormat("", Locale.US);
private static final TimeZone defaultTimezone = TimeZone.getTimeZone("GMT");
static { static {
parser.setLenient(false); parser.setLenient(false);
parser.setTimeZone(defaultTimezone);
} }
public static Date parse(final String input) { public static Date parse(final String input) {
if(input == null) { if(input == null) {
throw new IllegalArgumentException("Date most not be null"); throw new IllegalArgumentException("Date must not be null");
} }
String date = input.replace('/', '-'); String date = input.trim().replace('/', '-').replaceAll("( ){2,}+", " ");
// if datetime is more precise than seconds, make sure the value is in ms
if(date.contains(".")) { if(date.contains(".")) {
int start = date.indexOf('.'); int start = date.indexOf('.');
int current = start+1; int current = start+1;
while(current < date.length() && Character.isDigit(date.charAt(current))) { while(current < date.length() && Character.isDigit(date.charAt(current))) {
current++; current++;
} }
// even more precise than microseconds: discard further decimal places
if(current - start > 4) { if(current - start > 4) {
if(current < date.length()-1) { if(current < date.length()-1) {
date = date.substring(0, start + 4) + date.substring(current); date = date.substring(0, start + 4) + date.substring(current);
} else { } else {
date = date.substring(0, start + 4); date = date.substring(0, start + 4);
} }
// less than 4 decimal places: pad to have a consistent format for the parser
} else if(current - start < 4) { } else if(current - start < 4) {
if(current < date.length()-1) { if(current < date.length()-1) {
date = date.substring(0, current) + StringUtils.repeat("0", 4-(current-start)) + date.substring(current); date = date.substring(0, current) + StringUtils.repeat("0", 4-(current-start)) + date.substring(current);
} else { } else {
date = date.substring(0, current) + StringUtils.repeat("0", 4-(current-start)); date = date.substring(0, current) + StringUtils.repeat("0", 4-(current-start));
} }
} }
} }
String[] patterns = { String[] patterns = {
"dd MMM yy HH:mm:ss Z", "dd MMM yy HH:mm:ss Z",
"dd MMM yy HH:mm Z", "dd MMM yy HH:mm Z",
"EEE, dd MMM yyyy HH:mm:ss Z", "EEE, dd MMM yyyy HH:mm:ss Z",
"EEE, dd MMM yyyy HH:mm:ss",
"EEE, dd MMMM yyyy HH:mm:ss Z", "EEE, dd MMMM yyyy HH:mm:ss Z",
"EEE, dd MMMM yyyy HH:mm:ss",
"EEEE, dd MMM yyyy HH:mm:ss Z",
"EEEE, dd MMM yy HH:mm:ss Z", "EEEE, dd MMM yy HH:mm:ss Z",
"EEEE, dd MMM yyyy HH:mm:ss",
"EEEE, dd MMM yy HH:mm:ss",
"EEE MMM d HH:mm:ss yyyy", "EEE MMM d HH:mm:ss yyyy",
"EEE, dd MMM yyyy HH:mm Z", "EEE, dd MMM yyyy HH:mm Z",
"EEE, dd MMM yyyy HH:mm",
"EEE, dd MMMM yyyy HH:mm Z", "EEE, dd MMMM yyyy HH:mm Z",
"EEE, dd MMMM yyyy HH:mm",
"EEEE, dd MMM yyyy HH:mm Z",
"EEEE, dd MMM yy HH:mm Z", "EEEE, dd MMM yy HH:mm Z",
"EEEE, dd MMM yyyy HH:mm",
"EEEE, dd MMM yy HH:mm",
"EEE MMM d HH:mm yyyy", "EEE MMM d HH:mm yyyy",
"yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd'T'HH:mm:ss",
"yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ss.SSS Z", "yyyy-MM-dd'T'HH:mm:ss.SSS Z",
"yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss'Z'", "yyyy-MM-dd'T'HH:mm:ss'Z'",
"yyyy-MM-ddZ", "yyyy-MM-ddZ",
@ -77,7 +94,7 @@ public class DateUtils {
} }
} }
Log.d(TAG, "Could not parse date string \"" + input + "\""); Log.d(TAG, "Could not parse date string \"" + input + "\" [" + date + "]");
return null; return null;
} }
@ -117,6 +134,7 @@ public class DateUtils {
public static String formatRFC3339UTC(Date date) { public static String formatRFC3339UTC(Date date) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
format.setTimeZone(defaultTimezone);
return format.format(date); return format.format(date);
} }
} }