Java SimpleDateFormat cannot handle valid ISO8601 time zone strings, fixed

This commit is contained in:
Andrew Donald Kennedy 2012-02-07 18:48:01 +00:00
parent 241a33b5c8
commit 3623918de5
2 changed files with 49 additions and 23 deletions

View File

@ -27,14 +27,15 @@ import java.util.regex.Pattern;
*/
public class DateUtils {
public static final Pattern MILLIS_PATTERN = Pattern.compile("(.*\\.[0-9][0-9][0-9])[0-9]*Z?");
public static final Pattern MILLIS_PATTERN = Pattern.compile("(.*\\.[0-9][0-9][0-9])[0-9]*");
public static final Pattern TZ_PATTERN = Pattern.compile("(.*)[+-][0-9][0-9]:?[0-9][0-9]Z?");
// This regexp will match all TZ forms that are valid is ISO 8601
public static final Pattern TZ_PATTERN = Pattern.compile("(.*)([+-][0-9][0-9](:?[0-9][0-9])?|Z)");
public static String trimToMillis(String toParse) {
Matcher matcher = MILLIS_PATTERN.matcher(toParse);
if (matcher.find()) {
toParse = matcher.group(1) + 'Z';
toParse = matcher.group(1);
}
return toParse;
}
@ -44,11 +45,25 @@ public class DateUtils {
public static String trimTZ(String toParse) {
Matcher matcher = TZ_PATTERN.matcher(toParse);
if (matcher.find()) {
toParse = matcher.group(1) + 'Z';
toParse = matcher.group(1);
}
// TODO explain why this check is here
if (toParse.length() == 25 && SECOND_PATTERN.matcher(toParse).matches())
toParse = toParse.substring(0, toParse.length() - 6) + 'Z';
toParse = toParse.substring(0, toParse.length() - 6);
return toParse;
}
public static String findTZ(String toParse) {
Matcher matcher = TZ_PATTERN.matcher(toParse);
if (matcher.find()) {
// Remove ':' from the TZ string, as SimpleDateFormat can't handle it
String tz = matcher.group(2).replace(":", "");
// Append '00; if we only have a two digit TZ, as SimpleDateFormat
if (tz.length() == 2) tz += "00";
return tz;
} else {
return "";
}
}
}

View File

@ -17,8 +17,7 @@
* under the License.
*/
package org.jclouds.date.internal;
import static org.jclouds.date.internal.DateUtils.trimToMillis;
import static org.jclouds.date.internal.DateUtils.trimTZ;
import static org.jclouds.date.internal.DateUtils.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@ -42,20 +41,16 @@ public class SimpleDateFormatDateService implements DateService {
* guard against the lack of thread safety.
*/
// @GuardedBy("this")
private static final SimpleDateFormat iso8601SecondsSimpleDateFormat = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
private static final SimpleDateFormat iso8601SecondsSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
// @GuardedBy("this")
private static final SimpleDateFormat iso8601SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
Locale.US);
private static final SimpleDateFormat iso8601SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US);
// @GuardedBy("this")
private static final SimpleDateFormat rfc822SimpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z",
Locale.US);
private static final SimpleDateFormat rfc822SimpleDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
// @GuardedBy("this")
private static final SimpleDateFormat cSimpleDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss '+0000' yyyy",
Locale.US);
private static final SimpleDateFormat cSimpleDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss '+0000' yyyy", Locale.US);
static {
iso8601SimpleDateFormat.setTimeZone(new SimpleTimeZone(0, "GMT"));
@ -64,83 +59,99 @@ public class SimpleDateFormatDateService implements DateService {
cSimpleDateFormat.setTimeZone(new SimpleTimeZone(0, "GMT"));
}
@Override
public final Date fromSeconds(long seconds) {
return new Date(seconds * 1000);
}
@Override
public final String cDateFormat(Date date) {
synchronized (cSimpleDateFormat) {
return cSimpleDateFormat.format(date);
}
}
@Override
public final String cDateFormat() {
return cDateFormat(new Date());
}
@Override
public final Date cDateParse(String toParse) {
synchronized (cSimpleDateFormat) {
try {
return cSimpleDateFormat.parse(toParse);
} catch (ParseException e) {
throw new RuntimeException(e);
} catch (ParseException pe) {
throw new RuntimeException("Error parsing data at " + pe.getErrorOffset(), pe);
}
}
}
@Override
public final String rfc822DateFormat(Date date) {
synchronized (rfc822SimpleDateFormat) {
return rfc822SimpleDateFormat.format(date);
}
}
@Override
public final String rfc822DateFormat() {
return rfc822DateFormat(new Date());
}
@Override
public final Date rfc822DateParse(String toParse) {
synchronized (rfc822SimpleDateFormat) {
try {
return rfc822SimpleDateFormat.parse(toParse);
} catch (ParseException e) {
throw new RuntimeException(e);
} catch (ParseException pe) {
throw new RuntimeException("Error parsing data at " + pe.getErrorOffset(), pe);
}
}
}
@Override
public final String iso8601SecondsDateFormat() {
return iso8601SecondsDateFormat(new Date());
}
@Override
public final String iso8601DateFormat(Date date) {
synchronized (iso8601SimpleDateFormat) {
return iso8601SimpleDateFormat.format(date);
}
}
@Override
public final String iso8601DateFormat() {
return iso8601DateFormat(new Date());
}
@Override
public final Date iso8601DateParse(String toParse) {
String tz = findTZ(toParse);
toParse = trimTZ(toParse);
toParse = trimToMillis(toParse);
toParse += tz; // Usable TZ added back
synchronized (iso8601SimpleDateFormat) {
try {
return iso8601SimpleDateFormat.parse(toParse);
} catch (ParseException e) {
throw new RuntimeException(e);
} catch (ParseException pe) {
throw new RuntimeException("Error parsing data at " + pe.getErrorOffset(), pe);
}
}
}
@Override
public final Date iso8601SecondsDateParse(String toParse) {
String tz = findTZ(toParse);
toParse = trimTZ(toParse);
toParse += tz; // Usable TZ added back
synchronized (iso8601SecondsSimpleDateFormat) {
try {
return iso8601SecondsSimpleDateFormat.parse(toParse);
} catch (ParseException e) {
throw new RuntimeException(e);
} catch (ParseException pe) {
throw new RuntimeException("Error parsing data at " + pe.getErrorOffset(), pe);
}
}
}