Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Breaking change]: System.Formats.Cbor DateTime formatting change #37378

Closed
1 of 3 tasks
eiriktsarpalis opened this issue Oct 4, 2023 · 0 comments · Fixed by #37881
Closed
1 of 3 tasks

[Breaking change]: System.Formats.Cbor DateTime formatting change #37378

eiriktsarpalis opened this issue Oct 4, 2023 · 0 comments · Fixed by #37881
Assignees
Labels
breaking-change Indicates a .NET Core breaking change 🏁 Release: .NET 8 Work items for the .NET 8 release doc-idea Indicates issues that are suggestions for new topics [org][type][category] Pri1 High priority, do before Pri2 and Pri3 📌 seQUESTered Identifies that an issue has been imported into Quest.

Comments

@eiriktsarpalis
Copy link
Member

eiriktsarpalis commented Oct 4, 2023

Description

Since it was released in .NET 5, the System.Formats.Cbor NuGet package included built-in methods for serializing and deserializing DateTimeOffset values according to RFC 7049. Unfortunately, the implementations weren't using invariant culture when formatting/parsing DateTimeOffset values. This can result in inconsistent or even incorrect date encodings in machines with cultures using non-Gregorian calendars.

The PR in dotnet/runtime#92924 changes the DateTimeOffset behavior so that invariant culture is always being used. This can cause breaking changes for users relying on the previous behavior, including making it impossible to read date values that have been encoded with earlier versions of the System.Formats.Cbor NuGet package.

Version

.NET 8 GA

Previous behavior

The code

// Install a culture with a non-Gregorian calendar
var culture = new CultureInfo("he-IL");
culture.DateTimeFormat.Calendar = new HebrewCalendar();
Thread.CurrentThread.CurrentCulture = culture;

DateTimeOffset value = DateTimeOffset.Parse("2020-04-09T14:31:21.3535941+01:00", CultureInfo.InvariantCulture);

var writer = new CborWriter();
writer.WriteDateTimeOffset(value);
byte[] cborEncoding = writer.Encode();

Console.WriteLine(Convert.ToHexString(cborEncoding));

produces the CBOR encoding

C07828D7AAD7A922D7A42DD796272DD79822D7955431343A33313A32312E333533353934312B30313A3030

which corresponds to 0(תש\"פ-ז'-ט\"וT14:31:21.3535941+01:00) in CBOR diagnostic notation. This is an invalid date representation per RFC 7049.

New behavior

The same code will produce the CBOR encoding

C07821323032302D30342D30395431343A33313A32312E333533353934312B30313A3030

which corresponds to 0("2020-04-09T14:31:21.3535941+01:00") in CBOR diagnostic notation.

Type of breaking change

  • Binary incompatible: Existing binaries may encounter a breaking change in behavior, such as failure to load or execute, and if so, require recompilation.
  • Source incompatible: When recompiled using the new SDK or component or to target the new runtime, existing source code may require source changes to compile successfully.
  • Behavioral change: Existing binaries may behave differently at run time.

Reason for change

The previous behavior produced invalid date encodings per RFC 7049.

Recommended action

You might have to be able to read CBOR date encodings that were persisted using earlier versions of System.Formats.Cbor. This can be achieved either by

  1. Not upgrading to the latest version of the System.Formats.Cbor NuGet package or
  2. Change your code to use the extension method provided below.
public static class CborReaderExtensions
{
    private const string Rfc3339FormatString = "yyyy-MM-ddTHH:mm:ss.FFFFFFFK";

    public static DateTimeOffset ReadDateTimeOffsetReplacement(this CborReader reader, CultureInfo? cultureInfo = null)
    {
        CborTag tag = reader.PeekTag();
        if (tag != CborTag.DateTimeString)
        {
            throw new InvalidOperationException($"Expected CborTag {(int)CborTag.DateTimeString}");
        }

        reader.ReadTag();
        string dateString = reader.ReadTextString();
        return DateTimeOffset.ParseExact(dateString, Rfc3339FormatString, cultureInfo, DateTimeStyles.RoundtripKind);
    }
}

Which can be used to roundtrip the previous example as follows:

var reader = new CborReader(cborEncoding);
DateTimeOffset date = reader.ReadDateTimeOffsetReplacement(culture);
Console.WriteLine(date.ToString(CultureInfo.InvariantCulture));

Feature area

Other (please put exact area in description textbox)

Affected APIs

  • System.Formats.Cbor.CborWriter.WriteDateTimeOffset
  • System.Formats.Cbor.CborRead.ReadDateTimeOffset

Associated WorkItem - 167413

@eiriktsarpalis eiriktsarpalis added doc-idea Indicates issues that are suggestions for new topics [org][type][category] breaking-change Indicates a .NET Core breaking change Pri1 High priority, do before Pri2 and Pri3 labels Oct 4, 2023
@dotnet-bot dotnet-bot added the ⌚ Not Triaged Not triaged label Oct 4, 2023
@gewarren gewarren added 🗺️ reQUEST Triggers an issue to be imported into Quest. and removed ⌚ Not Triaged Not triaged labels Oct 4, 2023
@github-actions github-actions bot added 📌 seQUESTered Identifies that an issue has been imported into Quest. and removed 🗺️ reQUEST Triggers an issue to be imported into Quest. labels Oct 5, 2023
@gewarren gewarren added the 🏁 Release: .NET 8 Work items for the .NET 8 release label Nov 2, 2023
@ghost ghost added the in-pr This issue will be closed (fixed) by an active pull request. label Nov 2, 2023
@ghost ghost removed the in-pr This issue will be closed (fixed) by an active pull request. label Nov 3, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking-change Indicates a .NET Core breaking change 🏁 Release: .NET 8 Work items for the .NET 8 release doc-idea Indicates issues that are suggestions for new topics [org][type][category] Pri1 High priority, do before Pri2 and Pri3 📌 seQUESTered Identifies that an issue has been imported into Quest.
Projects
No open projects
Status: Done
Status: ✅ Done
Development

Successfully merging a pull request may close this issue.

3 participants