The direct "immutable" equivalent of the bug code (purely based on my experience with .NET Core immutable collections) would have the setX methods return a new immutable datetime with the field set as requested. So just "use an immutable type" wouldn't fix this bug.
"Change the code completely so you supply all 3 components at the same time" obviously fixes the bug, but that doesn't require an immutable type.
The .NET immutable collections seem to mostly avert problems like this. For example, ImmutableDictionary.Add<TKey, TValue>(key, value) throws an exception if key already exists with a different value (as determined by an explicit or implicit IEqualityComparer<TValue>).
.NET's (immutable) DateTime, on the other hand, has AddMonths(Int32), which sets the day to Min(original day number, last day of result month), which, while arguably reasonable, isn't obvious without reading the documentation.
My favorite counterintutive DateTime "mutator," however is AddMilliseconds(Double): prior to .NET 7, its floating-point argument is rounded to the nearest integer (!?!).
Comments
The direct "immutable" equivalent of the bug code (purely based on my experience with .NET Core immutable collections) would have the setX methods return a new immutable datetime with the field set as requested. So just "use an immutable type" wouldn't fix this bug.
"Change the code completely so you supply all 3 components at the same time" obviously fixes the bug, but that doesn't require an immutable type.
The .NET immutable collections seem to mostly avert problems like this. For example, ImmutableDictionary.Add<TKey, TValue>(key, value) throws an exception if key already exists with a different value (as determined by an explicit or implicit IEqualityComparer<TValue>).
.NET's (immutable) DateTime, on the other hand, has AddMonths(Int32), which sets the day to Min(original day number, last day of result month), which, while arguably reasonable, isn't obvious without reading the documentation.
My favorite counterintutive DateTime "mutator," however is AddMilliseconds(Double): prior to .NET 7, its floating-point argument is rounded to the nearest integer (!?!).
Yep, that's what I was getting at. Immutability is not a "silver bullet"