To put it bluntly, the date/time libraries in .NET are flawed. From the beginning there were no concepts of time zones or really any sort of universal time. As a result most libraries were designed to accept a DateTime class and left the developer to guess if the value should be in local or UTC time. For client server applications the problem was even worse, as local could mean client local or server local time.
In .NET 2.0 the DateTime.Kind property was introduced. This didn’t really solve any problems, but it did break existing applications in rather subtle ways. Even to this day most developers don’t take the time to correctly set the Kind property, or really pay any attention to it. As Jon Skeet explains,
The value of the property affects various different operations in different ways. For example, if you call ToUniversalTime() on an "unspecified" DateTime, it will assume that you really meant it as a local value before. On the other hand, if you call ToLocalTime() on an "unspecified" DateTime, it will assume that you really meant it as a UTC value before. That's one model of behaviour.
In .NET 3.5 we were finally introduced to the DateTimeOffset type. While this finally offered a universal means of including an offset from UTC, it also brought with it some new flaws. For example, a DateTime can be implicitly cast into a DateTimeOffset even if the Kind property is Unspecified. So chances are anytime you see such a conversion you are probably seeing a bug. But more importantly, a DateTimeOffset doesn’t actually represent time zone. Again, we turn it over to Jon Skeet to explain,
A local date and time isn't tied to any particular time zone. At this moment, is it before or after "10pm on August 20th 2011"? It depends where you are in the world. (I'm leaving aside any non-ISO calendar representations for the moment, by the way.) So a DateTimeOffset contains a time-zone-independent component (that "10pm on ..." part) but also an offset from UTC - which means it can be converted to an instant on what I think of as the time line. Ignoring relativity, everyone on the planet experiences a a particular instant simultaneously. If I click my fingers (infinitely quickly!) then any particular event in the universe happened before that instant, at that instant or after that instant. Whether you were in a particular time zone or not is irrelevant. In that respect instants are global compared to the local date and time which any particular individual may have observed at a particular instant.
[…]
DateTimeOffset also isn't a good type to use if you want to tie yourself to a specific time zone, because it has no idea of the time zone which gave the relevant offset in the first place. As of .NET 3.5 there's a pretty reasonable TimeZoneInfo class, but no type which talks about "a local time in a particular time zone". So with DateTimeOffset you know what that particular time is in some unspecified time zone, but you don't know what the local time will be a minute later, as the offset for that time zone could change (usually due to daylight saving time changes).
To address many, but far from all, of these problems Jon Skeet is working on a port of Joda Time called Noda Time. What follows are the key concepts in both libraries.
- Instant: Represents an instant on the timeline measured from the Unix epoch. In .NET this uses ticks while the Java version uses milliseconds. There are 10,000 ticks in a millisecond. Aside from having a different epoch, this class is analogist to a DateTimeOffset with the offset being zero.
- Partial: A partial represents part of a local date/time, but isn’t complete enough to represent a specific instant in time. For example, the MonthDay subclass can be used to represent annual events such as birthdays. The YearMonth subclass is useful for things like credit card expiration dates where defaulting to the first or last of the month may be inappropriate. For anyone who has had problems mixing date-only and date-time values, the LocalDate type will be appreciated.
- Interval: This represents a specific time interval. Unlike TimeSpan, this retains the starting and ending instants. You would use this to express concepts such as “on January 1st, 2011 from 8 am to 12 pm”.
- Duration: This is essentially the TimeSpan class. The resolution is in ticks for .NET and milliseconds for Java.
- Period: “A period in Joda-Time represents a period of time defined in terms of fields, for example, 3 years 5 months 2 days and 7 hours. This differs from a duration in that it is inexact in terms of milliseconds. A period can only be resolved to an exact number of milliseconds by specifying the instant (including chronology and time zone) it is relative to.”
- DateTimeZone and Chronology: A DateTimeZone contains most of the information needed to represent a timezone. It is rarely used alone, rather it is combined with a specific CalendarSystem object into a type called Chronology.
- ZonedDateTime: This combines a local date/time with a specific time zone and calendar system. This makes it less error prone than just using a UTC offset.
Community comments
Correction
by Hernan Gonzalez,
Re: Correction
by Jonathan Allen,
Unix epoch
by Mike Gale,
Re: Unix epoch
by Jonathan Allen,
Good to see a better library for DateTime handling
by Georg Müller,
Correction
by Hernan Gonzalez,
Your message is awaiting moderation. Thank you for participating in the discussion.
The first mentioned concept (besides lacking itemization) should be "Instant" instead of "Instance". This concept is explained in the first sentence (an instant on the timeline, that is, the time that physics uses -disregarding relativity, of course- and perhaps also leap seconds). Waht follows in the paragraph can induce confusion: how it's represented is conceptually irrelevant, and it's really not a "DateTimeOffset with the offset being zero", the concepts "date, time, year, month, etc" does not enter here. An example could be the precise instant in which Neil Armstrong set foot on the moon: that "physical" instant could be coded in several ways (for example, the civil datetime at GMT) but one must no confuse the representation with the concept (1/2 50/100 0.5 are the same number).
Another issue with the list is that it leaves aside some really key concepts (more than the Partial). The LocalDateTime: a "civil" date and time, what shows some calendar and wall, what one selects when one records an appointment in google calendar, for example; essentially a tuple of numbers {year-month-day-hour-min-sec-secfrac} with NO specified (nor implied!) timezone. The LocalDate is the same, but without time (just year-month-day).
Finally, a DateTime (or ZonedDateTime) is a "complete" date-time spec (a LocalDateTime plus a Timezone); from a DateTime we can obtain an Instant, but not viceversa.
Good to see a better library for DateTime handling
by Georg Müller,
Your message is awaiting moderation. Thank you for participating in the discussion.
Its great that there is now some movement in the sorry state of DateTime Handling in .NET.
JodaTime is a great library and its nice that somebody uses its best features to bring it over to the .NET space.
Re: Correction
by Jonathan Allen,
Your message is awaiting moderation. Thank you for participating in the discussion.
Clearly you know far more about this topic than I. If you would be interested in writing a more comprehensive article on Joda/Noda Time and time zone issues in general please contact me at jonathan@infoq.com.
Unix epoch
by Mike Gale,
Your message is awaiting moderation. Thank you for participating in the discussion.
The choice of this zero looks bad to me. We need to use something like the base of Julian day Numbers or whatever and get away from things like that.
Re: Unix epoch
by Jonathan Allen,
Your message is awaiting moderation. Thank you for participating in the discussion.
I believe that the Unix epoch was chosen to reduce the amount of work necessary to port the specific calendars over from Joda Time.
My personal opinion is that it doesn't matter what is chosen because we shouldn't be dealing with zero-time anyways. Unfortunately structs don't work that way.