FWIW, I believe there are only two ways to build this library correctly: the way you described here (where intermediate date values are allowed to be invalid) and simply making "one month after 2021-01-31" throw an exception.
I agree those are the technically correct way to do it.
But if the reason people are wanting to use the library, is they want something to handle the complexity for them, coercing the data is good for simplication.
There is actually another option, that provides idempotent/associative consistency and implicit coercion to valid values.
This option, which discards some use cases (days beyond 28, when manipulating months), you coerce all values 29..31 to 28. This isn't even as technically correct, as the original option, but it removes the inconsistency and holds to the simplification contract to users.
But if the reason people are wanting to use the library, is they want something to handle the complexity for them, coercing the data is good for simplication.
There is actually another option, that provides idempotent/associative consistency and implicit coercion to valid values.
This option, which discards some use cases (days beyond 28, when manipulating months), you coerce all values 29..31 to 28. This isn't even as technically correct, as the original option, but it removes the inconsistency and holds to the simplification contract to users.