views:

35083

answers:

8

We are developing a C# application for a web-service client. This will run on Windows XP PC's.

One of the fields returned by the web service is a DateTime field. The server returns a field in GMT format i.e. with a "Z" at the end.

However, we found that .NET seems to do some kind of implicit conversion and the time was always 12 hours out.

The following code sample resolves this to some extent in that the 12 hour difference has gone but it makes no allowance for NZ daylight saving.

CultureInfo ci = new CultureInfo("en-NZ");
string date = "Web service date".ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            

As per this date site:

UTC/GMT Offset

Standard time zone: UTC/GMT +12 hours
Daylight saving time: +1 hour
Current time zone offset: UTC/GMT +13 hours

How do we adjust for the extra hour? Can this be done programmatically or is this some kind of setting on the PC's?

+8  A: 

TimeZone.CurrentTimeZone.ToLocalTime(date);

Dana
This only works if the system knows that the date being converted from is in UTC. Please see my answer.
Drew Noakes
A: 

In answer to Dana's suggestion:

The code sample now looks like:

string date = "Web service date"..ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(convertedDate);

The original date was 20/08/08; the kind was UTC.

Both "convertedDate" and "dt" are the same:

21/08/08 10:00:26; the kind was local

nzpcmad
Please see my answer for an explanation of this.
Drew Noakes
A: 

I had the problem with it being in a data set being pushed across the wire (webservice to client) that it would automatically change because the DataColumn's DateType field was set to local. Make sure you check what the DateType is if your pushing DataSets across.

If you don't want it to change, set it to Unspecified

Miles
A: 

I'd just like to add a general note of caution.

If all you are doing is getting the current time from the computer's internal clock to put a date/time on the display or a report, then all is well. But if you are saving the date/time information for later reference or are computing date/times, beware!

Let's say you determine that a cruise ship arrived in Honolulu on 20 Dec 2007 at 15:00 UTC. And you want to know what local time that was.
1. There are probably at least three 'locals' involved. Local may mean Honolulu, or it may mean where your computer is located, or it may mean the location where your customer is located.
2. If you use the built-in functions to do the conversion, it will probably be wrong. This is because daylight savings time is (probably) currently in effect on your computer, but was NOT in effect in December. But Windows does not know this... all it has is one flag to determine if daylight savings time is currently in effect. And if it is currently in effect, then it will happily add an hour even to a date in December.
3. Daylight savings time is implemented differently (or not at all) in various political subdivisions. Don't think that just because your country changes on a specific date, that other countries will too.

Mark T
A: 

Don't forget if you already have a DateTime object and are not sure if it's UTC or Local, it's easy enough to use the methods on the object directly:

DateTime convertedDate = DateTime.Parse(date);
DateTime localDate = convertedDate.ToLocalTime();

How do we adjust for the extra hour?

Unless specified .net will use the local pc settings. I'd have a read of: http://msdn.microsoft.com/en-us/library/system.globalization.daylighttime.aspx

By the looks the code might look something like:

DaylightTime daylight = TimeZone.CurrentTimeZone.GetDaylightChanges( year );

And as mentioned above double check what timezone setting your server is on. There are articles on the net for how to safely affect the changes in IIS.

Brendan Kowitz
The system will deal with this comlexity for you, provided you tell the system what 'kind' your date is (local/utc/unspecified).
Drew Noakes
+6  A: 

I'd look into using the System.TimeZoneInfo class if you are in .NET 3.5. See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx. This should take into account the daylight savings changes correctly.

string date = "2009-02-25 16:13:00Z"; // Coordinated Universal Time string from DateTime.Now.ToUniversalTime().ToString("u");
DateTime localDateTime = DateTime.Parse(date); // Local .NET timeZone.
DateTime utcDateTime = localDateTime.ToUniversalTime();

// ID from "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time Zone"
// See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.id.aspx
string nzTimeZoneKey = "New Zealand Standard Time";
TimeZoneInfo nzTimeZone = TimeZoneInfo.FindSystemTimeZoneById(nzTimeZoneKey);
DateTime nzDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, nzTimeZone);
Daniel Ballinger
If you are working in your own time zone (en-NZ in this case) then you don't need to deal with TimeZoneInfo. It's just unnecessary complexity. See my answer for more detail.
Drew Noakes
+1 for not assuming server is in New Zealand
ajbeaven
+25  A: 

Given that you are parsing the date from a string, the system has no way of knowing what kind of DateTime you are parsing.

DateTime has a kind property, which can have one of the three values:

  • Unspecified
  • Local
  • Utc

So for the code you show:

DateTime convertedDate = DateTime.Parse(dateStr);

var kind = converted.Kind; // will equal DateTimeKind.Unspecified

You say you know what kind it is, so tell it.

DateTime convertedDate = DateTime.SpecifyKind(
    DateTime.Parse(dateStr),
    DateTimeKind.Utc);

var kind = converted.Kind; // will equal DateTimeKind.Utc

Now, once the system knows its in UTC time, you can just call ToLocalTime:

DateTime dt = converted.DateToLocalTime();

This will give you the result you require.

Drew Noakes
just another way to specify the kind: `DateTime convertedTime = new DateTime(DateTime.Parse(dateStr).Ticks), DateTimeKind.Utc);`
Brad
Cheers, @Brad..
Drew Noakes
+1  A: 

I came across this question as I was having a problem with the UTC dates you get back through the twitter API (created_at field on a status); I need to convert them to DateTime. None of the answers/ code samples in the answers on this page were sufficient to stop me getting a "String was not recognized as a valid DateTime" error (but it's the closest I have got to finding the correct answer on SO)

Posting this link here in case this helps someone else - the answer I needed was found on this blog post: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ - basically use DateTime.ParseExact with a format string instead of DateTime.Parse

DannykPowell