chinabugotech/hutool · error · ParseException
(The {} locale does not support dates before 1868 AD) Unpars
Error message
(The {} locale does not support dates before 1868 AD)
Unparseable date: "{} What it means
Thrown by FastDateParser.parse(String) when the Japanese Imperial calendar locale (ja_JP_JP / Locale with JAPANESE_IMPERIAL calendar) is in use and the date being parsed resolves to a year before 1868 AD, which precedes the Meiji era and has no representation in that calendar system. It wraps the generic 'Unparseable date' with a locale-specific note.
Source
Thrown at hutool-core/src/main/java/cn/hutool/core/date/format/FastDateParser.java:219
* @param in ObjectInputStream from which the object is being deserialized.
* @throws IOException if there is an IO issue.
* @throws ClassNotFoundException if a class cannot be found.
*/
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
final Calendar definingCalendar = Calendar.getInstance(timeZone, locale);
init(definingCalendar);
}
@Override
public Date parse(String source) throws ParseException {
final ParsePosition pp = new ParsePosition(0);
final Date date = parse(source, pp);
if (date == null) {
// Add a note re supported date range
if (locale.equals(JAPANESE_IMPERIAL)) {
throw new ParseException("(The " + locale + " locale does not support dates before 1868 AD)\n" +
"Unparseable date: \"" + source, pp.getErrorIndex());
}
throw new ParseException("Unparseable date: " + source, pp.getErrorIndex());
}
return date;
}
@Override
public Date parse(String source, ParsePosition pos) {
// timing tests indicate getting new instance is 19% faster than cloning
final Calendar cal = Calendar.getInstance(timeZone, locale);
cal.clear();
return parse(source, pos, cal) ? cal.getTime() : null;
}
@Override
public boolean parse(String source, ParsePosition pos, Calendar calendar) {View on GitHub (pinned to 8870454b2a)
Solutions
- Parse with a Gregorian/proleptic locale (e.g. Locale.US or Locale.JAPAN) instead of the imperial Japanese locale.
- Filter or reject dates before 1868 before attempting parse in the imperial locale.
- Set the JVM default locale away from ja_JP_JP if it was inherited unintentionally.
Example fix
// before
FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd", tz, new Locale("ja","JP","JP"));
fdf.parse("1800-01-01");
// after
FastDateFormat fdf = FastDateFormat.getInstance("yyyy-MM-dd", tz, Locale.US);
fdf.parse("1800-01-01"); Defensive patterns
Strategy: validation
Validate before calling
boolean isImperial(Locale l){ return l!=null && "JP".equals(l.getCountry()) && "ja".equals(l.getLanguage()) && "JP".equals(l.getVariant()); }
// if isImperial(locale) && parsedYear<1868 -> use a Gregorian locale Try / catch
try { return fdf.parse(source); }
catch (ParseException e) { if (e.getMessage().contains("does not support dates before 1868")) { return gregorianFdf.parse(source); } throw e; } Prevention
- Pin the parsing locale to a Gregorian one for historical dates.
- Avoid inheriting the JVM default locale for date parsing.
- Document locale requirements in your config schema.
When it happens
Trigger: Parsing a string such as '1800-01-01' with a FastDateFormat configured for Locale.JAPANESE_IMPERIAL (typically Locale = new Locale("ja","JP","JP") which triggers the Japanese Imperial calendar). Any parsed year < 1868 triggers it, even if the pattern itself matches.
Common situations: Server/app default Locale set to Japanese Imperial via config or container locale negotiation; legacy data containing historical dates being parsed in a locale-aware component; tests run on a JVM whose default locale resolves to the imperial calendar.
Related errors
- Unterminated quote
- Unparseable date: {}
- Format '{}' not supported
- No date time pattern for locale: {}
- Can not convert {}:[{}] to {}
AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14).
Data as JSON: /api/errors/4c0c5e3fae448779.
Report an issue: GitHub.