Currently DateTimeFormat.forPattern allows very simple exceptions, like "YYYY-MM-dd".
It often desirable to have regex like patterns -- alternations and optionals. Like:
YYYY-?MM-?dd
parses both 2009-04-12 and 20090412
HH:mm(:ss)?
should parse both 12:14 and 12:14:00
YYYY-MM-dd|dd.MM.YYYY
should parse both 2009-04-12 and 12.04.2009
First version of patch: optional (?) is implemented. Variants (|) is TBD. Documentation is missing too. Please, review.
===
Index: src/test/org/joda/time/format/TestDateTimeFormat.java
===================================================================
--- src/test/org/joda/time/format/TestDateTimeFormat.java (revision 1397)
+++ src/test/org/joda/time/format/TestDateTimeFormat.java (working copy)
@@ -27,6 +27,7 @@
import org.joda.time.DateTimeFieldType;
import org.joda.time.DateTimeUtils;
import org.joda.time.DateTimeZone;
+import org.joda.time.LocalTime;
import org.joda.time.chrono.GJChronology;
/**
@@ -982,6 +983,29 @@
DateTime date = dateFormatter.parseDateTime(str);
assertEquals(new DateTime(2007, 3, 8, 22, 0, 0, 0, UTC), date);
}
}
//-----------------------------------------------------------------------
public void testFormatParse_textHalfdayAM_UK() {
Index: src/java/org/joda/time/format/DateTimeFormat.java
===================================================================
--- src/java/org/joda/time/format/DateTimeFormat.java (revision 1397)
+++ src/java/org/joda/time/format/DateTimeFormat.java (working copy)
@@ -374,7 +374,9 @@
*/
static void appendPatternTo(DateTimeFormatterBuilder builder, String pattern) {
}
//-----------------------------------------------------------------------
@@ -395,12 +397,13 @@
*/
int length = pattern.length();
int[] indexRef = new int[1];
for (int i=0; i<length; i++) {
for (int i=pos; i<length; ) {
indexRef[0] = i;
String token = parseToken(pattern, indexRef);
i = indexRef[0];
@@ -425,15 +428,14 @@
boolean lenientParse = true;
if (i + 1 < length) {
// If next token is a number, cannot support
// lenient parse, because it will consume digits
// that it should not.
lenientParse = false;
}
indexRef[0]--;
}
@@ -453,13 +455,13 @@
int maxDigits = 9;
if (i + 1 < length) {
maxDigits = tokenLen;
}
indexRef[0]--;
}
@@ -555,11 +557,25 @@
builder.appendLiteral(new String(sub));
}
break;
case '(':
default:
throw new IllegalArgumentException
("Illegal pattern component: " + token);
}
}
return pattern.length();
}
/**
@@ -591,6 +607,8 @@
break;
}
}
} else if (c == '(' || c == ')' || c == '|' || c == '?') {
} else {
// This will identify token as text.
buf.append('\'');
@@ -609,7 +627,7 @@
inLiteral = !inLiteral;
}
} else if (!inLiteral &&
(c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '(' || c == ')' || c == '|' || c == '?')) {
i--;
break;
} else {
@@ -618,7 +636,7 @@
}
}
indexRef[0] = i;
return buf.toString();
}
@@ -679,7 +697,7 @@
formatter = (DateTimeFormatter) cPatternedCache.get(pattern);
if (formatter == null) {
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
appendPatternTo(builder, pattern);
formatter = builder.toFormatter();
Index: src/java/org/joda/time/format/DateTimeFormatterBuilder.java
--- src/java/org/joda/time/format/DateTimeFormatterBuilder.java (revision 1397)
+++ src/java/org/joda/time/format/DateTimeFormatterBuilder.java (working copy)
@@ -2656,4 +2656,21 @@
}
}
public void makeTailParserOptional() {
+
}
===
patch v1 — no variants, only optional
patch v2 — no variants, only optional, pattern yyyy-?MM now works
patch v3 — all features, not doc yet
patch v4 — all declared features, tests, doc
Attached version of the patch with all features implemented, with tests and with documentation.
I see what you are trying to achieve here, and it isn't necessarily a bad idea. It is backwards incompatible however with current patterns.
If I were to see something like this added to Joda-Time, I'd probably want to follow the model that I'd started in JSR-310.
So, I can't include it due to compatibility, and I'm not sure whether its too complex for Joda-Time in general.
Maybe it worth adding another method forPatternExt() that accepts extended pattern, keeping forPatten() intact?