Repository navigation
Fix DateTime64: throw instead of wrapping when a scale 8/9 value does not fit in Int64 - #712
Open
polyglotAI-bot wants to merge 1 commit into
Open
polyglotAI-bot wants to merge 1 commit into
polyglotAI-bot wants to merge 1 commit into
Conversation
… not fit in Int64 The binary write path scaled the 100 ns tick count up with an unchecked multiply. For DateTime64(9) (and DateTime64(8) after 4892-10-07) a value outside the Int64 range at that scale wrapped, and a different instant was stored with no error. WriteChecked now checks the bound before scaling up and throws ArgumentOutOfRangeException with the value, the column type and the range, like the Date/Date32/DateTime writers and the TCP codec. Fixes: #711 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
polyglotAI-bot
requested review from
alex-clickhouse and
mzitnik
as code owners
November 7, 2026 03:23
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
There are no active review issues for the DateTime64 overflow-validation change.
No open issues 🎉
What changed in this PR
Fixes #711 in the HTTP client by rejecting binary DateTime64(8/9) writes whose scaled timestamp exceeds Int64 limits, preventing silent timestamp corruption. Scales 0–7 remain unchanged.
Changes:
- Adds overflow validation before scaling, with errors reporting the original value, column type, and writable range.
- Adds server-backed tests for boundary values and out-of-range inputs, including timezone and offset conversions.
- Documents the limits and records the behavior change in a changelog fragment.
| File | Description |
|---|---|
docs/http.mdx |
Documents DateTime64 write limits and out-of-range exceptions. |
ClickHouse.Driver/Types/DateTime64Type.cs |
Checks Int64 bounds before scaling and formats range errors. |
ClickHouse.Driver.Tests/Types/DateTimeBinaryRangeTests.cs |
Adds boundary round-trip and rejection tests for scales 8–9. |
changelog.d/711-datetime64-overflow.fixes.md |
Records the overflow fix and new exception behavior. |
Review effort: Balanced
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #711.
The HTTP client writes a
DateTime64as an Int64 count of 10^-scale seconds since the epoch.DateTime64Type.ToClickHouseTicksgets that count from the .NET tick count (100 ns) withMathUtils.ShiftDecimalPlaces, which multiplies by 10^(scale-7) with no overflow check. Scales 8 and 9 are finer than a tick, so they reach the Int64 limit inside theDateTimerange:DateTime64(9)before 1677-09-21 or after 2262-04-11 UTC, andDateTime64(8)after 4892-10-07 UTC. For those values the multiply wrapped, and the server stored a different instant with no error (for example, 1500-06-01 was stored as 2084-12-18 at scale 9).DateTime64Type.WriteCheckednow checks the tick count againstlong.MaxValue / factorandlong.MinValue / factorbefore it scales up. Out-of-range values throwArgumentOutOfRangeException. TheDateTimeandDate32writers and the TCP codec (DateTime64ColumnCodec.CountFromDateTimeOffset) already do this. Scales 0–7 are not changed.Changes
ClickHouse.Driver/Types/DateTime64Type.cs: range check inWriteCheckedfor scale > 7. The exception reports the caller's original value (ActualValue), the column type and the range that can be written. The old comment ("No range check: any coerced instant is representable") was wrong and is removed. For scale > 7 the count is computed once (ticks * factor) after the check.ClickHouse.Driver.Tests/Types/DateTimeBinaryRangeTests.cs: DateTime64 cases next to the existing Date/Date32/DateTime range tests.docs/http.mdx: the write type map and the "Out-of-range values" note now includeDateTime64(8)andDateTime64(9).changelog.d/711-datetime64-overflow.fixes.md.Test
The tests insert through
InsertBinaryAsyncinto a real server:DateTime64(9)min and max, theDateTime64(8)max, the max as aDateTimeOffsetat +01:00, and the max as anUnspecifiedDateTimein anAsia/Tokyocolumn. The test asserts the stored count withreinterpretAsInt64(t). It does not usetoString(t), because 26.3 renders an instant before 1900 as 1900-01-01 (checked on 26.3.28.5).ArgumentOutOfRangeException. CoversDateTime(Utc, Local, Unspecified in a non-UTC column),DateTimeOffsetwith an offset,DateOnlyand NodaTimeInstant. The test checks the type name, the range in the message andActualValue.The expected counts and boundary dates come from the Int64 range (±9223372036854775800 at tick precision), not from client output. All past-limit cases fail on
main(no exception); all cases pass with the fix. The fullClickHouse.Driver.Testssuite passes on net10.0 against 26.9.DateTimeBinaryRangeTestsalso passes against 26.3. The HTTP client builds for net6.0, net8.0, net9.0 and net10.0.changelog.cs --checkpasses.Every binary write entry point (
Write(object), the typedWriteValueoverloads used by POCO inserts, and the Nullable/Array/Map/Tuple/Variant/JSON wrappers) goes throughWriteChecked, so the one check covers all of them. The HTTP parameter path sends an ISO string and is not affected.Checklist
Pre-PR validation gate
mainAGENTS.mdandClickHouse.Driver/AGENTS.mdRelated: ClickHouse/clickhouse-java#3292 (the report this issue was relayed from).
🤖 Generated with Claude Code
Note
Medium Risk
Touches core binary serialization for high-precision DateTime64 inserts; behavior changes from silent wrong timestamps to exceptions for out-of-range values at scales 8–9.
Overview
Fixes silent corruption when binary-inserting fine-scale
DateTime64values whose scaled epoch count would not fit inInt64. For scales 8 and 9, scaling ticks before write could wrap and persist the wrong instant; those values now throwArgumentOutOfRangeExceptionfromDateTime64Type.WriteChecked, with a message that names the column type and the storable range (scales ≤7 are unchanged).Adds end-to-end
InsertBinaryAsynctests forDateTime64(8/9)at the Int64 boundary (including timezone/offset/Instant/DateOnlycases) and just past it. Docs and a changelog entry document the accepted UTC ranges and the new validation on the binary write path.Reviewed by Cursor Bugbot for commit 232d110. Bugbot is set up for automated code reviews on this repo. Configure here.