chinabugotech/hutool · error · IllegalArgumentException
invalid number of X
Error message
invalid number of X
What it means
The ISO-8601 timezone token 'X' in a FastDateParser pattern only supports lengths 1, 2, or 3 (X, XX, XXX). Any other run length (e.g. XXXX or XXXXX) reaches the default branch and throws. Standard java.text.SimpleDateFormat allows 1-5 X's, so this is a reduced feature set.
Source
Thrown at hutool-core/src/main/java/cn/hutool/core/date/format/FastDateParser.java:775
private static final Strategy ISO_8601_2_STRATEGY = new ISO8601TimeZoneStrategy("(Z|(?:[+-]\\d{2}\\d{2}))");
private static final Strategy ISO_8601_3_STRATEGY = new ISO8601TimeZoneStrategy("(Z|(?:[+-]\\d{2}(?::)\\d{2}))");
/**
* Factory method for ISO8601TimeZoneStrategies.
*
* @param tokenLen a token indicating the length of the TimeZone String to be formatted.
* @return a ISO8601TimeZoneStrategy that can format TimeZone String of length {@code tokenLen}. If no such strategy exists, an IllegalArgumentException will be thrown.
*/
static Strategy getStrategy(final int tokenLen) {
switch (tokenLen) {
case 1:
return ISO_8601_1_STRATEGY;
case 2:
return ISO_8601_2_STRATEGY;
case 3:
return ISO_8601_3_STRATEGY;
default:
throw new IllegalArgumentException("invalid number of X");
}
}
}
private static final Strategy NUMBER_MONTH_STRATEGY = new NumberStrategy(Calendar.MONTH) {
@Override
int modify(final FastDateParser parser, final int iValue) {
return iValue - 1;
}
};
private static final Strategy LITERAL_YEAR_STRATEGY = new NumberStrategy(Calendar.YEAR);
private static final Strategy WEEK_OF_YEAR_STRATEGY = new NumberStrategy(Calendar.WEEK_OF_YEAR);
private static final Strategy WEEK_OF_MONTH_STRATEGY = new NumberStrategy(Calendar.WEEK_OF_MONTH);
private static final Strategy DAY_OF_YEAR_STRATEGY = new NumberStrategy(Calendar.DAY_OF_YEAR);
private static final Strategy DAY_OF_MONTH_STRATEGY = new NumberStrategy(Calendar.DAY_OF_MONTH);
private static final Strategy DAY_OF_WEEK_STRATEGY = new NumberStrategy(Calendar.DAY_OF_WEEK) {
@Override
int modify(final FastDateParser parser, final int iValue) {View on GitHub (pinned to 8870454b2a)
Solutions
- Reduce the X run to at most 3 (XXX gives '+hh:mm' / 'Z').
- If XXXX/XXXXX semantics are needed, use java.text.SimpleDateFormat for that format instead of FastDateParser.
- Use Z or z tokens for zone formatting/parsing where appropriate.
Example fix
// before
FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssXXXXX");
// after
FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssXXX"); Defensive patterns
Strategy: validation
Validate before calling
String sanitizeX(String p){ return p.replaceAll("X{4,}", "XXX"); } // collapse long X runs
// or reject:
void assertShortX(String p){ if(p.matches(".*X{4,5}.*")) throw new IllegalArgumentException("FastDateParser supports at most XXX"); } Try / catch
try { FastDateFormat.getInstance(pattern); }
catch (IllegalArgumentException e){ if(e.getMessage().contains("invalid number of X")) { pattern = pattern.replaceAll("X{4,}","XXX"); } else throw e; } Prevention
- Prefer XXX for ISO-8601 offsets in Hutool patterns.
- Document the 1-3 X limit vs java.text.SimpleDateFormat.
- Validate patterns during onboarding/config load.
When it happens
Trigger: A pattern containing four or five consecutive X characters (XXXX / XXXXX), e.g. 'yyyy-MM-dd'T'HH:mm:ssXXXXX'. Lengths outside {1,2,3} are rejected at parser construction time.
Common situations: Patterns copied from ISO-8601 examples or RFC 3339 that use the full 5-X form (e.g. for '+hh:mm' with optional colon and 'Z'); migrating code from SimpleDateFormat to Hutool's FastDateFormat.
Related errors
- invalid number of X
- Unterminated quote
- Format '{}' not supported
- Illegal pattern component: {}
- Unparseable date: {}
AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14).
Data as JSON: /api/errors/6b4ed7a2a0813b11.
Report an issue: GitHub.