sharanggupta opened a new pull request, #4400:
URL: https://github.com/apache/logging-log4j2/pull/4400

   Fixes #4399.
   
   Instants before the epoch were mishandled in two places, because a 
truncating `/` or `%` was used where a floor division is needed:
   
   * `MutableInstant.initFromEpochMilli(long, int)` split a negative 
`epochMilli` with `/ 1000`, so `-61_500 ms` became `epochSecond=-61, 
nanoOfSecond=-500_000_000`. That breaks the `0 <= nanoOfSecond < 1_000_000_000` 
invariant that `initFromEpochSecond` enforces and every consumer expects. It 
now uses `Math.floorDiv`, which gives `epochSecond=-62, 
nanoOfSecond=500_000_000`.
   * `InstantPatternDynamicFormatter` keyed its per-minute cache with 
`epochSecond / 60` and rendered the seconds with `epochSecond % 60`. Before the 
epoch the seconds were negative and the wrong cached minute was selected: 
`%d{HH:mm:ss.SSS}` printed `23:59:+'.000` for `-59_000 ms` instead of 
`23:59:01.000`. These now use `Math.floorDiv` and `Math.floorMod`.
   
   Both methods exist since Java 8, so there is no change to the supported 
baseline. For instants after the epoch the results are identical, because floor 
and truncating division agree for non-negative operands.
   
   Deliberately not included: `InstantPatternThreadLocalCachedFormatter` starts 
its cache with the epoch instant `-1`, so the first event on a thread at 
exactly second (or millisecond) `-1` prints an empty string. That is described 
under "Related" in #4399. I left it out because #4322 rewrites that class.
   
   ## Tests
   
   * `MutableInstantTest.testInitFromEpochMilliNormalizesTheNanoOfSecond`: 
seven `(epochMilli, nanoOfMilli)` cases, checking the normalized second and 
nano and that `getEpochMillisecond()` and `getNanoOfMillisecond()` round-trip. 
Four fail on `2.x`; the whole-second case and the two cases after the epoch 
pass before and after.
   * 
`InstantPatternDynamicFormatterTest.output_before_the_epoch_should_match_DateTimeFormatter`:
 six patterns (`s`, `ss`, `mm:ss`, `HH:mm:ss.SSS`, with micros, with an offset) 
times eight instants before the epoch, compared with `DateTimeFormatter` as the 
existing `output_should_match_DateTimeFormatter` does. 42 of the 48 cases fail 
on `2.x`; the six that pass are the `-60 s` cases, which fall on a minute 
boundary.
   
   ## Verification
   
   * The `core.time`, `core.util.internal.instant`, `core.util.datetime`, 
`core.pattern`, `core.layout` and `core.impl` test packages of 
`log4j-core-test` pass in the default Surefire execution (8,675 tests, 0 
failures), and spotless is green on all modules. The 
`test-without-jackson-databind` execution of this module cannot load the 
Jackson-based layout tests that my package filter selects, so I read the 
results per execution.
   * Outside the repository, I compared `InstantPatternDynamicFormatter` with 
`DateTimeFormatter` over random patterns, zones, locales and instants from year 
0 to 9999, about 710,000 comparisons over three seeds. After this change the 
only differences are zones whose offset has a seconds part (for example 
`Africa/Monrovia` before 1972, which `InstantPatternDynamicFormatterTest` 
already excludes) and the `-1` case above.
   * I did not run the complete `./mvnw verify` of the whole reactor locally 
and rely on CI for it.
   
   Generative-AI disclosure (ASF guidance): Claude Code (Anthropic) assisted in 
finding the problem, drafting the change, tests and this description; the 
result was reviewed, the new tests were confirmed to fail on `2.x` and pass 
with the change, and the tests and checks above were run before opening this PR.
   
   ## Checklist
   
   * [x] Base your changes on `2.x` branch if you are targeting Log4j 2; use 
`main` otherwise
   * [ ] `./mvnw verify` succeeds: not run for the whole reactor locally, see 
Verification above
   * [x] Non-trivial changes contain an entry file in the 
`src/changelog/.2.x.x` directory
   * [x] Tests are provided (`MutableInstantTest`: 1 new parameterized test, 
`InstantPatternDynamicFormatterTest`: 1 new parameterized test)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to