A Brief History of DateTime Follow-up [Anthony Moore]
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.