How to work with time zones in ASP.NET?
Asked Answered
B

7

33

I am working on an "online reminder system" project (ASP.NET 2.0 (C#) / SQL Server 2005)

As this is a reminder service which will send the mail to users on a particular dates. But the problem is users are not from a specific countries, they are from all over the world and from different time zones. Now When I am registering I am asking for users time zone in the same way as windows asks our time zone at the time of installation.

But I am not getting the if the user selected (+5.30) or something timezone then how to handle this time zone in my asp.net application. How to work according to timezone.

And please suggest if there is any better way to handle timezones in this application ??

Thanks

Bessiebessy answered 7/5, 2009 at 5:17 Comment(1)
This question may be old and have an accepted answer, but it is quite broad and the answers only address one aspect of the problem. The answer is also incorrect, as the recommended solution doesn't cover daylight saving time, and uses TimeZoneInfo - which was not available in .NET 2.0 (which as a requirement of this question). I recommend this question be closed or deleted.Nablus
T
29

First thing is to make sure which time zone your data is in. I would recommend making sure that any DateTime that you store, is stored in UTC time (use the DateTime.ToUniversalTime() to get hold of it).

When you are to store a reminder for a user, you will need the current UTC time, add or remove the user's time zone difference, and convert that new time back to UTC; this is what you want to store in the DB.

Then, when you want to check for reminders to send, you simply need to look in the database for reminders to send out now, according to UTC time; essentially get all reminders that have a time stamp that is before DateTime.Now.ToUniversalTime().

Update with some implementation specifics: You can get a list of time zones from the TimeZoneInfo.GetSystemTimeZones() method; you can use those to show a list of time zones for the user. If you store the Id property from the selected time zone, you can create a TimeZoneInfo class instance from it, and calculate the UTC time for a given local date/time value:

TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("<the time zone id>");
// May 7, 08:04:00
DateTime userDateTime = new DateTime(2009, 5, 7, 8, 4, 0);
DateTime utcDateTime = userDateTime.Subtract(tzi.BaseUtcOffset);
Thomasenathomasin answered 7/5, 2009 at 5:24 Comment(9)
Ok, your idea sounds good, but I am feeling a problem there. You said "you will need the current UTC time, add or remove the user's time zone difference, and convert that new time back to UTC;" but now when I'll run my process which will send the reminders. I am located in Indian timezone (IST), so when should I run the reminders so that reminder will be sent to the users at correct time.Bessiebessy
Instead of storing in UTC, what I thought is: I'll have three columns in my reminder_schedule table which will be "Original_DateTime", "UTC_DateTime" and "IST_DateTime". Now Original_DateTime date time will store the exact date whatever user will set. UTC_DateTime will store the UTC DateTime on the basis of users timezone. and last "IST_DateTime" will store the datetime according to Indian Timezone by converting UTC_DateTime to Indian TimeZone. Then finally I'll run my reminders sending process accoridng to INdian tmezone and hence all reminder will be sent on exact time.Bessiebessy
I'm not sure that the idea I've given above is perfect but these are my thought, please tell me how should I proceed further ??Bessiebessy
As a minimum you need to store the time i a "server reference" time; I usually prefer UTC, but in your case I guess IST will work as well. If you use IST you will not tecnically need to store the UTC time (since you can easily get that one from the DateTime class. Same thing goes for the user's local time; technically you only need the time zone for the user; then you can go from IST to UTC to local time. Might be convenient to have it in the DB though.Aaronson
hmmm.. sounds good. one more thing @Fredrik you have written "DateTime utcDateTime = userDateTime.Subtract(tzi.BaseUtcOffset);" line in you code, but how will we know that we have to substract or add the timezone to convert it to UTC ??Bessiebessy
@Prashant: sorry for not getting back to you: the answer is that the BaseUtcOffset should always be subtracted from the local time, since it is signed; in time zones that are before UTC (such as IST), BaseTimeOffset returns a TimeSpan with positive value, but for time zones that are after UTC, such as US Eastern Standard Time, it returns a negative time span; so if UTC is 12:00 and you have a time zone with a -1 hour offset (so local time is 11:00) you will get 11:00 - (-1) hour => 11:00 + 1 hour => 12:00.Aaronson
A note here that you should use DateTime.UtcNow() rather than DateTime.Now().ToUniversalTime(), due to potential DST issues - see msdn.microsoft.com/en-us/library/ms973825.aspx#datetime_topic8Simms
@FredrikMörk - Two things. First, I see that the TimeZoneInfo class gives the DST offset in addition to the base UTC offset. So if I want to determine the full offset (utc adjustment + dst offset) for an arbitrary point in time (for example at the userDateTime you created), is the standard procedure to simply check if the userDateTime is within the DSTstart and End values that are part of the TimeZoneInfo's adjustment rules? Second, I am curious if your example above might result in an incorrect utcDateTime?Afloat
@Matt - This post may be old, but it is quite wrong. You should not subtract the base offset manually. You should instead use TimeZoneInfo.ConvertTime to go from a user's time to UTC. If you just want to know the offset of an arbitrary time, you would use GetUtcOffset. Also note that TimeZoneInfo wasn't available in .net 2.0 when this question was asked. And finally - you really should ask this sort of thing in a new question instead of a comment on an 5-yr old post.Nablus
S
19

I would recommend to always use UTC (GMT) time on the server side (in code-behind, database, etc), and convert time from UTC to local time for display purposes only. This means that all time manipulations - including saving time in database, performing calculations, etc - should be be done using UTC.

The problem is: how does your code-behind know what is the time zone of the client browser? Say the user enters some date/time value (such as 12/30/2009 14:30) in the form and submits it to the server. Assuming that the user submitted local time, how does the server know how to convert this value to UTC?

The application can ask the user to specify the time zone (and save it in a persistent cookie or database), but it requires and extra effort from the user, and your app would need to implement the logic and screens for this. It would be nicer if the app could determine client's time zone automatically.

I have addressed this issue with the help of JavaScript's getTimezoneOffset function, which is the only API that can tell the server about the time difference between local time on the client and GMT. Since this is a client-side API, I did the following: on the server side check for a custom session cookie holding the time offset value, and if it's not available, reload the page (only during GET, and not POST, calls) with some JavaScript logic added to generate the time offset and save it in the cookie. From the client-side this is almost transparent (once during session I reload a page on GET). Once I have the offset in the cookie, I apply it to the time management functions depending on direction of time conversion (UTC to local time, or local time to UTC).

This may sound a bit complicated, and it is, but after I wrote helper functions, integrating this feature in the site was a matter of making a single call in Page_Load (of pages that needed time conversion), and using time conversion routines when sending and retrieving time values to and from the browser. Here is an example of how it can be used:

using My.Utilities.Web;
...

// Derive the form class from BaseForm instead of Page.
public class WebForm1: BaseForm
{
...
private void Page_Load(object sender, System.EventArgs e)
{
  // If we only want to load the page to generate the time
  // zone offset cookie, we do not need to do anything else.
  if (InitializeLocalTime())
    return;

  // Assume that txtStartDate is a TextBox control.
  if (!IsPostback)
  {
     // To display a date-time value, convert it from GMT (UTC)
     // to local time.
     DateTime startDate = GetStartDateFromDB(...);
     txtStartDate.Text  = FormatLocalDate(startDate);
     ...
  }
  else
  {
     // To save a date-time value, convert it from local
     // time to GMT (UTC).
     DateTime tempDate  = DateTime.Parse(txtStartDate.Text);
     DateTime startDate = ConvertLocalTimeToUtc(tempDate);
     SaveStartDateInDB(startDate, ...);
     ...
  }
}
...
}

If you need more specifics, check out the It’s About Time: Localizing Time in ASP.NET Applications article (sorry, but I do not have a direct link to the article on the publisher's site, since asp.netPRO restricts access to paid subscribers only; there are links to PDF copies, though). I wish I could post the sample from the article, but I don't want to violate the copyright; however, here is a project to build a helper library that has all necessary functionality and documentation (just ignore the stuff you do not need).

UPDATE: The article has been posted online with sample project by the new publisher here.

Sterlingsterlitamak answered 7/5, 2009 at 7:54 Comment(3)
I disagree with the statement that your app should determine user's timezone automatically. Users can set any timezone/date/time they want in theirs operating system and bypass your business rules. Never trust user input.Laquitalar
I don't understand your point Bruno. First, there are no business rules associated with time zones. It's an issue of usability. We could've used GMT, but it's not convenient for users, so we use local time. If user changes the system clock (or whatever way they use to specify time zone), who cares? I don't see a problem here. I'm also not following a transition from your first statement to the second statement. Do you not like the "automatic" part, or do you not like the idea of using local time at all (if it's not done automatically, user must be able to change it somehow, right)?Sterlingsterlitamak
+1. @BrunoSalvino, how would you suggest to pass the timezone data? The other way I can think of doing this is providing an explicit user input. The latter is even less trustworthy than the automatic solution proposed here, as the user is more likely to make mistake. From user's perspective this will require timezone inputs, which is not user-friendly especially when the page does not even require a date-time information, but still has to persist the date and time of certain user interaction (like the time the user uploaded some file or did a financial transaction).Toll
R
3

The problem with all of the answers so far is that they do not take into account what Prashant is trying to achieve. If the user of his system on the day before daylight savings changes has an offset of +12 and sets a reminder for the next day, his offset when the reminder is supposed to be triggered will be +13 instead.

That is why you can only use current offset for something that is happening now. Although I do agree with everyone else that all times server-side (except possibly those used only for display) should be stored in UTC.

Renfro answered 12/5, 2009 at 2:26 Comment(0)
P
2

You may want to look at using the DateTimeOffset structure instead of the DateTime if you are on framework 2.0 or later.

The DateTimeOffset represents a point in time relative to UTC time, so it should be easier to work with in this case.

Paratroops answered 7/5, 2009 at 6:36 Comment(0)
M
1

There are 2 steps:

  • Detect different timezone at client side using Javascript:

    var dt = new Date();
    var diffInMinutes = -dt.getTimezoneOffset();
    
  • Then at server side, C# code to convert server time to client time based on detected timezone offset above:

------------------------;

string queryStr = Request.QueryString["diffInMinutes"];
int diffInMinutes = 0;
if (Int32.TryParse(queryStr, out diffInMinutes))
{
    clientTime = serverTime.ToUniversalTime().AddMinutes(diffInMinutes);
}
Mader answered 14/4, 2015 at 14:0 Comment(1)
Works for me. Good example. The problem is not storing the date as UTC as you showed there is already a "ToUniveralTime()" method to convert to UTC if needed. The real problem is getting the local timezone offset where the client is located. This example also works great for MVC where json objects get passed to controller.Kiss
B
0

Basically, all you need to do is add the offset (hours + minutes) to the local time that the user has entered. Adding in the offset basically gives you a DateTime in the UTC (basically GMT) timezone.

It's usually easiest to standardize all your times to UTC, so that your application logic doesn't have to deal with the offsets.

This page has a few good examples: http://msdn.microsoft.com/en-us/library/bb546099.aspx

Bukharin answered 7/5, 2009 at 5:25 Comment(0)
W
0

The issue is that the offset from UTC will vary at different times of the year -- each time zone has its own rules. (I learned this the hard way when developing meeting room scheduling app.)

Looks like there is built-in support here: http://msdn.microsoft.com/en-us/library/system.timezoneinfo.converttime.aspx

Haven't tried it myself but it seems to promise the correct conversion, accounting for daylight savings.

If not, here is a (pricey) commercial tool I have used: http://www.worldtimeserver.com/time_zone_guide/

Whitebait answered 12/5, 2009 at 5:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.