-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
fix https://github.com/nim-lang/RFCs/issues/211: var a: DateTime
compiles and is usable
#14002
Conversation
It would have been preferable if you had discussed this in the RFC instead of opening a PR, now the discussion will be split up in multiple places. This might be the easiest way to to make
Even with this PR
As I commented in the RFC, it's not expensive to check if a DateTime is valid as long as the fields are made private. Having a cheap and correct
This is what I proposed in the RFC (except I would make every field private) and I still believe it's the best option.
I don't doubt that making the fields private would break some code, but I also believe it will prevent many bugs in the future. It will also make it significantly easier to further improve the |
As a compromise I suggest both:
|
discussion is more concrete with a PR that users can see and try, especially wrt estimating amounts of breaking changes by looking at CI; a lot of details only become apparent with an implementation. You can open a competing PR if you think you have a better proposal, and the 2 implementations can be compared.
that's a separate issue and type system can't help (think: leap year and april 31th); there are many ways besides
that's a separate issue and can be solved by exposing existing times.assertValidDate and making it more correct
it's currently legal in Nim and doesn't give any warnings; if it becomes illegal or deprecated, this can be changed
no need to break any code, instead we'd create a new improved notethe design for the new |
Sounds good to me
Fair enough, but it often seems that opening a PR when there's no consensus derails the discussion to be about that particular implementation instead.
I'm aware that there are many ways a DateTime can be invalid, but if the times module never returns such DateTimes and the user cannot create them with field assignments or obj ctor it wouldn't matter. The only invalid DateTime that could be constructed outside the times module would be
It cannot be fixed inside |
901a20b
to
72c867d
Compare
…) [backport:1.2] * fix nim-lang/RFCs#211: `var a: DateTime` works * assertValidDate checks for sentinel month (cherry picked from commit e3919b6)
var a: DateTime
now compiles and is usable, eg for delayed initialization (think serialization etc)This is the simplest approach IMO until we finally implement #12378 to avoid having a broken type system, see my note nim-lang/RFCs#126 (comment)), taking into account backward compatibility and other factors.
var a: DateTime
now simply works, and produces a DateTime with a "sentinel" value 0 for monthday and month.assertValidDate
will check for it so thattoTime
will fail if uninitializednotes
I intentionally did not add a
proc isValid(a: DateTime): bool
because there are many ways a DateTime can be invalid, and it'd be expensive to compute if all user cares about is whethera
was initialized.I intentionally did not add
isInitialized
, because we should instead use eithera != default(DateTime)
ora.notDefault
using a new procnotDefault
in some other module (see [RFC] addisDefault
; more general than isNil, isEmpty etc #13526 for details)when Default fields for objects #12378 is implemented, we'll simply change this back to
MonthdayRange* = range[1..31]
andMonthdayRange.default
would become 1 instead of 0alternatives considered
There is little benefit in preventing those sentinel value at CT because there are other ways to produce invalid DateTime objects anyways (eg: think leap years, or April 31), but I still did consider those:
All these (especially A1, and except, to some extent, A4) are breaking changes of the worst kind, where things sometimes produce same results, sometimes don't, depending on how it's used (eg
monthday <= 15
):A1 changing to
MonthdayRange* = range[0..30]
A2 introducing a
type MonthdayRange* = range[0.Monthday..30.Monthday]
+type Monthday = distinct int
+proc to1Based*(a: Monthday): int = a.ord + 1
still a breaking change, eg for code like
myDate.month.int
, or for serialization/deserialization routinesplus lots of work
A3 introducing a
type Monthday* = enum d1, d2, d3, ..., d30
+proc to1Based*(a: Monthday): int = a.ord + 1
ditto
A4 make
month
private and implement via getter setterbreaking change (eg object ctor would break, taking
monthday
by reference would break, serialization complications, and many other unrelated cases)