4

I have a method that creates a UTC DateTime from user input, using the GMT offset of their geographical position:

public static DateTime LocalToUtc
    (int year, int month, int day, int hour, decimal gmtOffset) {

    // argument validation here

    var dateTime = new DateTime(year, month, day).AddHours(hour);
    var dateTimeOffset = 
        new DateTimeOffset(dateTime, TimeSpan.FromHours(gmtOffset));

    return dateTimeOffset.UtcDateTime;

} 

The problem is that this function is off by an hour if it's daylight savings in the user's timezone.

So while my personal GMT offset is -8, the current timezone offset is -7 because of daylight savings.

How do I change the function above to account for daylight savings? Don't I somehow need to create some timezone object from the GMT offset and get its timezone offset?

4 Answers4

3

There is no way to do it without knowing the actual time zone: several time zones have the same base UTC offset, but with different rules for daylight saving time. For instance, W. Europe Standard Time and W. Central Africa Standard Time both have an offset of +01:00, but the former supports DST while the latter doesn't. So the offset is not enough to decide whether DST applies or not...

Your method should take a TimeZoneInfo parameter instead of gmtOffset. This way you can just use the TimeZoneInfo.ConvertTime method to convert the date, it will automatically take DST into account.

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • Okay, so let's say I had data that gave me what time zone they were in. How do I create a TimeZoneInfo object? In other words, we change the parameter to TimeZoneInfo. Okay. Then we need a TimeZoneInfo object to send to the function. How is that constructed? – DotNetQuestionDate Apr 27 '11 at 22:39
  • 2
    You can use the `TimeZoneInfo.FindSystemTimeZoneById` method. Or you could get the list of all time zones with the `TimeZoneInfo.GetSystemTimeZones` method, and make the user pick from that list. – Thomas Levesque Apr 27 '11 at 22:47
  • Or you can call TimeZoneInfo.CreateCustomTimeZone to instantiate whatever time zone you want. – Erik Funkenbusch Apr 27 '11 at 22:53
1

DateTime already has methods to do this called ToLocalTime() and ToUniversalTime(). What's wrong with using that?

EDIT:

Based upon the comment that the author is looking to convert to utc from a timezone other than the current computers timezone, then I refer you to John Skeets answer Here

From the MSDN documentation:

string displayName = "(GMT+06:00) Antarctica/Mawson Time";
string standardName = "Mawson Time"; 
TimeSpan offset = new TimeSpan(06, 00, 00);
TimeZoneInfo mawson = TimeZoneInfo.CreateCustomTimeZone(standardName, 
    offset, displayName, standardName);
Console.WriteLine("The current time is {0} {1}", 
                  TimeZoneInfo.ConvertTime(DateTime.Now, 
                       TimeZoneInfo.Local, mawson), mawson.StandardName);      
Community
  • 1
  • 1
Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • DateTime is buggy when diffing values in different timezones. It's especially annoying when the kind of UTC, Local is not set at all. – GregC Apr 27 '11 at 22:19
  • @GregC - No, it's not buggy. You just can't always be sure that a DateTime has the correct localtime information set. That's why you convert to UTC to do the comparison. – Erik Funkenbusch Apr 27 '11 at 22:22
  • 1
    @Mystere Man, GregC is right, the DateTime structure is not suited to handle different time zones. That's precisely why the DateTimeOffset structure was introduced... – Thomas Levesque Apr 27 '11 at 22:24
  • @Mystere Man: try to make a new DateTime object using DateTime.Now, and another using DateTime.UtcNow. Take a difference. Observe bug (unless you're in GB). – GregC Apr 27 '11 at 22:26
  • How can I use new DateTime(year, month, day).AddHours(hour).ToUniversalTime() when no GMT offset information is included in the DateTime? How will it know what GMT to use? – DotNetQuestionDate Apr 27 '11 at 22:29
  • @GregC: Not observing bug. 7 hours for me, PST plus +1 for daylight savings. – DotNetQuestionDate Apr 27 '11 at 22:31
  • I'm aware of that. But it's not *buggy*. Buggy implies that it's not working as it was designed. DateTime does exactly what it was designed to do. If you expect it to do something it wasn't designed for, that's not buggy. It's like complaining that a Smartcar is buggy because it has problems hauling heavy trailers. The question asker did not say he was comparing dates in multiple time zones though. If the app is being run on the local computer, then ToUniversalTime() is perfectly fine. – Erik Funkenbusch Apr 27 '11 at 22:31
  • @DotNetQuestionDate - There are several DateTime constructor overloads that allow you to specify wither the datetime is UTC or LocalTime. LocalTime is local to the computer running the app. If you want to specify a DateTime for a different timezone that the computer the app is running on then that's a little different – Erik Funkenbusch Apr 27 '11 at 22:35
  • OK, let me give you another bug then. It's possible to create a DateTime value that does not specify UTC or Local. Try to call ToUniversalTime() on that value. Should throw, since it really doesn't know what to do. But it doesn't. – GregC Apr 27 '11 at 22:36
  • Yes, timezone different than the computer the app is running on. – DotNetQuestionDate Apr 27 '11 at 22:40
  • @GregC - It doesn't throw if it's already in UTC either. It just does nothing if that's the case. If it's unspecified, the framework assumes it's local time (this is the documented behavior). – Erik Funkenbusch Apr 27 '11 at 22:41
  • @Mystere Man: I am not entirely convinced. Let's leave it at that. Let's agree to disagree. – GregC Apr 27 '11 at 22:49
0

You should use the TimeZone.GetUtcOffset Method if you don't want to use the built-in UTC method.

http://msdn.microsoft.com/en-us/library/system.timezone.getutcoffset.aspx

It will give you your offset to UTC. You could also use the built-in methods to get UTC time from local times.

http://msdn.microsoft.com/en-us/library/system.datetime.touniversaltime(v=VS.100).aspx

Matthew
  • 10,244
  • 5
  • 49
  • 104
0

Since it sounds you're asking users for a time offset that might not necessarily be in the machine's local time zone, the local machine time wouldn't work. But using the TimeZoneInfo class, you should be able to construct an instance that accounts for the offset and DST status and use the built-in methods from there.

Edit: On my machine, at least, TimeZoneInfo.GetSystemTimeZones() seems to return all valid time zones. This could be easily mapped to a drop-down menu to allow the user to select.

Calvin Fisher
  • 4,653
  • 5
  • 36
  • 47
  • The GMT offset is calculated based on a geographical dataset. They have to select their lat/lng when they sign up. That lat/lng are then looked up in a DB table which derives the GMT offset. – DotNetQuestionDate Apr 27 '11 at 22:32
  • Are they inputting their lat/lng specifically to get the timezone, or is it used for some other purpose as well? If not, you could just use `TimeZoneInfo.GetSystemTimeZones()` and give the user a drop-down selection. Alternately: Does this dataset just give offset, or DST information as well? If it has DST information, you could create a custom instance of TimeZoneInfo on the fly. – Calvin Fisher Apr 27 '11 at 23:04