chinabugotech/hutool · error · IllegalArgumentException
Unterminated quote
Error message
Unterminated quote
What it means
Thrown when constructing a FastDateParser from a pattern that contains an opening single quote (') with no matching closing quote. In SimpleDateFormat-style patterns, a single quote begins a literal/quoted section that must be terminated by another single quote. The parser reaches end-of-pattern while the quote is still 'active', so the format object can never be built.
Source
Thrown at hutool-core/src/main/java/cn/hutool/core/date/format/FastDateParser.java:183
private StrategyAndWidth literal() {
boolean activeQuote = false;
final StringBuilder sb = new StringBuilder();
while (currentIdx < pattern.length()) {
final char c = pattern.charAt(currentIdx);
if (!activeQuote && isFormatLetter(c)) {
break;
} else if (c == '\'' && (++currentIdx == pattern.length() || pattern.charAt(currentIdx) != '\'')) {
activeQuote = !activeQuote;
continue;
}
++currentIdx;
sb.append(c);
}
if (activeQuote) {
throw new IllegalArgumentException("Unterminated quote");
}
final String formatField = sb.toString();
return new StrategyAndWidth(new CopyQuotedStrategy(formatField), formatField.length());
}
}
private static boolean isFormatLetter(final char c) {
return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z';
}
// Serializing
// -----------------------------------------------------------------------
/**
* Create the object after serialization. This implementation reinitializes the transient properties.
*
* @param in ObjectInputStream from which the object is being deserialized.View on GitHub (pinned to 8870454b2a)
Solutions
- Balance the quotes: every opening ' must have a closing '. For a literal apostrophe inside the pattern use two consecutive quotes, e.g. "HH''mm".
- Validate the pattern before constructing the parser (unit test, or FastDateFormat.getInstance(pattern) in a try/catch at startup).
- If the quote is intentional literal text, ensure the literal segment is terminated, e.g. "yyyy'year'".
Example fix
// before
FastDateFormat fdf = FastDateFormat.getInstance("dd 'MM");
// after
FastDateFormat fdf = FastDateFormat.getInstance("dd 'MM'");
// or, for a literal apostrophe:
FastDateFormat fdf = FastDateFormat.getInstance("HH''mm"); Defensive patterns
Strategy: validation
Validate before calling
private static final Pattern BALANCED = Pattern.compile("^(?:[^']|'(?:[^']|'')*')*$");
void checkPattern(String p){
// simple heuristic: count of unescaped quotes must be even
int q=0; for(int i=0;i<p.length();i++){ if(p.charAt(i)=='\''){ if(i+1<p.length() && p.charAt(i+1)=='\''){ i++; continue; } q++; } }
if((q&1)!=0) throw new IllegalArgumentException("unbalanced quote in pattern: "+p);
} Try / catch
try { FastDateFormat fdf = FastDateFormat.getInstance(pattern); }
catch (IllegalArgumentException e) { if (e.getMessage().contains("Unterminated quote")) { /* fix pattern */ } else throw e; } Prevention
- Unit-test every externally-supplied date pattern at startup.
- Use doubled single quotes ('') for literal apostrophes.
- Never build patterns by blind concatenation of user input.
When it happens
Trigger: Calling DateUtil.parse / new FastDateFormat (or DatePattern-based parsing) with a pattern like "dd 'MM" (only one quote), "HH''mm'ss" (unbalanced), or any pattern where a ' opens a literal that runs to the end of the string. Also triggered when a quote is meant to escape a format letter but is never closed.
Common situations: User-supplied or config-driven date patterns built by string concatenation; patterns localized from resource bundles that lose a quote during editing; patterns that attempt to embed an apostrophe (e.g. "o'clock") but only add one quote instead of two ('' for a literal apostrophe).
Related errors
- Format '{}' not supported
- Unparseable date: {}
- invalid number of X
- Illegal pattern component: {}
- invalid number of X
AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14).
Data as JSON: /api/errors/8f1cd5e916646720.
Report an issue: GitHub.