Wednesday, 5 October 2011

Working with dates

Lately I've been doing a good amount of work on a website feature that requires the output of a relative date and time. For example, a entry written on a user's wall (the website has social aspects) may be annoted with "written by John Smith about two hours ago." It is a relatively straight forward task to accomplish this - but I thought I'd write a few words about some pitfalls you may come across.

Even if you have only every written the most trivial of applications you are likely to have used the DateTime structure in .NET (if you've written .NET apps, that is). And you're very likely to have used DateTime.Now; Let's take a closer look at the DateTime structure. According to Microsoft's documentation a DateTime "Represents an instant in time, typically expressed as a date and time of day." And, DateTime.Now "Gets a DateTime object that is set to the current date and time on this computer, expressed as the local time."

Cool. DateTime.Now is really handy for getting a handle on the current time, and we've all used it. But now I'm going to tell you that you shouldn't.

DateTime.Now returns a DateTime object that represents "the current date and time on this computer, expressed as the local time." As convenient as this may be, it's a potential source of trouble. "This computer" is the computer where the code executes. It could be your desktop machine, a development server, a production server, or a mobile device. The time on that machine depends on where the machine is located (which timezone it is in) and whether or not the machine is affected by daylight savings. So why is this a problem?

The problem is that if you cannot guarantee that all the machines in your infrastructure are all in the same timezone and are equally affected (or not affected) by daylight savings, using DateTime.Now in your code will potentially yield different timestamps on different machines even if the call to DateTime.Now was made at exactly the same time on the machines in question. 1pm in Oslo, Norway, on October 5th 2011 is not the same time as 1pm in London, UK, on the same date. "But that's just silly", I hear you say. "All our infrastructure is in the same data centre in one place." OK. Fine. That may very well be the case. But what about your users. Where are they? Are all of them in the same time zone as your servers? And is it likely that you'll never grow beyond having only local users and having only one data centre?

Even if your answer to the above questions is "we'll never scale beyond one data centre and all our users are in the same place and always will be" I think you should keep reading. It might just make your life a little simpler down the road. Just in case.

The problem with DateTime.Now is that it always represents the local time of the machine on which the code executes, and you don't really want to worry about where that machine is, because doing so makes life as a developer painful. What you want to do instead is use DateTime.UtcNow which returns an instance of DateTime representing the Coordinated Universal Time (UTC) of now. UTC is the local time of the server less any timezone differences and less any daylight savings difference. If you only ever store and use UTC DateTimes then none of your DateTime comparisons will ever have to take into consideration any time differences caused by time zones or daylight savings.

The only thing you now have to worry about is the thing you should worry about, which is displaying the correct date to your end users. You'll have to adjust for the timezone they're in because a date for an event at 12pm in London should be rendered as 1pm for a user in Oslo (they're always an hour ahead of London time).

Regardless of whether you use UTC or not, you'll always have to consider time zones and daylight savings when rendering dates for a user. Using UTC, that's all you'll have to worry about. If you use DateTime.Now, however, you'll also have to ensure that you know what the time offset of that DateTime instance is if you're going to compare it to another date, or if you're going to render it to a user. Pain in the arse (PITA).

I reckon that you should always use UTC times in your applications regardless of what your user base might look like. It makes life simpler from the start, and if you ever need to support users across different timezones you'll be a step ahead.

So, as a rule, this is what I do:

  • Always call DateTime.UtcNow and never DateTime.Now. (In fact, I don't use DateTime... I use DomainTime which is a wrapper I've created around DateTime. I'll write more about that in a later post).
  • Always treat DateTime stored in a database as UTC. This means that when I read that DateTime out of the database I specify that it is a UTC DateTime using the DateTime.SpecifyKind() method. This is very important, because the machine will by default treat any DateTime as local time.

    Note that if you're using an ORM such as NHibernate you need to tell the ORM that the date should be treated as UTC. With FluentNHibernate this is really simple:

    Map(x => x.DateCreated).CustomType().Not.Nullable();

  • Lastly, before displaying the date to the user, I apply the time difference between UTC and the user's location. There are several ways of doing this. For example you can have your users tell you which time zone they're in and you can apply the offset. Or, if your users are web users you can use Javascript's Date.getTimezoneOffset() method and apply the difference (in minutes) to your UTC date. Check out this StackOverflow question for some specific pitfalls of that particular method.

I'll follow up on this post with another post or two about displaying relative times and how to use a DomainTime wrapper around your dates.

3 comments:

Nigel Holland said...

Hi Øyvind,

I agree with you about UtcNow.

I find it useful for my date/time wrappers to have the ability to set the value that UtcNow() returns for unit testing purposes.

Nigel

Øyvind Valland said...

Hi Nigel,

Yeah, any wrapper around DateTime should allow you to specify the date/time so that you can use it for testing. That's why I don't use DateTime directly any more, because it makes testing code that depends on time very difficult. I'm going to write a short post about this soon.

Anonymous said...

Anime Nippon~Jin - Kagi Nippon He, INVITE YOU TO TO VISIT THE WEB AND FORUM OFFICER, WHERE YOU WILL BE ABLE TO FIND:

- Dates of Conventions in the whole American Continent of Anime, Sleeve, Comics, Meetings of Cosplay, Video games, Festivals, Concerts (Peru, Chile, Honduras, Brazil, Mexico, EUA, Canada, Argentina, Uruguay, Costa Rica, Colombia, Bolivia, Venezuela, Paraguay, Ecuador, El Salvador, Panama, etc.) Europe (Spain, Italy, England, France, Portugal) and It was Grasping and Oceania (Japan, South Korea, China, Malaysia, Taiwan, etc.)

- A place with topics of conversation and where you can initiate conversation topics.

- Shops of Anime in your locality.

- Cosplay, tutors, photos, Galleries of Events, Videos, and more.

- Video games

- Radio, Movies, Television


AND ALSO YOU CAN FOLLOW US FOR FACEBOOK, TWITTER, GOOGLE, TUMBLR AND WORDPRESS!!

AND HELP US WHY THE WEB CONTINUES IN FOOT DEACTIVATING ADBLOCKER ON OUR PAGE AND TO CLICK IN SOME ANNOUNCEMENT OF OURS SPONSORS.


Official WEB's


http://kaginipponhe.com
http://kaginipponhe-anj.blogspot.mx/
http://animenipponjin-knh.blogspot.mx/
http://animenipponjin.b1.jcink.com/



FACEBOOK
https://www.facebook.com/AnimeNipponJin



TWITTER

https://twitter.com/AnimeNipponJin



GOOGLE

https://plus.google.com/u/0/108899406425756347857/posts/p/pub



TUMBLR



http://anime-nippon-jin.tumblr.com/

WORDPRESS:

https://kaginipponhe.wordpress.com/



Say NO to racism and discrimination in the Cosplay

Di NO al racismo y discriminación en el Cosplay

Dire NO al razzismo e alla discriminazione nel Cosplay

บอกว่าไม่มีการเหยียดสีผิวและการเลือกปฏิบัติในการคอสเพลย์

Zeg nee tegen racisme en discriminatie in de Cosplay

Mengatakan TIDAK untuk perkauman dan diskriminasi dalam cosplay yang

Sagen Sie NEIN zu Rassismus und Diskriminierung in der Cosplay

Скажи НЕТ расизма и дискриминации в косплей

コスプレでの人種差別や差別にノーと言ってやる



http://kaginipponhe.b1.jcink.com/


https://www.facebook.com/pages/Di-NO-al-racismo-y-discriminaci%C3%B3n-en-el-Cosplay/175730419146227