-
Notifications
You must be signed in to change notification settings - Fork 344
At/On overloads that return DateTimeOffset? #300
Comments
Thanks for the feedback. On discouraging One example is in domains such as television schedules - where a show airs (for example) at Besides the few business domains where that is a natural fit, also consider the much more general case of prompting a user for input from a form. You might ask for a date, or a time, or a date and time, but you'll rarely ask for an offset directly. Another common use case is when loading simple flat files, such as CSV. Rarely are offsets present in this sort of data. If you force a Indeed, you will see this behavior today in that the local time zone is assumed in the So - discouraging ... whew, that was long, ok on to the next part ... As you pointed out, we already have I think what what would make more sense is just a more discoverable way to go from |
In my experience with our (large-ish) codebase, allowing any use of DateTime is the slippery slope. I agree that there are some use cases of local date/times (though I don't totally buy the television schedule one), but use cases of instants in time are much more common. The problem is that DateTime attempts to cover multiple use cases (including dates!) without a clear distinction. The Kind is a very subtle thing that is often overlooked. The fact that dates are indistinguishable from date/times at midnight is a travesty! I definitely agree there are some dodgy behaviors in conversions, and there shouldn't be an implicit cast. Though it is pretty hard to remove that now! I also agree it is inconvenient to have to specify an offset whenever a DateTimeOffset is created, but IME it is the only way of writing code that robustly represents instants. BTW we have found it more convenient/robust to construct using a TimeZoneInfo - e.g. date.At(tod, timeZoneInfo) - as it usually conveys better what you mean and deals better with daylight savings. I would be happy with your proposed date.At(timeOfDay).WithOffset(timeSpan) as long as At() returned some time other than DateTime - i.e. a LocalDateTime or something like that. This makes it clear what it actually represents. I guess noda time has it right, but seems overly complex for many use cases, and I hold out hope that the BCL can be made better. Perhaps @jskeet can provide some useful input. |
Well, my personal view is that once you've thought what you actually want to do, Noda Time should end up being at most as complex as
If you're looking for simplicity of coding without having to think about what exactly you want, then date/time isn't an amenable domain :( |
I agree about the philosophies. In my view, if you want an API that leads you down the right path and tries to prevent you from doing the wrong thing, then please use Noda Time. It's quite solid. The trade-off is in two parts: 1) Some things will be more verbose, by design - you noted this about the complexity. 2) Some interoperability areas are difficult, or not possible - such as EF support. Hopefully something will be done about that eventually (see dotnet/efcore#242). WRT the work being done here for What we can do is to include some Roslyn Analyzers to highlight common usage mistakes. This was discussed extensively in dotnet/corefx#2116, dotnet/corefx#700 and dotnet/corefx#626. To put it more clearly, WRT to Looking into the future I can see some guidance somewhere on MSDN that says something to the effect of this:
|
BTW - I'm not completely against the idea of changing the philosophy to offer a comprehensive solution out of the box. It would indeed make The current proposal is more of a middle-ground compromise, rather than a best-of-breed solution. It would be interesting if anyone else out there had a different idea for a middle-ground approach, or an alternative viewpoint than Noda Time that was still comprehensive. |
Thanks. The issue with the "cover the simple cases" philosophy is that dates/times are passed around between our code and third party/OSS libraries. We can choose to use NodaTime in our codebase, but where we are using other libraries the common currency is the DateTime and so we end up converting back-and-forth. That leads people to just use DateTime to avoid the ceremony, and then the slippery slope begins. That said, I agree it's a big project and perhaps analyzers can spot common errors, though it's not clear to me how complete that can be without some type-system-level info that captures the DateTimeKind. WRT date.At(tod, timeZoneInfo) - the purpose is to avoid folks determining the offset themselves and potentially doing it wrong (e.g. hard-coding offsets and ignoring DST). DateTimeOffset can't do timezone-aware date arithmetic but it can represent that specific instant, the offset of which can be specified using a TZI. Perhaps it would lead the unsuspecting developer to thing that DateTimeOffset is timezone-aware. |
So then, you would like to see all three of these, correct? DateTime dt = date.At(timeOfDay);
DateTimeOffset dto = date.At(timeOfDay, offset);
DateTimeOffset dto = date.At(timeOfDay, tzi); And these too? DateTime dt = timeOfDay.On(date);
DateTimeOffset dto = timeOfDay.On(date, offset);
DateTimeOffset dto = timeOfDay.On(date, tzi); How would you feel about these? DateTimeOffset dto = dateTime.WithOffset(offset);
DateTimeOffset dto = dateTime.InTimeZone(tzi); The issue with either of those last two is that they'll suffer from the same problem we have in the current API. That is, whenever you go from |
The current APIs for that are: DateTimeOffset dto = dateTime;
DateTimeOffset dto = new DateTimeOffset(dateTime);
DateTimeOffset dto = new DateTimeOffset(dateTime, offset); The first one is a big problem for a lot of these APIs. While implicit casting helps for some things, it's not so great when it pulls in additional information. In this case, is pulls in the local time zone when kind is local or unspecified. The second one is more explicit, but still has the same behavior with regard to using the local time zone. The third one is ideal when kind is unspecified, but otherwise the kind has to match the offset or you get a runtime exception. Another tricky API that we have to remember is: TimeSpan offset = timeZoneInfo.GetUtcOffset(dateTime); Here, if the kind is unspecified then it's interpreted to belong to the time zone. But if it is local or UTC, it's first adjusted to that time zone - essentially treating it as an Instant. This does cause some grief... |
This will need to be re-thought with respect to #1901. Clearly there will be need for all of the following scenarios:
And perhaps also:
|
Why not |
I don't agree, we shouldn't make the presumption that because an offset was not provided that the offset is zero. That is incorrectly mapping an unspecified (abstract, floating, civil, etc.) value to a specific point in time. |
So how should this situation be solved? By an additional method for that case only? |
Perhaps. I'm open to ideas. I will experiment with a few things myself and report back here. Thanks. :) |
Hi, I feel there are not enough use cases in this discussion. Perhaps the strawman presented by @mj1856 is truly how it needs to be, but:
I think, in order to do that, you need to contact textbook authors, because the problem starts in the classroom. Java has completely forced the issue by creating a big promotion of this as part of Java 8.
I think you might get inspiration from Ben Evan's and Richard Warburton's article describing the motivation for cleaning up date and time in Java: I think, if there is ever a motivation for core team to fix something, the idea that "java does it better than you" (appeal to ego), might be it!
I am not as smart as you, but for the life of me, I cannot fathom why such logic would not be encapsulated in a DateTimeFloat data type if we're supposed to represent something that can float across time zones. I'm just very into domain driven design, and what you're telling me is you don't practice DDD and it's ok to you that your types don't have precise domain meaning. |
Just remove DateTime from the SDK altogether. Forget trying to fix it. Just banish it. Is this not an option? |
The use of DateTime in our internal codebase is one of the most common sources of errors when code runs in a different timezone. We generally find DateTimeOffset to be pretty robust for these purposes. We currently have our own Date type, and we have an At method that takes a TimeSpan (for TimeOfDay) and a TimeZoneInfo, and returns a DateTimeOffset. It would be good to have that support in here. I'm happy to submit a PR if you think this makes sense.
Would also be good to discourage use of DateTime and encourage DateTimeOffset wherever possible.
The text was updated successfully, but these errors were encountered: