Skip to content

Enable CA1305 (specify IFormatProvider) and fix culture-sensitive parameter/date formatting #200

Description

@daviburg

Summary

Enable CA1305 ("Specify IFormatProvider") as a build-enforced analyzer rule, and fix the culture-sensitive formatting it surfaces. CA1305 flags formatting/parsing calls that omit an explicit culture and therefore fall back to CultureInfo.CurrentCulture, so the produced string varies with the machine/thread locale.

This rule is disabled by default in the SDK's current analysis mode (AnalysisMode=Default), so it never fires today even though EnableNETAnalyzers=true and TreatWarningsAsErrors=true are set in eng/build/Engineering.props.

Why this matters (real correctness risk, not style)

The SDK turns typed parameters into wire text — JSON bodies and URL paths/query strings — which must be culture-invariant to match connector backends. Under a non-invariant CurrentCulture the current code can emit wrong bytes:

  • Floating-point params in de-DE serialize with a comma decimal separator (3,14) → malformed URL/query.
  • Integers under digit-substituting locales (ar-SA, fa-IR) render non-ASCII digits → malformed query.
  • DateTime values render culture-formatted / with non-ASCII digits → invalid ISO-8601 on the wire.

This is silent on invariant-culture CI (and Azure Functions defaults often use invariant globalization), and only fails in specific customer locales — the worst failure mode.

Evidence (from a local AnalysisMode=All build)

CA1305 fires ~1,400 times. Two representative surfaces:

1. Hand-written — Iso8601DateTimeConverter (src/Azure.Connectors.Sdk/Serialization/JsonConverters.cs):

// Write (line ~35): renders digits with CurrentCulture
writer.WriteStringValue(value.ToUniversalTime().ToString(DateTimeFormat));
// Read: also CurrentCulture
return DateTime.Parse(value, null, System.Globalization.DateTimeStyles.RoundtripKind);

Both should pass CultureInfo.InvariantCulture.

2. Generated clients — parameter → URL construction (bulk of the hits), e.g. AzureADExtensions / AzureAutomationExtensions:

queryParams.Add($"$top={Uri.EscapeDataString(top.Value.ToString())}");           // int.ToString(), no culture
var path = $".../resourcegroups/{Uri.EscapeDataString(subscription.ToString())}"; // no culture

Guid/string params are harmless, but numeric and DateTime params are not.

Scope / considerations

  • Generated code is in scope. The generated clients are produced by our own CodefulSdkGenerator (BPM repo) and are core to the value proposition; culture-broken parameter formatting is a real product bug. The generated hits must be fixed in the generator's emit templates (make every param.ToString() in path/query building culture-invariant, or route through an invariant formatting helper) — one generator change fixes all ~1,400 and every future connector — not per-file suppressions.
  • Severity: start as warning (which TreatWarningsAsErrors escalates to error) via .editorconfig (dotnet_diagnostic.CA1305.severity = warning), rather than raising the whole AnalysisMode, to keep the change targeted.

Tasks

  • Fix hand-written Iso8601DateTimeConverter Write and Read to use CultureInfo.InvariantCulture (Connectors-NET-SDK repo).
  • Fix the CodefulSdkGenerator emit templates so generated path/query parameter formatting is culture-invariant (BPM repo), then regenerate clients.
  • Enable dotnet_diagnostic.CA1305.severity in .editorconfig once the surfaced violations are resolved.
  • Build stays green with the rule enforced.

Acceptance criteria

  • No CA1305 violations in hand-written or generated code.
  • Rule enforced at build (.editorconfig).
  • Optional: a regression test asserting Iso8601DateTimeConverter round-trips correctly under a non-invariant culture (e.g. ar-SA/de-DE).

Filed from analysis on PR #196; not part of that PR's scope. Companion to #199 (CA1062).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions