Skip to content

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

Closed

Description

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Labels

🏁 Release: .NET 8Work items for the .NET 8 release📌 seQUESTeredIdentifies that an issue has been imported into Quest.Pri1High priority, do before Pri2 and Pri3breaking-changeIndicates a .NET Core breaking changedoc-ideaIndicates issues that are suggestions for new topics [org][type][category]

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions