SIGN IN SIGN UP

[http-client-csharp] Fix duration integer encoding for non-Int32 wire types (#10832)

A TypeSpec `duration` encoded as integer milliseconds/seconds with a
wire type other than `int32` (e.g. `integer`, `int64`, `safeint`,
`uint*`) was serialized as a `double` via `TimeSpan.TotalMilliseconds` /
`TotalSeconds`, producing fractional JSON output that violates the
integer contract. Additionally, for wire types larger than `int32` (e.g.
`int64`, `uint32`, `uint64`, `safeint`, unbounded `integer`), using
`Int32`-based ser/deser would overflow/truncate large values.

```typespec
@encode(DurationKnownEncoding.milliseconds, integer)
audio_end_ms: duration;
```

Previously generated:

```csharp
writer.WriteNumberValue(AudioEndMs.TotalMilliseconds); // double, may emit 123.45
```

Now generates (for `int32`-sized wire types):

```csharp
writer.WriteNumberValue(Convert.ToInt32(Math.Round(AudioEndMs.TotalMilliseconds)));
```

And for wire types larger than `int32` (`int64`, `uint32`, `uint64`,
`safeint`, unbounded `integer`):

```csharp
writer.WriteNumberValue(Convert.ToInt64(Math.Round(AudioEndMs.TotalMilliseconds)));
```

The `Math.Round` wrapper makes the rounding behavior explicit so
fractional values (e.g. `1500.7ms`) are rounded to the nearest integer
(`1501`) rather than relying on `Convert.ToInt32`/`ToInt64`'s implicit
rounding.

### Changes

- **`TypeFactory.GetSerializationFormat`**: The
`DurationKnownEncoding.Seconds` / `Milliseconds` switches only matched
`InputPrimitiveTypeKind.Int32` for the integer arm; every other integer
kind fell through the `_` default into `Duration_*_Double`. Extended
both arms to cover the full integer-kind set, splitting based on .NET
range:
- `Int8`, `Int16`, `Int32`, `UInt8`, `UInt16` → `Duration_Seconds` /
`Duration_Milliseconds` (uses `Int32`).
- `Int64`, `UInt32`, `UInt64`, `SafeInt`, unbounded `Integer` → new
`Duration_Seconds_Int64` / `Duration_Milliseconds_Int64` (uses `Int64`).
- `Float` / `Float32` still map to `_Float`, and `Float64` / others to
`_Double`.
- **New `SerializationFormat` values**: Added `Duration_Seconds_Int64`
and `Duration_Milliseconds_Int64` to the input enum and to the generated
`SerializationFormat` enum (`SerializationFormatDefinition`).
- **`MrwSerializationTypeDefinition`**: Handles the new Int64 formats by
emitting `JsonElement.GetInt64()` for deserialization and
`Convert.ToInt64(Math.Round(...))` for JSON serialization. The Int32
formats now also wrap in `Math.Round` for explicit rounding.
- **`TypeFormattersDefinition`**: Handles the new Int64 formats in the
URI/query-string `ConvertToString` path with
`Convert.ToInt64(Math.Round(...)).ToString(...)`. The Int32 formats now
also wrap in `Math.Round`.
- **`ConvertSnippets`**: Added `InvokeToInt64` helper.
- **`MathSnippets`** (new): Added `InvokeRound` helper that emits
`Math.Round(arg)`.
- **Tests**:
- `TypeFactoryTests.DurationIntegerWireTypeSerializationFormat` and
`DurationFloatWireTypeSerializationFormat` cover every integer and float
wire-type kind for both encodings, asserting Int32 vs Int64 routing.
- `MrwSerializationTypeDefinitionTests`
`TestTimeSpanDeserializeExpression` and `TestTimeSpanSerializeStatement`
extended with the new Int64 format cases and the `Math.Round` wrapping.
- `JsonModelCoreTests.DurationMillisecondsIntegerWireTypeWritesAsInt` /
`DurationMillisecondsFloatWireTypeWritesAsDouble` and
`DeserializationTests.TestDeserializationOfDurationMillisecondsIntegerWireType`
build a model with a `duration` property and compare the full generated
output against per-case `TestData` files (Int32 emits
`GetInt32`/`Convert.ToInt32(Math.Round(...))`; Int64 and unbounded
`integer` emit `GetInt64`/`Convert.ToInt64(Math.Round(...))`).

### Validation

- Full C# generator unit-test suites (Generator, Generator.Input,
Generator.ClientModel, TestProjects.Local): all passing.
- `eng/scripts/Generate.ps1` ran to completion; the only regenerated
changes are the expected updates to
`TestProjects/Local/Sample-TypeSpec/src/Generated/Internal/SerializationFormat.cs`
and `TypeFormatters.cs` reflecting the new Int64 enum members, switch
arms, and `Math.Round` wrapping.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
C
Copilot committed
02cd17baac8207a8ca8505bde4c485571d562ce5
Parent: d2691e5
Committed by GitHub <noreply@github.com> on 5/29/2026, 6:46:47 PM