views:

550

answers:

4

I've got an asp.net application that must run some code every day at a specific time in the Eastern Time Zone (DST aware). So my first thought is, get the eastern time value and convert it to local server time.

So I need something like this:

var eastern = DateTime.Today.AddHours(17); // run at 5pm eastern
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var utc = TimeZoneInfo.ConvertTimeToUtc(eastern, timeZoneInfo);
var local = TimeZoneInfo.ConvertTimeFromUtc(utc, TimeZoneInfo.Local);

But how do I specify that the eastern DateTime object should be in the EST timezone.

Am I approaching this the wrong way?

+1  A: 

A DateTime doesn't know about a time zone. Even DateTimeOffset doesn't really know about a time zone - it knows about a UTC instant and an offset from that.

You can write your own struct which does have a TimeZoneInfo and a DateTime, but I'm not sure you need it in this case. Are you just trying to schedule 5pm in Eastern time, or is this actually more general? What are you doing with the DateTime (or whatever) afterwards? Using DateTimeOffset and TimeZoneInfo you can definitely get the UTC instant of the time you're interested in; if you just need to know the time between "now" and then, that's fairly easy.

I feel duty-bound to point out that when Noda Time is production-ready, it would almost certainly be the right answer :)

Jon Skeet
@Aaronaught: Sorry, DateTimeZone is what we use in Noda Time :) Will edit.
Jon Skeet
Yes, I am just trying to schedule something to run at 5pm eastern (DST aware).
Freyday
+1  A: 

You could use the DateTime.UtcNow to get UTC central time(which I believe is GMT 0) and from htere on just figure out how many time zones the one you want is and remove/add an hour for each zone.

Ravenheart
+2  A: 

First, there are several things you have to consider. You have to deal with Daylight Savings Time, which from time time seems to change (the start and end dates have changed twice in the last 10 years). So in the Northern Hemisphere Winter, Eastern time is -5 GMT (or UTC). But, in the Summer it's -6 GMT or is that -4 GMT, I can never keep it straight (nor should I have to).

There are some DNF library functions to deal with time zone information, however you really need .net 3.5 for the most useful stuff. There's the TimeZoneInfo class in .net 3.5.

TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTime dt = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.Now,
     TimeZoneInfo.IsDaylightSavingsTime(tzi) ? 
        tzi.DaylightName : tzi.StandardName);
if (dt.Hour == 17)
    ....

Also, keep in mind that twice every year an hour is lost or gained, so you also have to account for that if, for example, you have a countdown timer you display "time until next processing" or something like that. The fact is, time handling is not as easy as it would seem at first thought, and there are a lot of edge cases.

Mystere Man
Every time I try to run code (.net 3.5) that tries to convert DateTime.Now to EST I get the following error: "The conversion could not be completed because the supplied DateTime did not have the Kind property set correctly."
Freyday
This typically means you're setting your source DateTime in a way that bypasses the setting of Kind type. DateTime.Now, for instance has a Kind of Local set. How are you supplying the source DateTime? I suspect your using one of the constructors like this: DateTime dt = new DateTime(2010, 2, 1); this sets the Kind to "Unspecified". This sets the Kind: DateTime dt = new DateTime(2010, 2, 1, 0, 0, 0, DateTimeKind.Utc);
Mystere Man
A: 

Seems I was able to answer my own question. Here's the code I'm using to get a next-run DateTime object.

    private DateTime GetNextRun()
    {

        var today = DateTime.Today;
        var runTime = new DateTime(today.Year, today.Month, today.Day, 17, 0, 0);
        var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
        var offset = timeZoneInfo.GetUtcOffset(runTime);
        var dto = new DateTimeOffset(runTime, offset);

        if (DateTime.Now > dto.LocalDateTime) 
            dto = dto.AddDays(1);

        return dto.LocalDateTime;
    }

Doing all the conversion using DateTimeOffset instead of DateTime proved effective. It even seems to handle Daylight Savings Time correctly.

Freyday