Skip to content

fix: support negative year (BC date) deserialization#4036

Open
daguimu wants to merge 1 commit into
alibaba:mainfrom
daguimu:fix/negative-year-datetime-issue3845
Open

fix: support negative year (BC date) deserialization#4036
daguimu wants to merge 1 commit into
alibaba:mainfrom
daguimu:fix/negative-year-datetime-issue3845

Conversation

@daguimu
Copy link
Copy Markdown

@daguimu daguimu commented Mar 25, 2026

Problem

LocalDateTime with negative years (BC dates) serializes correctly (e.g. "-0001-12-31 16:00:00") but fails to deserialize with:

JSONException: read LocalDateTime error -0001-12-31 16:00:00

Reproduction:

LocalDateTime time = LocalDateTime.of(0, 1, 1, 0, 0, 0, 0).minusHours(8);
JSONObject bean = new JSONObject().fluentPut("time", time);
String s = bean.toJSONString();  // {"time":"-0001-12-31 16:00:00"}
JSON.parseObject(s).getLocalDateTime("time");  // throws JSONException

Root Cause

The negative year date string "-0001-12-31 16:00:00" has length 20 (due to the leading -). The readLocalDateTime() method dispatches length-20 strings to parseLocalDateTime20(), which expects a completely different format (dd Mon yyyy HH:mm:ss). This returns null, and the string eventually falls through to an error.

Fix

Added handling for the -yyyy-MM-dd HH:mm:ss format in the string fallback path of readLocalDateTime(). The fix:

  • Checks if the string is 20 chars starting with -
  • Validates the format matches -yyyy-MM-dd HH:mm:ss (supporting both and T separators, and / delimiters)
  • Manually parses the year, month, day, hour, minute, second components
  • Follows the same manual parsing style used throughout the codebase

Tests Added

  • testNegativeYearRoundTrip — Serialize then deserialize a BC date, verify equality
  • testNegativeYearDirectParse — Parse "-0001-12-31 16:00:00" and verify all components
  • testNegativeYearWithT — Parse with T separator: "-0001-12-31T16:00:00"
  • testNegativeYearWithSlash — Parse with / delimiter: "-0001/12/31 16:00:00"
  • testNegativeYearLarger — Year -100 (101 BC) round-trip
  • testPositiveYearStillWorks — Regression test for normal dates

Impact

Minimal — only affects deserialization of negative year datetime strings (20 chars starting with -). Normal date parsing is unaffected.

Fixes #3845

…Time

LocalDateTime with negative years (e.g. year -1 = 2 BC) serializes as
"-0001-12-31 16:00:00" (20 chars). The readLocalDateTime method dispatched
this to parseLocalDateTime20 which expects a different date format,
causing it to fail with "read LocalDateTime error".

Add handling for the -yyyy-MM-dd HH:mm:ss format in the string fallback
path, supporting both space and T separators, and / delimiters.

Fixes alibaba#3845
char c17 = str.charAt(17);
if ((c5 == '-' || c5 == '/') && (c8 == '-' || c8 == '/')
&& (c11 == ' ' || c11 == 'T') && c14 == ':' && c17 == ':') {
int year = -(((str.charAt(1) - '0') * 1000)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Critical] The new negative-year parser computes numeric fields with str.charAt(i) - '0' but does not validate that those positions are actually digits, and the time fields only have upper-bound checks. Inputs with valid separators but non-digit numeric characters can be coerced into different valid values instead of being rejected; for example, -0001-12-3/ 16:00:00 can parse the day as 29 because '/' - '0' == -1. Please validate every numeric position before computing the fields, or use existing digit helpers that reject non-digits, and require lower bounds for hour/minute/second as well.

— gpt-5.5 via Qwen Code /review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]公元前时间反序列化出错

2 participants