Skip to content

feat: add skipNormalization flag to duration struct and implement NewISODurationFromString function - #222

Open
ShocOne wants to merge 1 commit into
microsoft:mainfrom
ShocOne:main
Open

feat: add skipNormalization flag to duration struct and implement NewISODurationFromString function#222
ShocOne wants to merge 1 commit into
microsoft:mainfrom
ShocOne:main

Conversation

@ShocOne

@ShocOne ShocOne commented Mar 8, 2026

Copy link
Copy Markdown
Contributor
  • Introduced a skipNormalization boolean in the duration struct to control normalization behavior.

  • Added NewISODurationFromString function to parse ISO 8601 duration strings while preserving their original format.

  • Updated tests to verify the correct parsing and preservation of various ISO duration formats.

    Fixes Microsoft Graph SDK ISO 8601 Duration Normalization #213

    The ISODuration.String() method unconditionally calls normalize() during serialization, which
    converts day-based durations into week equivalents (e.g. P90D → P12W6D). This breaks APIs that
    require exact ISO 8601 duration formats such as the Microsoft Graph /agreements endpoint.

    • Adds a skipNormalization flag to the internal duration struct
    • Gates the normalize() call in string() behind !skipNormalization
    • Adds NewISODurationFromString(s string) (*ISODuration, error) — parses the string and sets the
      flag, so the original format is preserved on serialization
    • All existing behaviour via ParseISODuration and NewDuration is unchanged

    Examples

    // Before: normalization was unconditional
    d := serialization.NewDuration(0, 0, 90, 0, 0, 0, 0)
    d.String() // "P12W6D" — breaks Graph /agreements API

    // After: use NewISODurationFromString to preserve format
    d, err := serialization.NewISODurationFromString("P90D")
    d.String() // "P90D" — correct

    Test plan

    • P90D, P365D, P180D, P30D round-trip unchanged through String()
    • Parsed field accessors (GetDays() etc.) return correct values
    • Week and time formats are preserved
    • Invalid input returns an error
    • Existing ParseISODuration and NewDuration tests all pass
    • Full test suite passes (go test ./...)

…ISODurationFromString function

- Introduced a skipNormalization boolean in the duration struct to control normalization behavior.
- Added NewISODurationFromString function to parse ISO 8601 duration strings while preserving their original format.
- Updated tests to verify the correct parsing and preservation of various ISO duration formats.
@ShocOne
ShocOne requested a review from a team as a code owner March 8, 2026 15:41

@baywet Vincent Biret (baywet) left a comment

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.

Thanks for the contribution!

I think the issue here also outlines a normalization issue. So I created #223 and #224 to help sort this out.

Interestingly enough, the dotnet implementation does not support weeks at all, and nobody has complained about this to date. But anyway, it does not normalize days to years for example.

This is because the underlying API used is in fact implementing the W3C standard, which is a subset of the ISO 8601 docs.

So overall, I think it'd be fair for a parse -> serialize scenario to be symmetric and not lead to any normalization. Only the components based instances should lead to a normalization as a way to ensure we don't end up serializing invalid values.

Ideally, instead of normalizing, we should probably do validation in the constructors to give feedback as early as possible. But the error return pattern does not go well with constructors not typically returning errors.


// NewISODurationFromString parses an ISO 8601 duration string and preserves the original
// format on serialization, bypassing normalization. Use this when an API requires an exact
// duration format such as P90D rather than its normalized equivalent P12W6D.

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.

Suggested change
// duration format such as P90D rather than its normalized equivalent P12W6D.
// duration format such as P7D rather than its normalized equivalent P1W.

if err != nil {
return nil, err
}
d.skipNormalization = true

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.

arguably, should we simply flip the flag in the parse method instead of introducing a new one?

@github-project-automation github-project-automation Bot moved this to In Progress 🚧 in Kiota Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In Progress 🚧

Development

Successfully merging this pull request may close these issues.

Microsoft Graph SDK ISO 8601 Duration Normalization

2 participants