Displaying server date and server time on client - c#

I have a C# time application where the user can create a timer and start it, it will do something like that on the server:
(the dates are stored in CEST (the problem is summer and winter time))
// insert into db
var entry = new TimeEntry();
entry.Start = DateTime.Now; (04/03/2013 12:00)
_timeEntries.Add(entry); // => now it's in the db
if he stops it then it do something like that:
// update the entry
var entry = _timeEntries.Get(1) // 1 is the id from the created entry before
entry.End = DateTime.Now; (04/03/2013 12:37)
_timeEntrires.Update(entry); // => now it updates it in the db
Something like that will be shown for the user in the browser:
TimerId: 1
Start: 04/03/2013 12:00
Stop: 04/03/2013 12:25
Total (in min): 25
Now I think there will be some problems when the user changes his timezone.
What's the best way to solve this? Store the dates as UTC and then convert with JavaScript?
Thanks for your helps :-).

From : Determine a User's Timezone
You will have to pass the offset from the client to the server like this:
new Date().getTimezoneOffset()/60;
getTimezoneOffset() will subtract your time from GMT and return the
number of minutes. So if you live in GMT-8, it will return 480. To put
this into hours, divide by 60. Also, notice that the sign is the
opposite of what you need -- it's calculating GMT's offset from your
time zone, not your time zone's offset from GMT. To fix this, simply
multiply by -1.

Since these are event times, you should be working strictly with UTC, and taking that value from the server.
When the client application calls you with whatever you're using for start and stop input commands, use DateTime.UtcNow() to get the time and save that in the database.
You really don't want to use local time at all for the recording of these times. Otherwise, if the start or end fall within an ambiguous period (such as a DST/SummerTime "fall-back" transition), you would have no way of knowing if the event fell before or after the transition.
Context always matters with DateTime questions, and in the context of "event times" - your only reliable options are UTC DateTime, or DateTimeOffset. Local calendar times (client or server) are not good for specific event times. They are useful in other contexts though.
When you actually show the data back to your user, if you're just showing the duration between start and end, then you can simply calculate it from the UTC times you recorded. If you want to show the specific times back to the user, that's when you would convert the UTC time to whatever time zone the user wants to display it in. If you opt for DateTimeOffset, the advantage is that you don't have to convert at this stage.

Related

CRM 2011, Date in plugin and DST

I have one issue which I resolved by myself but yet need some confirming words whether I am 100% correct on my thought, just because there is not any documentation I found to prove myself correct.
My server is in DST time currently, CRM UI is also showing 1 hour up then data stored in db. that's fine.
When I calculate and store date with plugin, after my plugin update operation finishes, CRM platform deducts 1 hour from data I saved. I have read that when we do some operation via SDK related date time, CRM stores date time as it is. is it the case that when time is in DST, platform also get involves to deduct 1 hour by then ?
As a resolution, I have commented out my line of deducting 1 hour and letting CRM to do it now.
Am I correct on my understanding or it would be appreciable if some one can provide any documentation URL.
Any time you're working in the SDK, DateTimes are retrieved and stored as UTC. The CRM web platform will convert the UTC time into the user's time. You shouldn't need to be doing any Conversions of time, just using UTC.
Let's assume your local time is UTC -1 (with DST since UTC doesn't observe it). So if UTC is 14:00, your local time is 13:00.
Let's also assume your plugin in going to populate a date attribute on the entity that is for the current time, tomorrow. If your code looks like this:
entity.new_SomeDate = DateTime.Now.AddDays(1);
service.Update(entity);
Assuming DateTime.Now is 13:00, it'll store 13:00 in the database (as if it was UTC). Then when you go to look at the time value from the CRM website, since you're UTC - 1 it'll display 12:00, even though you wanted 13:00.
Now if your code looks like this:
entity.new_SomeDate = DateTime.UtcNow.AddDays(1);
service.Update(entity);
Assuming DateTime.Now is 13:00, it'll store 14:00 in the database since DateTime.UtcNow is 14:00. Then when you go to look at the time value from the CRM website, since you're UTC - 1 it'll display 13:00, since it'll take the UTC value - 1.
Now if your code looks like this:
entity.new_SomeDate = entity.new_UserEnteredDateFromCrm.AddDays(1);
service.Update(entity);
The new_UserEnteredDateFromCrm will already have been converted from the users' Time Zone to UTC and it'll work as expected.
This makes DateTimes that you would prefer to store as Dates very difficult though (birthdays anyone?) and you may have to think through it a little more in depth.
I've figured it out myself. that date has component in it which explores what kind of date time is it in. it can be either Utc, Local or Unspecified.
when you pass any date to CRM attribute via code. make sure that date time kind is Utc, otherwise CRM service internal operation will convert it to be into Utc.
In my case, I was stucked to this because when I read date from CRM, I had to set office start in that date. e.g. I needed to make 03/02/2014 12:00 to 03/02/2014 8:30 to make incoming date aligned with office start time. I was doing operation like,
DateTime InDate = Case.CreatedOn.Value;
DateTime Dt = new DateTime(InDate.Year,InDate.Month,InDate.Day,8,30,0)
Having InDate in Utc Time, I was creating new date object so it lost DateTime kind from Utc to Local ( having in DST it signifies to be 1 hour up)
to avoid, always set DateTime kind to be exactly as provided In date to new object. e.g. above operation can be done alternatively like.
DateTime InDate = Case.CreatedOn.Value;
DateTime Dt = new DateTime(InDate.Year,InDate.Month,InDate.Day,8,30,0)
Dt = DateTime.SpecifyKind(Dt,DateTimeKind.Utc)
Hope that Helps.

Comparing time regardless of date

I have an object that has properties currently as DateTime.
The object is marked as valid within a time frame. The default being 00:00:00 to 23:59:59
The user enters the value in the UI and the property is set via:
new DateTime(DateTime.Now.Year,
DateTime.Now.Month,
DateTime.Now.Day,
model.Hours,
model.Minutes,
model.Seconds)
This is then converted to UTC when it hits the database.
Today's date is 29th August 2013. If a colleague in India runs this program it will store the data in the database as 28th August 2013 18:30:00 as they are 5.5 hours ahead of UTC so 29th August 2013 00:00:00 becomes yesterday.
When the logic tries to determine if the object is valid the logic is:
if (DateTime.UtcNow.TimeOfDay > model.MyPropertyFromDB.TimeOfDay)
We are trying to determine if the current time is within a range of 00:00:00 and 23:59:59
This fails as 14:00 (current time) is not greater than 18:30
What would be the best approach to compare just times?
Would storing the values as DateTimeOffSet help, is using ToLocal() ok?
Other considerations are that a user in India is using the app which is hosted in the UK so it needs to be timezone aware.
Thanks
Like others, I'm still unclear on exactly what you are wanting. But clearly, you shouldn't do this:
new DateTime(DateTime.Now.Year,
DateTime.Now.Month,
DateTime.Now.Day,
model.Hours,
model.Minutes,
model.Seconds)
That would be much better as:
DateTime.Today.Add(new TimeSpan(model.Hours, model.Minutes, model.Seconds))
But why are you doing this to begin with? Either of these would give you back the local date. I assume this is going to run on a server, so do you really want the time zone of the server to influence this result? Probably not. Please read: The Case Against DateTime.Now.
If you wanted the UTC date, you could do this:
DateTime.UtcNow.Date.Add(new TimeSpan(model.Hours, model.Minutes, model.Seconds))
That would at least be universally the same regardless of your server's time zone. But still, I don't think this is what you are after.
What's not clear is why is the user only entering the time while you are assigning the current date. If the date is relevant, then shouldn't the user enter it and it would be part of your model?
If the date is not relevant, then why are you storing it? You can use a TimeSpan type for the time value internally. You didn't say what your database is, but let's just guess that it is SQL Server, in which case you could use the time type on the field in the table.
I suppose it's possible that the date is relevant, but you want to control it, while the user takes control of providing the time. If that's the case, then you must know the time zone of the user (or the time zone of whatever the context is if it's not the user). Assuming you had a Windows time zone identifier (see the timezone tag wiki), then you could do something like this:
var tz = TimeZoneInfo.FindSystemTimeZoneById(theTimeZoneId);
var local = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz);
var dt = local.Date.Add(new TimeSpan(model.Hours, model.Minutes, model.Seconds));
If you don't have the time zone information, then this wouldn't be possible to solve.
As general advice, you might want to try using Noda Time instead of the built-in stuff. It's much better at helping you figure out this sort of thing. From the main page:
Noda Time is an alternative date and time API for .NET. It helps you to think about your data more clearly, and express operations on that data more precisely.
That appears to be directly the problem you are having here. If you want to clarify some of the questions I asked, I'd be happy to edit my answer and show you exactly how to do this with Noda Time.
Why your question is confusing
We are trying to determine if the current time is within a range of 00:00:00 and 23:59:59
All times are within that range. Well, maybe a value like 23:59:59.1 would be outside of it, but you aren't collecting fractional seconds in your model, so that's irrelevant. But why would you need to validate that? Maybe you are just trying to avoid numbers that aren't valid times at all? Like 99:99:99?
This fails as 14:00 (current time) is not greater than 18:30
Wait - you didn't say anything about comparing one time greater than another. 14:00 and 18:30 are both still in the range you specified.
What would be the best approach to compare just times?
Hard to answer. Are they both UTC times? Is one UTC and one is local? Are they both local? Do you know the time zone of the local times? Are you prepared to deal with ambiguous or invalid local times do to daylight saving time transitions?
Would storing the values as DateTimeOffSet help?
Perhaps, but you haven't given me enough information. It would help only if the date portion is relevant and the you get the correct offsets.
is using ToLocal() ok?
I would argue that no, it's not ok. Local in this context will give you the time zone of the server, which you probably don't want to introduce into your business logic.
So if I understand this correctly you have a time saved in UTC in the database and you are trying to determine whether it falls within a particular time frame? I'm not sure if you want the time frame in local time or UTC so here are both:
DateTime dbTime = model.MyPropertyFromDB;
TimeSpan minTime = new TimeSpan(0, 0, 0);
TimeSpan maxTime = new TimeSpan(23, 59, 59);
if (dbTime.TimeOfDay > minTime && dbTime.TimeOfDay < maxTime)
{
//Within time range (UTC)
}
if (dbTime.ToLocalTime().TimeOfDay > minTime && dbTime.ToLocalTime().TimeOfDay < maxTime)
{
//Within time range (local)
}
Edit: If you want to compare Now to a start and end time from an object in database:
TimeSpan now = DateTime.UtcNow.TimeOfDay;
TimeSpan startDate = model.startDate.TimeOfDay;
TimeSpan endDate = model.endDate.TimeOfDay;
if (now > startDate && now < endDate)
{
//Within time range (UTC)
}
I would say that the methodology being used here is fundamentally flawed and that you need to take a different approach.
new DateTime(DateTime.Now.Year, // Server date
DateTime.Now.Month,
DateTime.Now.Day,
model.Hours, // Local time
model.Minutes,
model.Seconds)
I can't see a way of 'normalising' the input in this way, unless you have a way of reliably knowing exactly which timezone a user is in. Simply, there's no easy way to turn a date built in this way into UTC.
My first question to you is, how is the model being passed from client to server? If you're using javascript/ajax then the solution should be fairly straightforward to solve by constructing the datetime object on the client (which will include their timezone data) and then rely on the browser to convert it to UTC for transit.
If you are using Razor\MVC then you can achieve a similar thing with forms encoding, except that you will need to call ToUTC on the server as the browser won't automatically fix the date for you for this media format.
Both methods require that you build a full datetime object on the client and then submit it, rather than trying to build it from seconds, minutes, hours on the server. You don't need to expose all this to the client of course, as long as the datetime is fully formed at the point of submission.
Once you've got a nice UTC datetime, you can extract just the time if you don't need the rest of it.
Hope this helps.
Pete

Asp.Net, SQL and TimeZones

It's been asked, but I am battling to grasp the concept of how to handle timezones in a web app. I have a system that tracks the progress of projects. I have a ProjectStartDate DATE in my SQL Server database. (There's a few more fields and tables, but lets focus on one).
The server is located somewhere in the United States. I live in Australia.
Calling SELECT GETDATE() returns "2013-08-11 14:40:50.630"
My system clock shows "2013-08-12 07:40"
In my database, I have 'CreateDateTime' columns on all tables. When I store that, within my c# code, I use CreateDate = DateTime.UtcNow
I use that, as I heard it was better to use UTC.
But, when a user is presented with a calendar control, and they select a Start Date for a project, I store what ever the user selected. No conversion... And as I said, the StartDate is a DATE type in the database.
The problem is, if a project started today - my front end says that the current project is No Started, because the server is still in Yesterday.
I think the dates should be stored as I am storing them. But maybe I need to somehow get the users timezone, and apply that on the UI level?
Problems I see are:
I don't know the users timezone. Add something to allow them to select it?
The status of the project maybe determined in a stored procedure, so when can I apply the conversion? In the proc, it might do a check, and return a VARCHAR stating "Not Started" if the StartDate <= DateTime.Now?
I use EntityFramework and Linq to get data most of the time. I need a strategy for inserting and retrieving data, both from a SQL sense, and a .Net sense.
I have added code to get the user to select their timezone based on this:
public List<TimeZoneDto> GetTimeZones()
{
var zones = TimeZoneInfo.GetSystemTimeZones();
var result = zones.Select(tz => new TimeZoneDto
{
Id = tz.Id,
Description = tz.DisplayName
}).ToList();
return result;
}
That is then persisted in their profile.
All dates are being stored as UTC, as advised in the answers below.
I'm still confused how to handle the dates when they go to and from the database, to the client. Here is an example of how I am storing a record:
public int SaveNonAvailibility(PersonNonAvailibilityDto n)
{
person_non_availibility o;
if (n.Id == 0)
{
o = new person_non_availibility
{
PersonId = n.PersonId,
Reason = n.Reason,
StartDate = n.StartDate,
EndDate = n.EndDate,
NonAvailibilityTypeId = n.NonAvailibilityTypeId,
CreateUser = _userId,
CreateDate = DateTime.UtcNow
};
_context.person_non_availibility.Add(o);
}
else
{
o = (from c in _context.person_non_availibility where c.Id == n.Id select c).FirstOrDefault();
o.StartDate = n.StartDate;
o.EndDate = n.EndDate;
o.Reason = n.Reason;
o.NonAvailibilityTypeId = n.NonAvailibilityTypeId;
o.LastUpdateDate = DateTime.UtcNow;
o.LastUpdateUser = _userId;
o.Deleted = n.Deleted ? DateTime.UtcNow : (DateTime?)null;
}
_context.SaveChanges();
return o.Id;
}
This method basically saves when a person is not available for work. Note the way I store the LastUpdateDate. Also, the 'Start' and 'End' dates. Those dates are more 'Business' dates.
On selection, and then date checking, is where I have issues. In this example, I am getting a persons rate of charge, based on NOW.
public decimal CurrentRate
{
get
{
if (ResourceCosts != null)
{
var i = ResourceCosts.FirstOrDefault(t => DateTime.UtcNow <= (t.EndDate.HasValue ? t.EndDate.Value : DateTime.UtcNow) && DateTime.UtcNow >= t.StartDate);
if (i != null) return i.Cost;
return 0;
}
return 0;
}
}
So, what I am wanting to do here, is, based on the current date, I want to see his rate (As his charge our rate maybe be $100 from the 1st Jan, to the 15th Jan, and then $110 from the 16th until the 31st of Jan. So, I look for the rate applicable today (if any). This doesn't work across time zones, and it's maybe here where I need to do some date manipulation based on the 'DateTime.UTCNow'?
Note, I now know the users timezone based on the code above where I am storing his timezone. I can use that somehow here? Maybe, when the user logs in, grab the date info from his profile (Zimezone info), and then have a global shared function, that returns the users datetime, based on adding or removing hours from the UTC Date... and using that where ever I am doing DateTime.UTCNow?
Hope someone can guide me.
You may find that there is not one single "right" way to handle all of this. There are multiple approaches to the several different problems you describe in your question. I will attempt to clarify a few points.
First, don't ever attempt to think about local time on a server. Your code and data should not have to change based on where you deploy it. You said your server was in the USA, but there are multiple time zones to consider, and many servers will have their time zone set to UTC regardless of their physical location.
You should avoid GETDATE() or SYSDATETIME() in SQL Server. If you need a current timestamp in SQL, use GETUTCDATE() or SYSUTCDATETIME(). If for some reason the server's time zone is important to you, use SYSDATETIMEOFFSET() instead.
Likewise, avoid using DateTime.Now in .Net from any server-side code. Use DateTime.UtcNow or DateTimeOffset.UtcNow for a UTC timestamp, or use DateTimeOffset.Now if for some reason the server's time zone is important to you.
You can read more about this in my blog post: The Case Against DateTime.Now
Next, let's talk about the data type you're using. The date type in SQL Server stores just a date. That's it. No time, no offset, and no time zone. An example would be 2013-08-11. You should use it when you really mean a whole calendar date. There is no worldwide uniform context of "today". Instead, everyone has their own meaning based on their time zone. Also, not every calendar day is 24 hours in length. A day could be 23, 23.5, 24, 24.5 or 25 hours long, depending on how daylight saving time is applied in the particular time zone, and if you are evaluating the day of a DST transition.
In .Net - there is no Date type, so a SQL date is converted to a DateTime with the time set to midnight (00:00:00), and the kind set to Unspecified. But don't fool yourself - the time isn't suddenly midnight, we are just filling in zeros for the time. This can lead to a lot of error and confusion. (If you want to avoid that, you can try Noda Time, which has a LocalDate type for this purpose.)
What you really need to be thinking about, and haven't defined in your question, is this:
What exact moment does a project start?
Right now you are just saying 2013-08-11, which doesn't refer to a specific moment in time. Do you mean the beginning of that day in a particular time zone? Or do you mean the beginning of that day according to the user's time zone? Those might not be the same thing. You can't compare to anyone's "now" (utc, local, or otherwise) unless you know what moment in time you are talking about.
If the project starts at an exact moment in time worldwide, then the easiest thing would be to store a datetime (or datetime2) type that contains that precise time in UTC. So you might say that a project starts at 2013-08-10T14:00:00Z - which would be exactly midnight on August 11th in Sydney, Australia. In .Net, you would use a DateTime type with the .Kind set to Utc.
Another way you could represent this is by storing a datetimeoffset type that has a value of 2013-08-11T00:00:00+10:00 - which is the same moment in time, but uses the offset to give you a value that is pre-converted. (Sydney is at UTC+10 on that date). You would use the DateTimeOffset type to work with this in .Net.
But if the project starts at different times depending on the user, then it's not really an exact moment in time. It's more of a "floating" start. If users from different places around the world are assigned to the same project, then some users could be starting before others. If that's your intention, then you can just use the date type if all projects start at midnight, or you can use a datetime or (datetime2) type if projects might start at different times. In your .Net code, you would use a DateTime type with the .Kind set to Unspecified.
With regard to getting the user's time zone, the best thing you could do would be to ask them. Despite the common misconception - you can't just get it from the browser. All you could tell from the browser is what their current offset is. (Read the "TimeZone != Offset" section of the timezone tag wiki).
When asking the user for their time zone, if you decide to use Windows time zones you can produce a dropdown list from the TimeZoneInfo.GetSystemTimeZones method. The .Id is the key you store in your database, and you show the .DisplayName to the user. Later you can use the TimeZoneInfo.FindSystemTimeZoneById method to get a TimeZoneInfo object that you can use for conversions.
If you wanted to be more precise, you could use IANA time zones instead of the Windows time zones. For that, I recommend using a map-based timezone picker control, such as this one. You might also use jsTimeZoneDetect to guess a default value for your control. On the server you would use Noda Time to perform time zone conversions.
There is an approach that doesn't require time zone conversions. Basically, you do everything in UTC. That includes transmitting the time to the browser in UTC. You can then use JavaScript to get the user's current time in UTC and compare against that.
You can use various functions of the JavaScript Date class to do this if you wish. But you may find it easier to work with a library such as moment.js.
While this approach is viable for many things, security is not one of them. Your user can easily change the clock of their computer to work around this.
Another approach would be to compare server-side against UTC. If you have the exact UTC starting time in your database, then you can just check DateTime.UtcNow in your .Net code and use that to decide what to do. You won't need the user's time zone to make this comparison, but you will need it if you want to show them what that means in their local time.
I hope this clears up the confusion and didn't make it worse! :) If you have additional concerns, please edit your question or ask in comments.
UPDATE
In response to your updated question, I suggest you try the following:
var timeZoneId = "Eastern Standard Time"; // from your user's selection
var timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
var nowInTimeZone = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, timeZone);
var todayInTimeZone = nowInTimeZone.Date;
var i = ResourceCosts.FirstOrDefault(t => t.StartDate <= todayInTimeZone &&
(!t.EndDate.HasValue || t.EndDate >= todayInTimeZone));
Of course this means that in your StartDate and EndDate fields, you are not storing these as UTC - but rather as the "business dates" that are relevant to the user. These only line up to a specific moment in time when you apply a time zone, so the same UTC timestamp could fall on different dates depending on what the user's time zone is.
Also, you are using fully inclusive ranges, which is usually OK for these kind calendar date ranges. But make sure you realize that there could be overlap. So if you have 2013-01-01 - 2013-02-01 and 2013-02-01 - 2013-03-01, then there is that one day 2013-02-01 that is in both ranges.
A common way around this problem is to use half-open intervals, [start,end). In other words, start <= now && end > now. But this is more common when using a full date and time instead of just a date. You might not need to do this, but you should at least think about it for your particular scenario.
You can get a user's timezone from the browser. This will only get the value that they've set on their computer. So they can manipulate this for their advantage. For example, if you were limiting access until midnight local time, they could change their time to get "early" access. So keep that in mind.
Next what you'll want to do is store all times in UTC in the database. Instead of using GetDate() you can use GetUTCDate().
Next, you can either store their timezone, or their offset from UTC in the database. You get into some danger just storing the UTC offset, because timezones that observe daylights savings will have different offsets at different time of the year.
But with these two pieces of information, you can calculate the "local" time of the user. You can either add the number of hours to your stored date, then compare that to the local server time (also adjusted from UTC.)
Of if you don't really care that much, you can store the time in UTC in the database, then simply compare DateTime.UTCNow on the server to determine if they should have access or not.

How to send reminders at correct time for users in different timezones?

I have never created a reminder application. Here is how I see it. Please let me know if I'm on the right way.
So I have users from different timezones.
ID DateTimeUTC TimeZoneID
1 2011-07-12 02:15:15.000 TimeZneID1
2 2011-07-13 16:00:00.000 TimeZneID2
3 2013-11-03 17:00:00.000 TimeZneID3
4 2011-08-22 03:00:00.000 TimeZneID4
5 2011-07-16 22:00:00.000 TimeZneID5
Create a scheduled process to run every 15 mins and do the steps below:
Get records;
The second is to convert DateTimeUTC to Time for the right timezone
Compare if it's match
a. Send Reminder
var tzi = TimeZoneInfo.FindSystemTimeZoneById(TimeZneID1);
var local = TimeZoneInfo.ConvertTimeFromUtc(DateTimeUTC, tzi);
var timeNow = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Now, tzi);
if(local == timeNow)
SendReminder();
Is it efficient way? is it the right way?
If date/time values are already in UTC in the database, you don't need to perform any conversions, surely... you just need to see whether the current UTC instant is a match, and if so, send the reminder.
That's assuming you really mean it's UTC in the database, i.e. you've converted it from the user's local time when they entered the reminder (assuming they did so to start with).
Typically, when dealing with dates like this, you would do all of your calculations in UTC and only switch to local time when it's time (no pun intended) to display the results. I assume from your question that this is a centralized database that's managing all the tasks, and you just need them to run at the correct local time?
if ( dateTimeUtc == DateTime.UtcNow )
{
// If your reminder needs to display the local time, pass it in:
var tzi = TimeZoneInfo.FindSystemTimeZoneById(TimeZneID1);
SendReminder(TimeZoneInfo.ConvertFromUtc(DateTime.UtcNow, tzi));
}
Note that DateTime.Now is in local time; you want DateTime.UtcNow for consistancy across time zones.
Another thing to be aware of is you are only running your task scheduler every 15 minutes, so the odds of times like 02:15:15 matching exactly are slim. What you would typically want to do is check for any reminder times that came up since the last run:
var currentRun = DateTime.UtcNow;
foreach ( dateTimeUtc in GetReminderDateTimes() )
{
if ( dateTimeUtc > lastRun && dateTimeUtc <= currentRun )
{
}
}
lastRun = currentRun;
In my opinion you might be over-complicating it. Since you are storing things in UTC, have the reminders in UTC, and match on UTC. Then just associate the reminders with the users that you want to remind.
I want to do similar stuff and have been debating what would be an appropriate approach. Essentially, I am writing an application which would send a message for a particular country at midnight (localtime). I have about 100 such countries and I have to also be mindful of daylight savings. I can only think of 2 ways to do this,
1. Create a corresponding thread for each country and which wakes up every hour or so and checks if it is midnight(local time) and send out the message. So essentially, I will be creating 100 threads doing nothing most of the time.
2. In this approach, there will be only one timer which checks every minute or 30 secs the local time for 100 countries and send message. There will need to be some extra logic as there will never be an exact midnight match.
Not sure, if there is any better way to tackle above situation. It would be great if I can get some ideas/suggestions here.
Thanks,
SP.

How to work with time zones in ASP.NET?

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
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);
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.
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.
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.
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);
}
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
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/

Categories

Resources