Thanks all for the feedback on A Brief History of DateTime. Thanks Justin for responding to most of this. I can elaborate on some of the issues raised:
>> (3) Was there any consideration given in the design process around making DateTimeOffset an (immutable) reference type?
We did not really consider this because unless something is going to be of an unpredictable size, there is not much benefit of an immutable reference type over a value type. Also, since we wanted to replace many DateTime scenarios going forward we did not want to have a type that performed significantly differently. Can you clarify why you would prefer this design?
>> If I use DateTimeOffset, I will still need to convert it to DateTime when passing it as a parameter to existing methods. This introduces an opportunity for errors.
This might seem scary, but it should not be as problematic as it might seem. The conversions to DateTime can lose some the display time, but not the absolute point in time. Any existing API that treats the DateTime as an absolute point in time will get the right results. Once that don’t will often be subject to usage problems regardless of whether the input is a DateTime or something converted from DateTimeOffset.
>> As it is, we now filter through all source code searching for DateTime usage. Every instance is flagged as a potential bug (and usually is) needing special review
The need to do this is unfortunate, but I would agree that it is necessary with DateTime. The nice thing about DateTimeOffset is that if you can be in a world where within your own application you are using it and not DateTime, the number of usability problems is significantly reduced. Arithmetic, comparison, display and serialization all default to a behavior that is much more likely to be what it is intended. Conversions to DateTime for legacy APIs still need some checking, but there are fewer mistake opportunities than when passing in a DateTime directly.
>> A better solution is to wrap something like http://www.twinsun.com/tz/tz-link.htm.
We agonized over whether to include separate data with this functionality because we know it is important and preferable to the Windows data by many customers. It was for this reason that we ensured that the system was extensible enough that someone could populate this data into TimeZoneInfo. A challenge was that there is significant complication to everyone to dealing with multiple copies of this data when updates are needed, such as happened in the USA this year. This is something that could still be done in the future if there is sufficient demand, so we can take this as an AddRef on demand for it, but for now we did not want to default people into having a second copy of the time zone data on every machine that could create administrative problems if it got stale.
>> Any thoughts on this? If not, what is the preferred way of storing and converting back to ateTimeOffset from a SQL column?!
IIn this case you would really need to decide if you cared about knowing the absolute time as well as the offset. As discussed, most of the time you need the absolute time, so I would just use the UTC. If you do care about both and you can’t wait for the next SQL release, you would need to use an extra column, which could either be an extra DateTime (e.g. DateTimeutc+DateTimeLocal), or you could store either the local or UTC plus its offset, probably in an integer field counting the ticks, depending on which one you needed more often, although I would recommend the UTC value because then sorting the column would be meaningful.
>> What do we do with a restless trader in Singapore executing a trade through a London app online to the NASDAQ in New York, and need to ensure we execute the trade within the Quote time.. what we need is a UTCDateTime from end-to-end that is only converted to local-time when we need to display it.. Universal Time is a Universal Standard!
>> What we really need is a UtcDateTime that *is a* DateTime. That may not be possible for technical reasons (value type), but if an IDateTime interface were to be introduced and all framework signatures were updated to use the interface, both backward compatibility could be preserved and those needing a UtcDateTime would be free to use it without need for conversion. It would integrate well with the framework, too.
This is great input. I did not elaborate why we did not head further down a road like this. Another way to have solved this would be to have a type that is UTC by definition and can never be anything else, which is essentially what Win32 did. If you want to go with a model of Interface coupled with concrete types that could be UTC, you have something very close to that in Whidbey anyway, since you can have DateTime instances with Kind=Utc that you can inspect and verify that are UTC. Regardless of whether you are using this flags-based approach or using an Interface with concrete implementations, the solution is kind of ineffective. For example, in both cases public APIs that take DateTime as input have to deal with the possibility that is not the UTC input, and possibly that its time zone is completely unspecified. You can use DateTimeOffset in this manner too if you follow the convention of always using a zero offset.
There are many other problems with a type constrained to be UTC. It is hard for human beings to process in situations like debugging and inspecting persisted formats, because they have to do some math every time they want to make sense of it. Obviously when you display to a user you want to convert to a local time. Here things get tricky. You can either make this conversion happen only during formatting (e.g. a ToStringLocal()) method but then you can’t programmatically inspect sub-parts of the local instance which you need to do some times. Alternatively you can provide a local representation but to do that you either have to break the “always UTC” convention and deal with the complexity of that, or you need a whole new type just for that. Another problem is that this still does not address scenario F above where you need to work with an absolute point in time, but you also don’t want to lose what its original local value was where that might differ from the time zone on the machine.
What you ideally want is a type with the identity and reliability of a UTC Data Type without these usability down-sides. This is what DateTimeOffset is. It may not be obvious at first, but the identity, comparison and arithmetic are all based on the absolute UTC time so you get this more reliable and consistent behavior where it counts, but it also addresses the usability concerns above. The time zone offset part can be thought of as ancillary data that is not part of the value’s core identity in the same way that the trailing zeros in a Decimal values are.