diff --git a/apiCount.include.md b/apiCount.include.md index 7ae47133..4002904b 100644 --- a/apiCount.include.md +++ b/apiCount.include.md @@ -1,4 +1,4 @@ -**API count: 1147** +**API count: 1150** ### Per Target Framework @@ -19,10 +19,10 @@ | `netcoreapp3.0` | 859 | | `netcoreapp3.1` | 858 | | `net5.0` | 736 | -| `net6.0` | 629 | -| `net7.0` | 488 | -| `net8.0` | 367 | -| `net9.0` | 238 | +| `net6.0` | 632 | +| `net7.0` | 491 | +| `net8.0` | 370 | +| `net9.0` | 241 | | `net10.0` | 182 | | `net11.0` | 58 | | `uap10.0` | 1044 | diff --git a/api_list.include.md b/api_list.include.md index 337882c9..de881dae 100644 --- a/api_list.include.md +++ b/api_list.include.md @@ -727,6 +727,16 @@ * `ReadOnlySet AsReadOnly()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.asreadonly?view=net-11.0#system-collections-generic-collectionextensions-asreadonly-1(system-collections-generic-iset((-0)))) +#### ISOWeek + + * `int GetWeekOfYear(DateOnly)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.isoweek.getweekofyear?view=net-11.0#system-globalization-isoweek-getweekofyear(system-dateonly)) + * Note: Only available on net6.0 and later, since DateOnly does not exist below that. + * `int GetYear(DateOnly)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.isoweek.getyear?view=net-11.0#system-globalization-isoweek-getyear(system-dateonly)) + * Note: Only available on net6.0 and later, since DateOnly does not exist below that. + * `DateOnly ToDateOnly(int, int, DayOfWeek)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.isoweek.todateonly?view=net-11.0) + * Note: Only available on net6.0 and later, since DateOnly does not exist below that. + + #### KeyValuePair * `void Deconstruct(TKey, TValue)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.keyvaluepair-2.deconstruct?view=net-11.0) diff --git a/assemblySize.include.md b/assemblySize.include.md index d3e4f3ff..160c9aff 100644 --- a/assemblySize.include.md +++ b/assemblySize.include.md @@ -17,10 +17,10 @@ | netcoreapp3.0 | 9.5KB | 326.5KB | +317.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | | netcoreapp3.1 | 9.5KB | 324.5KB | +315.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | | net5.0 | 9.5KB | 290.0KB | +280.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net6.0 | 10.0KB | 230.0KB | +220.0KB | +10.0KB | +7.0KB | +1.0KB | +3.5KB | -| net7.0 | 10.0KB | 199.0KB | +189.0KB | +9.0KB | +5.5KB | +512bytes | +3.0KB | -| net8.0 | 9.5KB | 168.5KB | +159.0KB | +8.5KB | +512bytes | +1.0KB | +3.5KB | -| net9.0 | 9.5KB | 116.0KB | +106.5KB | +8.0KB | | +512bytes | +3.0KB | +| net6.0 | 10.0KB | 231.0KB | +221.0KB | +9.5KB | +6.5KB | +512bytes | +3.5KB | +| net7.0 | 10.0KB | 199.5KB | +189.5KB | +9.5KB | +5.5KB | +512bytes | +3.5KB | +| net8.0 | 9.5KB | 169.5KB | +160.0KB | +8.5KB | | +512bytes | +3.5KB | +| net9.0 | 9.5KB | 116.5KB | +107.0KB | +8.5KB | | +512bytes | +3.5KB | | net10.0 | 10.0KB | 93.5KB | +83.5KB | +8.5KB | | +512bytes | +3.0KB | | net11.0 | 10.0KB | 21.0KB | +11.0KB | +9.0KB | | +512bytes | +3.5KB | @@ -44,9 +44,9 @@ | netcoreapp3.0 | 9.5KB | 470.3KB | +460.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | | netcoreapp3.1 | 9.5KB | 468.3KB | +458.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | | net5.0 | 9.5KB | 416.3KB | +406.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net6.0 | 10.0KB | 335.8KB | +325.8KB | +17.7KB | +8.7KB | +1.6KB | +4.2KB | -| net7.0 | 10.0KB | 287.9KB | +277.9KB | +16.6KB | +6.9KB | +1.1KB | +3.7KB | -| net8.0 | 9.5KB | 243.6KB | +234.1KB | +16.0KB | +811bytes | +1.6KB | +4.2KB | -| net9.0 | 9.5KB | 167.6KB | +158.1KB | +15.5KB | | +1.1KB | +3.7KB | +| net6.0 | 10.0KB | 337.2KB | +327.2KB | +17.2KB | +8.2KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 288.8KB | +278.8KB | +17.1KB | +6.9KB | +1.1KB | +4.2KB | +| net8.0 | 9.5KB | 245.0KB | +235.5KB | +16.0KB | +299bytes | +1.1KB | +4.2KB | +| net9.0 | 9.5KB | 168.5KB | +159.0KB | +16.0KB | | +1.1KB | +4.2KB | | net10.0 | 10.0KB | 136.1KB | +126.1KB | +16.0KB | | +1.1KB | +3.7KB | | net11.0 | 10.0KB | 30.9KB | +20.9KB | +16.5KB | | +1.1KB | +4.2KB | diff --git a/readme.md b/readme.md index 33d0edf0..b6151719 100644 --- a/readme.md +++ b/readme.md @@ -13,7 +13,7 @@ The package targets `netstandard2.0` and is designed to support the following ru * `uap10` -**API count: 1147** +**API count: 1150** ### Per Target Framework @@ -34,10 +34,10 @@ The package targets `netstandard2.0` and is designed to support the following ru | `netcoreapp3.0` | 859 | | `netcoreapp3.1` | 858 | | `net5.0` | 736 | -| `net6.0` | 629 | -| `net7.0` | 488 | -| `net8.0` | 367 | -| `net9.0` | 238 | +| `net6.0` | 632 | +| `net7.0` | 491 | +| `net8.0` | 370 | +| `net9.0` | 241 | | `net10.0` | 182 | | `net11.0` | 58 | | `uap10.0` | 1044 | @@ -111,10 +111,10 @@ This project uses features from the newest stable SDK and C# language. As such c | netcoreapp3.0 | 9.5KB | 326.5KB | +317.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | | netcoreapp3.1 | 9.5KB | 324.5KB | +315.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | | net5.0 | 9.5KB | 290.0KB | +280.5KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net6.0 | 10.0KB | 230.0KB | +220.0KB | +10.0KB | +7.0KB | +1.0KB | +3.5KB | -| net7.0 | 10.0KB | 199.0KB | +189.0KB | +9.0KB | +5.5KB | +512bytes | +3.0KB | -| net8.0 | 9.5KB | 168.5KB | +159.0KB | +8.5KB | +512bytes | +1.0KB | +3.5KB | -| net9.0 | 9.5KB | 116.0KB | +106.5KB | +8.0KB | | +512bytes | +3.0KB | +| net6.0 | 10.0KB | 231.0KB | +221.0KB | +9.5KB | +6.5KB | +512bytes | +3.5KB | +| net7.0 | 10.0KB | 199.5KB | +189.5KB | +9.5KB | +5.5KB | +512bytes | +3.5KB | +| net8.0 | 9.5KB | 169.5KB | +160.0KB | +8.5KB | | +512bytes | +3.5KB | +| net9.0 | 9.5KB | 116.5KB | +107.0KB | +8.5KB | | +512bytes | +3.5KB | | net10.0 | 10.0KB | 93.5KB | +83.5KB | +8.5KB | | +512bytes | +3.0KB | | net11.0 | 10.0KB | 21.0KB | +11.0KB | +9.0KB | | +512bytes | +3.5KB | @@ -138,10 +138,10 @@ This project uses features from the newest stable SDK and C# language. As such c | netcoreapp3.0 | 9.5KB | 470.3KB | +460.8KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | | netcoreapp3.1 | 9.5KB | 468.3KB | +458.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | | net5.0 | 9.5KB | 416.3KB | +406.8KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net6.0 | 10.0KB | 335.8KB | +325.8KB | +17.7KB | +8.7KB | +1.6KB | +4.2KB | -| net7.0 | 10.0KB | 287.9KB | +277.9KB | +16.6KB | +6.9KB | +1.1KB | +3.7KB | -| net8.0 | 9.5KB | 243.6KB | +234.1KB | +16.0KB | +811bytes | +1.6KB | +4.2KB | -| net9.0 | 9.5KB | 167.6KB | +158.1KB | +15.5KB | | +1.1KB | +3.7KB | +| net6.0 | 10.0KB | 337.2KB | +327.2KB | +17.2KB | +8.2KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 288.8KB | +278.8KB | +17.1KB | +6.9KB | +1.1KB | +4.2KB | +| net8.0 | 9.5KB | 245.0KB | +235.5KB | +16.0KB | +299bytes | +1.1KB | +4.2KB | +| net9.0 | 9.5KB | 168.5KB | +159.0KB | +16.0KB | | +1.1KB | +4.2KB | | net10.0 | 10.0KB | 136.1KB | +126.1KB | +16.0KB | | +1.1KB | +3.7KB | | net11.0 | 10.0KB | 30.9KB | +20.9KB | +16.5KB | | +1.1KB | +4.2KB | @@ -1342,6 +1342,16 @@ The class `Polyfill` includes the following extension methods: * `ReadOnlySet AsReadOnly()` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.collectionextensions.asreadonly?view=net-11.0#system-collections-generic-collectionextensions-asreadonly-1(system-collections-generic-iset((-0)))) +#### ISOWeek + + * `int GetWeekOfYear(DateOnly)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.isoweek.getweekofyear?view=net-11.0#system-globalization-isoweek-getweekofyear(system-dateonly)) + * Note: Only available on net6.0 and later, since DateOnly does not exist below that. + * `int GetYear(DateOnly)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.isoweek.getyear?view=net-11.0#system-globalization-isoweek-getyear(system-dateonly)) + * Note: Only available on net6.0 and later, since DateOnly does not exist below that. + * `DateOnly ToDateOnly(int, int, DayOfWeek)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.isoweek.todateonly?view=net-11.0) + * Note: Only available on net6.0 and later, since DateOnly does not exist below that. + + #### KeyValuePair * `void Deconstruct(TKey, TValue)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.keyvaluepair-2.deconstruct?view=net-11.0) @@ -2549,7 +2559,7 @@ void ObjectDisposedExceptionExample(bool isDisposed) ObjectDisposedException.ThrowIf(isDisposed, typeof(Consume)); } ``` -snippet source | anchor +snippet source | anchor @@ -2568,7 +2578,7 @@ void EnsureExample(Order order, Customer customer, string customerId, string ema this.quantity = Ensure.NotNegativeOrZero(quantity); } ``` -snippet source | anchor +snippet source | anchor diff --git a/src/Consume/Consume.cs b/src/Consume/Consume.cs index e40ec471..a8349f12 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -356,6 +356,16 @@ void BitConverter_Span_Methods() } #endif +#if NET6_0_OR_GREATER + void ISOWeek_DateOnly_Methods() + { + var date = new DateOnly(2024, 1, 1); + var week = ISOWeek.GetWeekOfYear(date); + var year = ISOWeek.GetYear(date); + date = ISOWeek.ToDateOnly(year, week, DayOfWeek.Monday); + } +#endif + #if NET5_0_OR_GREATER void BitConverter_Half_Methods() { diff --git a/src/Polyfill/ISOWeekPolyfill.cs b/src/Polyfill/ISOWeekPolyfill.cs new file mode 100644 index 00000000..ae328cbe --- /dev/null +++ b/src/Polyfill/ISOWeekPolyfill.cs @@ -0,0 +1,38 @@ +#if NET6_0_OR_GREATER && !NET10_0_OR_GREATER + +namespace Polyfills; + +using System; +using System.Globalization; + +static partial class Polyfill +{ + extension(ISOWeek) + { + /// + /// Calculates the ISO week number of a given date. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.globalization.isoweek.getweekofyear?view=net-11.0#system-globalization-isoweek-getweekofyear(system-dateonly) + //Note: Only available on net6.0 and later, since DateOnly does not exist below that. + public static int GetWeekOfYear(DateOnly date) => + ISOWeek.GetWeekOfYear(date.ToDateTime(TimeOnly.MinValue)); + + /// + /// Calculates the ISO week-numbering year (also called ISO year informally) mapped to the input date. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.globalization.isoweek.getyear?view=net-11.0#system-globalization-isoweek-getyear(system-dateonly) + //Note: Only available on net6.0 and later, since DateOnly does not exist below that. + public static int GetYear(DateOnly date) => + ISOWeek.GetYear(date.ToDateTime(TimeOnly.MinValue)); + + /// + /// Maps the ISO week date represented by a specified ISO year, week number, and day of week to the equivalent . + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.globalization.isoweek.todateonly?view=net-11.0 + //Note: Only available on net6.0 and later, since DateOnly does not exist below that. + public static DateOnly ToDateOnly(int year, int week, DayOfWeek dayOfWeek) => + DateOnly.FromDateTime(ISOWeek.ToDateTime(year, week, dayOfWeek)); + } +} + +#endif diff --git a/src/Split/net6.0/ISOWeekPolyfill.cs b/src/Split/net6.0/ISOWeekPolyfill.cs new file mode 100644 index 00000000..fd0e9a42 --- /dev/null +++ b/src/Split/net6.0/ISOWeekPolyfill.cs @@ -0,0 +1,26 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +using System.Globalization; +static partial class Polyfill +{ + extension(ISOWeek) + { + /// + /// Calculates the ISO week number of a given date. + /// + public static int GetWeekOfYear(DateOnly date) => + ISOWeek.GetWeekOfYear(date.ToDateTime(TimeOnly.MinValue)); + /// + /// Calculates the ISO week-numbering year (also called ISO year informally) mapped to the input date. + /// + public static int GetYear(DateOnly date) => + ISOWeek.GetYear(date.ToDateTime(TimeOnly.MinValue)); + /// + /// Maps the ISO week date represented by a specified ISO year, week number, and day of week to the equivalent . + /// + public static DateOnly ToDateOnly(int year, int week, DayOfWeek dayOfWeek) => + DateOnly.FromDateTime(ISOWeek.ToDateTime(year, week, dayOfWeek)); + } +} diff --git a/src/Split/net7.0/ISOWeekPolyfill.cs b/src/Split/net7.0/ISOWeekPolyfill.cs new file mode 100644 index 00000000..fd0e9a42 --- /dev/null +++ b/src/Split/net7.0/ISOWeekPolyfill.cs @@ -0,0 +1,26 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +using System.Globalization; +static partial class Polyfill +{ + extension(ISOWeek) + { + /// + /// Calculates the ISO week number of a given date. + /// + public static int GetWeekOfYear(DateOnly date) => + ISOWeek.GetWeekOfYear(date.ToDateTime(TimeOnly.MinValue)); + /// + /// Calculates the ISO week-numbering year (also called ISO year informally) mapped to the input date. + /// + public static int GetYear(DateOnly date) => + ISOWeek.GetYear(date.ToDateTime(TimeOnly.MinValue)); + /// + /// Maps the ISO week date represented by a specified ISO year, week number, and day of week to the equivalent . + /// + public static DateOnly ToDateOnly(int year, int week, DayOfWeek dayOfWeek) => + DateOnly.FromDateTime(ISOWeek.ToDateTime(year, week, dayOfWeek)); + } +} diff --git a/src/Split/net8.0/ISOWeekPolyfill.cs b/src/Split/net8.0/ISOWeekPolyfill.cs new file mode 100644 index 00000000..fd0e9a42 --- /dev/null +++ b/src/Split/net8.0/ISOWeekPolyfill.cs @@ -0,0 +1,26 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +using System.Globalization; +static partial class Polyfill +{ + extension(ISOWeek) + { + /// + /// Calculates the ISO week number of a given date. + /// + public static int GetWeekOfYear(DateOnly date) => + ISOWeek.GetWeekOfYear(date.ToDateTime(TimeOnly.MinValue)); + /// + /// Calculates the ISO week-numbering year (also called ISO year informally) mapped to the input date. + /// + public static int GetYear(DateOnly date) => + ISOWeek.GetYear(date.ToDateTime(TimeOnly.MinValue)); + /// + /// Maps the ISO week date represented by a specified ISO year, week number, and day of week to the equivalent . + /// + public static DateOnly ToDateOnly(int year, int week, DayOfWeek dayOfWeek) => + DateOnly.FromDateTime(ISOWeek.ToDateTime(year, week, dayOfWeek)); + } +} diff --git a/src/Split/net9.0/ISOWeekPolyfill.cs b/src/Split/net9.0/ISOWeekPolyfill.cs new file mode 100644 index 00000000..fd0e9a42 --- /dev/null +++ b/src/Split/net9.0/ISOWeekPolyfill.cs @@ -0,0 +1,26 @@ +// +#pragma warning disable +namespace Polyfills; +using System; +using System.Globalization; +static partial class Polyfill +{ + extension(ISOWeek) + { + /// + /// Calculates the ISO week number of a given date. + /// + public static int GetWeekOfYear(DateOnly date) => + ISOWeek.GetWeekOfYear(date.ToDateTime(TimeOnly.MinValue)); + /// + /// Calculates the ISO week-numbering year (also called ISO year informally) mapped to the input date. + /// + public static int GetYear(DateOnly date) => + ISOWeek.GetYear(date.ToDateTime(TimeOnly.MinValue)); + /// + /// Maps the ISO week date represented by a specified ISO year, week number, and day of week to the equivalent . + /// + public static DateOnly ToDateOnly(int year, int week, DayOfWeek dayOfWeek) => + DateOnly.FromDateTime(ISOWeek.ToDateTime(year, week, dayOfWeek)); + } +} diff --git a/src/Tests/ISOWeekPolyfillTests.cs b/src/Tests/ISOWeekPolyfillTests.cs new file mode 100644 index 00000000..8d786e47 --- /dev/null +++ b/src/Tests/ISOWeekPolyfillTests.cs @@ -0,0 +1,155 @@ +#if NET6_0_OR_GREATER + +using System.Globalization; + +// Runs on net6.0 and later, so net10.0 and above validate the BCL and net6.0 through net9.0 +// the polyfill. The DateTime overloads have existed since netcoreapp3.0 on every one of those, +// so they are the oracle rather than hand written expectations. +public class ISOWeekPolyfillTests +{ + [Test] + public async Task MatchesTheDateTimeOverloads() + { + var failures = 0; + var visited = 0; + var date = new DateTime(1, 1, 1); + var last = new DateTime(9999, 12, 31); + while (true) + { + visited++; + var only = DateOnly.FromDateTime(date); + if (ISOWeek.GetWeekOfYear(only) != ISOWeek.GetWeekOfYear(date) || + ISOWeek.GetYear(only) != ISOWeek.GetYear(date)) + { + failures++; + break; + } + + if (date == last) + { + break; + } + + date = date.AddDays(1); + } + + await Assert.That(failures).IsEqualTo(0); + // every representable date, so a future edit cannot quietly shrink the sweep + await Assert.That(visited).IsEqualTo(3652059); + } + + [Test] + public async Task ToDateOnly_MatchesToDateTime() + { + var failures = 0; + var visited = 0; + for (var year = 1; year <= 9999; year++) + { + var weeks = ISOWeek.GetWeeksInYear(year); + for (var week = 1; week <= weeks; week++) + { + foreach (var day in new[] + { + DayOfWeek.Monday, + DayOfWeek.Tuesday, + DayOfWeek.Wednesday, + DayOfWeek.Thursday, + DayOfWeek.Friday, + DayOfWeek.Saturday, + DayOfWeek.Sunday + }) + { + visited++; + if (!SameOutcome(year, week, day)) + { + failures++; + } + } + } + } + + await Assert.That(failures).IsEqualTo(0); + await Assert.That(visited).IsEqualTo(3652061); + } + + [Test] + public async Task KnownValues() + { + // 2020-01-01 falls in ISO week 1 of 2020, and 2021-01-01 in week 53 of 2020 + await Assert.That(ISOWeek.GetWeekOfYear(new DateOnly(2020, 1, 1))).IsEqualTo(1); + await Assert.That(ISOWeek.GetYear(new DateOnly(2020, 1, 1))).IsEqualTo(2020); + await Assert.That(ISOWeek.GetWeekOfYear(new DateOnly(2021, 1, 1))).IsEqualTo(53); + await Assert.That(ISOWeek.GetYear(new DateOnly(2021, 1, 1))).IsEqualTo(2020); + + await Assert.That(ISOWeek.ToDateOnly(2020, 1, DayOfWeek.Monday)).IsEqualTo(new DateOnly(2019, 12, 30)); + await Assert.That(ISOWeek.ToDateOnly(2020, 53, DayOfWeek.Sunday)).IsEqualTo(new DateOnly(2021, 1, 3)); + await Assert.That(ISOWeek.ToDateOnly(1, 1, DayOfWeek.Monday)).IsEqualTo(new DateOnly(1, 1, 1)); + } + + [Test] + public async Task ArgumentValidation() + { + await Assert.That(Throws(0, 1, DayOfWeek.Monday)).IsEqualTo("ArgumentOutOfRangeException:year"); + await Assert.That(Throws(10000, 1, DayOfWeek.Monday)).IsEqualTo("ArgumentOutOfRangeException:year"); + await Assert.That(Throws(2024, 0, DayOfWeek.Monday)).IsEqualTo("ArgumentOutOfRangeException:week"); + await Assert.That(Throws(2024, 54, DayOfWeek.Monday)).IsEqualTo("ArgumentOutOfRangeException:week"); + await Assert.That(Throws(2024, 1, (DayOfWeek) 9)).IsEqualTo("ArgumentOutOfRangeException:dayOfWeek"); + + // week 53 is accepted for a year that has one + await Assert.That(Throws(2020, 53, DayOfWeek.Monday)).IsEqualTo("none"); + + // running off the end of the representable range reports the DateTime arithmetic failure, + // which is what the BCL does too, since ToDateOnly is built on ToDateTime + await Assert.That(Throws(9999, 52, DayOfWeek.Sunday)).IsEqualTo("ArgumentOutOfRangeException:value"); + } + + // compares the values rather than formatted strings, since this runs over three and a half + // million combinations and formatting would dominate + static bool SameOutcome(int year, int week, DayOfWeek day) + { + DateOnly viaDateOnly = default; + string? dateOnlyError = null; + try + { + viaDateOnly = ISOWeek.ToDateOnly(year, week, day); + } + catch (ArgumentException exception) + { + dateOnlyError = exception.ParamName; + } + + DateOnly viaDateTime = default; + string? dateTimeError = null; + try + { + viaDateTime = DateOnly.FromDateTime(ISOWeek.ToDateTime(year, week, day)); + } + catch (ArgumentException exception) + { + dateTimeError = exception.ParamName; + } + + if (dateOnlyError != null || + dateTimeError != null) + { + return dateOnlyError == dateTimeError; + } + + return viaDateOnly == viaDateTime; + } + + static string Throws(int year, int week, DayOfWeek day) + { + try + { + ISOWeek.ToDateOnly(year, week, day); + return "none"; + } + catch (ArgumentException exception) + { + return $"{exception.GetType().Name}:{exception.ParamName}"; + } + } +} + +#endif