58

I am storing all my dates in UTC format in my database. I ask the user for their timezone and I want to use their time zone plus what I am guessing is the server time to figure out the UTC for them.

Once I have that I want to do a search to see what the range is in the database using their newly converted UTC date.

But I always get this exception.

System.ArgumentException was unhandled by user code  
Message="The conversion could not be completed because the   
supplied DateTime did not have the Kind property set correctly.  
For example, when the Kind property is DateTimeKind.Local,   
the source time zone must be TimeZoneInfo.Local.  
Parameter name: sourceTimeZone"

I don't know why I am getting this.

I tried 2 ways

 TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(id);
 // I also tried DateTime.UtcNow
 DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); 
 var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone );

This failed so I tried

 DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local); 
 var utc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(now, 
                                           ZoneId, TimeZoneInfo.Utc.Id);

This also failed with the same error. What am I doing wrong?

Edit Would this work?

 DateTime localServerTime = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
 TimeZoneInfo info = TimeZoneInfo.FindSystemTimeZoneById(id);

 var usersTime = TimeZoneInfo.ConvertTime(localServerTime, info);

 var utc = TimeZoneInfo.ConvertTimeToUtc(usersTime, userInfo);

Edit 2 @ Jon Skeet

Yes, I was just thinking about that I might not even need to do all this. Time stuff confuses me right now so thats why the post may not be as clear as it should be. I never know what the heck DateTime.Now is getting (I tried to change my Timezone to another timezone and it kept getting my local time).

This is what I wanted to achieve: User comes to the site, adds some alert and it gets saved as utc (prior it was DateTime.Now, then someone suggested to store everything UTC).

So before a user would come to my site and depending where my hosting server was it could be like on the next day. So if the alert was said to be shown on August 30th (their time) but with the time difference of the server they could come on August 29th and the alert would be shown.

So I wanted to deal with that. So now I am not sure should I just store their local time then use this offset stuff? Or just store UTC time. With just storing UTC time it still might be wrong since the user still probably would be thinking in local time and I am not sure how UTC really works. It still could end up in a difference of time.

Edit3

 var info = TimeZoneInfo.FindSystemTimeZoneById(id)

 DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(DataBaseUTCDate,
                                             TimeZoneInfo.Utc, info);
Carlos
  • 102
  • 1
  • 13
chobo2
  • 83,322
  • 195
  • 530
  • 832
  • `DateTime.Now.Kind` is already `Local`; you don't need to call `SpecifyKind` on it. – SLaks Aug 30 '09 at 18:47
  • @Edit 2: Store the alerts in UTC and show them in the timezone supplied by the user as shown in my answer. – dtb Aug 30 '09 at 19:15
  • So how would that work? I get the DateTime.UtcNow then filter alerts from the db? then convert the found results to their local time? – chobo2 Aug 30 '09 at 19:29
  • Yes; do do everything in UTC and then convert the `DateTimeOffset` values to the user's timezone when you display them. – dtb Aug 30 '09 at 19:33
  • check edit3 out if that is right. Thanks – chobo2 Aug 30 '09 at 19:51
  • @Edit 3: Looks right; depending on the type of `DataBaseUTCDate`, one of the overloads of `TimeZoneInfo.ConvertTime` should do it. – dtb Aug 30 '09 at 20:12
  • What do you mean type? It is a Utc dateTime made by DateTime.UtcNow. – chobo2 Aug 30 '09 at 20:30
  • Hmm I did what I showed you but I just noticed when I was doing the conversion it does not do it. Like it seems to literally do just the time I thought it would go back. Like Even though it is August30th and 31st in UTC time. Yet when the conversion goes through it shows August31st 12:00:00AM. Why is it still does not go back to August30th? – chobo2 Aug 31 '09 at 06:46

5 Answers5

100

You need to set the Kind to Unspecified, like this:

DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified);
var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone);

DateTimeKind.Local means the in local time zone, and not any other time zone. That's why you were getting the error.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Ok I will try this. Would what I have just edited work? It seems to be off(West Africa TimeZone was off by about 10mins) when you go from one time zone to another but the UTC was the correct time. – chobo2 Aug 30 '09 at 18:38
  • Hmm so it does not work because it does not know the right Timezone? Then I am still mssing something because I think I am getting the wrong utc times. – chobo2 Aug 30 '09 at 18:50
  • Hmm ok but I guess it is alot of dancing around for nothing? Like it seems DateTime.UtcNow would get the same result. and I think if I just go with my code from one time zone to another it is off. – chobo2 Aug 30 '09 at 19:13
  • 2
    'Unspecified' is a horrible choice of name for 'something other than local or utc' – smirkingman Apr 24 '13 at 10:50
  • I don't understand why `Local` doesn't work. I *have* a Local time and I want it to go to UTC? – PeterX Jan 21 '15 at 08:58
  • @PeterX: For that, `Local` will work. It only fails if you're converting from some _non-local_ time zone, because then it isn't local. – SLaks Jan 21 '15 at 14:52
40

The DateTime structure supports only two timezones:

  • The local timezone the machine is running in.
  • and UTC.

Have a look at the DateTimeOffset structure.

var info = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");

DateTimeOffset localServerTime = DateTimeOffset.Now;

DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(localServerTime, info);

DateTimeOffset utc = localServerTime.ToUniversalTime();

Console.WriteLine("Local Time:  {0}", localServerTime);
Console.WriteLine("User's Time: {0}", usersTime);
Console.WriteLine("UTC:         {0}", utc);

Output:

Local Time:  30.08.2009 20:48:17 +02:00
User's Time: 31.08.2009 03:48:17 +09:00
UTC:         30.08.2009 18:48:17 +00:00
dtb
  • 213,145
  • 36
  • 401
  • 431
  • That seems to work. Time stuff confuses I have to read up on that DAteTimeOffSet and why it works and that converting stuff does not. But I am wondering can't I just DateTime.UtcNow? like when I do it it converts my server time to utc date what is the same if I did your dateoffSet stuff(using West Africa Time). Or would they be different? – chobo2 Aug 30 '09 at 19:00
  • 1
    As Jon Skeet says, if you just want the current time in UTC, just use `DateTime.UtcNow` or `DateTimeOffset.UtcNow`. – dtb Aug 30 '09 at 19:03
  • 2
    I wonder why DateTime doesn't provide a TimeZoneInfo property, so we could specify a Date and Time along with the timezone!? – brighty Aug 28 '14 at 12:47
10

Everyone else's answer seems overly complex. I had a specific requirement and this worked fine for me:

void Main()
{
    var startDate = DateTime.Today;
    var StartDateUtc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.SpecifyKind(startDate.Date, DateTimeKind.Unspecified), "Eastern Standard Time", "UTC");
    startDate.Dump();
    StartDateUtc.Dump();
}

Which outputs (from linqpad) what I expected:

12/20/2013 12:00:00 AM

12/20/2013 5:00:00 AM

Props to Slaks for the Unspecified kind tip. That's what I was missing. But all the talk about there being only two kinds of dates (local and UTC) just muddled the issue for me.

FYI -- the machine I ran this on was in Central Time Zone and DST was not in effect.

JohnOpincar
  • 5,620
  • 3
  • 35
  • 38
7

As dtb says, you should use DateTimeOffset if you want to store a date/time with a specific time zone.

However, it's not at all clear from your post that you really need to. You only give examples using DateTime.Now and you say you're guessing that you're using the server time. What time do you actually want? If you just want the current time in UTC, use DateTime.UtcNow or DateTimeOffset.UtcNow. You don't need to know the time zone to know the current UTC time, precisely because it's universal.

If you're getting a date/time from the user in some other way, please give more information - that way we'll be able to work out what you need to do. Otherwise we're just guessing.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

UTC is just a time zone that everyone agreed on as the standard time zone. Specifically, it's a time zone that contains London, England. EDIT: Note that it's not the exact same time zone; for example, UTC has no DST. (Thanks, Jon Skeet)

The only special thing about UTC is that it's much easier to use in .Net than any other time zone (DateTime.UtcNow, DateTime.ToUniversalTime, and other members).

Therefore, as others have mentioned, the best thing for you to do is store all dates in UTC within your database, then convert to the user's local time (by writing TimeZoneInfo.ConvertTime(time, usersTimeZone) before displaying.


If you want to be fancier, you can geolocate your users' IP addresses to automatically guess their time zones.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • That will be future versions lol. Got to get the basics down first. – chobo2 Aug 30 '09 at 19:30
  • Then what do you want to know now? – SLaks Aug 30 '09 at 19:33
  • So this would not give any time different then. Like first getting all alerts by UTC then changing them to to local time once found won't cause any alerts to be missed? Like their won't be some that where just some how where off by a couple mins or something? – chobo2 Aug 30 '09 at 19:33
  • I am trying to figure out how to give all the alerts the user needs to see at the right time based on their timezone. Thats what I need. – chobo2 Aug 30 '09 at 19:34
  • 6
    UTC is *not* the same as the time zone in London. Most importantly, London has daylight savings applied in summer - so currently, despite being in the Europe/London time zone, I'm offset by an hour from UTC. – Jon Skeet Aug 30 '09 at 20:27
  • Fixed; thanks. Are there any other differences? (Of more than one second) – SLaks Aug 30 '09 at 20:33