This question already has answers here:
Date Conversion issue from webservice
(2 answers)
Closed 8 years ago.
I'm getting a following string from a web service 2013-10-15T12:54:18+01:00. This date is in the summer DST and my .NET code (web service proxy, I presume) automatically adds one hour to it. The same is not the case if the returned value falls withing the winter DST. The time returned (12:54:18) is what I want to display, I don't want any sort of recalculation to be done.
I'm using TimeSpan DateTime.TimeOfDay to show the time.
What can I do to make it happen?
I am trying to put the pieces together from your question and the additional comments. So far this is my analysis:
On the wire you see two different date and time strings:
Without daylight savings (winter) the string is 2013-12-30T12:54:18
With daylight savings (summer) the string is 2013-10-15T12:54:18+01:00
This is a sign that the web service is using GMT Standard Time as the time zone.
You want to extract the GMT timestamp from the date and time string.
However, between you and the web service there is some unspecified web service proxy (I assume some kind of .NET framework?) and in your code you only have access to a DateTime and furthermore your code is executing in the Central Europe Standard Time time zone which basically is one hour ahead of GMT both during summer and winter if we disregard the short time where there is a transition to and from daylight savings.
I hope I am correct so far.
You can convert the incoming DateTime to GMT using this code:
// Framework code creates the DateTime.
var sourceDateTime = DateTime.Parse("2013-10-15T12:54:18+01:00");
// Application code can further process the DateTime.
var destinationTimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
var destinationDateTime = TimeZoneInfo.ConvertTime(sourceDateTime, destinationTimeZoneInfo);
This example gives the correct answer during daylight savings. The incoming date and time string contains an offset and is correctly parsed into a local time zone (CET). The TimeZoneInfo.ConvertTime assumes that the source DateTime is in the local time zone and the result is correct.
However, the code fails during winter where there is no daylight savings:
var sourceDateTime = DateTime.Parse("2013-12-30T12:54:18");
Notice that the date and time string no longer contains a time zone offset. The offset is +00:00 but for some reason it is missing from the string. This means that the source DateTime is assumed to be in the local time zone (CET) and not converted from the actual time zone (GMT). This is the source of your problem if my analysis is correct.
Another way to explain it:
| Server (GMT) | Framework (CET) | My code
-------+---------------+---------------------------+----------------------------
Summer | +01:00 suffix | GMT -> CET adds 1 hour | CET -> GMT subtracts 1 hour
-------+---------------+---------------------------+----------------------------
Winter | No suffix | Assumed to be CET | CET -> GMT subtracts 1 hour
There is no easy fix for this problem. If you could persuade the web service to provide the correct offset even when it is +00:00 my code above would work both summer and winter. Even better, only use UTC and only convert to a local time when the end-user gets involved. But I guess that you have no control over the web service?
One option would be to execute your code in the same time zone as the server (e.g. GMT). Then you should be able to use the timestamp directly without any conversion.
Another more ugly option is to determine if the web service is outside daylight savings and then adjust the time accordingly:
var destinationTimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
if (!destinationTimeZoneInfo.IsDaylightSavingTime(sourceDateTime)) {
var sourceDateTimeOffset = new DateTimeOffset(sourceDateTime, destinationTimeZoneInfo.BaseUtcOffset);
sourceDateTime = sourceDateTimeOffset.UtcDateTime;
}
var destinationDateTime = TimeZoneInfo.ConvertTime(sourceDateTime, destinationTimeZoneInfo);
I have tried to keep the code as general as possible but actually it is only trying to fix the situation where +00:00 is missing and in that case destinationTimeZoneInfo.BaseUtcOffset is precisely 0 so it might be slightly overkill to do it like this.
More importantly, I am not sure that this code provides the correct results during the time when there is a daylight savings transition. Even though I believe that GMT and CET transitions at the same date CET is still one hour ahead of GMT. You really have to create some unit tests to make sure you get the desired result.
A DateTime object does not contain offset in this kind of example. It only holds a kind which could be i.e.: local offset (offset of the runtime environment) or UTC. Do it like so, since DateTimeOffset is what you want:
DateTimeOffset test = DateTimeOffset.Parse("2013-10-15T12:54:18+01:00");
Console.WriteLine(test.DateTime.TimeOfDay); // UTC
Console.WriteLine(test.LocalDateTime.TimeOfDay); // Localized with the offset parsed
Will output
12:54:18
11:54:18
You might want to check this MSDN article which describes the transitions that your TimeOfDay is currently returning.
By examining that article, you can find the way on how to correct it. TimeZoneInfo is the class that you'll need.
Edit, the work that Jon Skeet provided might help as well, look at the Noda Time blogspot for more info!
Related
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?
For strings such as 2012-09-19 01:27:30.000, DateTime.Parse cannot tell what time zone the date and time are from.
DateTime has a Kind property, which can have one of three time zone options:
Unspecified
Local
Utc
NOTE If you are wishing to represent a date/time other than UTC or your local time zone, then you should use DateTimeOffset.
So for the code in your question:
DateTime convertedDate = DateTime.Parse(dateStr);
var kind = convertedDate.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 = convertedDate.Kind; // will equal DateTimeKind.Utc
Now, once the system knows its in UTC time, you can just call ToLocalTime:
DateTime dt = convertedDate.ToLocalTime();
This will give you the result you require.
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.
// Coordinated Universal Time string from
// DateTime.Now.ToUniversalTime().ToString("u");
string date = "2009-02-25 16:13:00Z";
// Local .NET timeZone.
DateTime localDateTime = DateTime.Parse(date);
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);
TimeZone.CurrentTimeZone.ToLocalTime(date);
DateTime objects have the Kind of Unspecified by default, which for the purposes of ToLocalTime is assumed to be UTC.
To get the local time of an Unspecified DateTime object, you therefore just need to do this:
convertedDate.ToLocalTime();
The step of changing the Kind of the DateTime from Unspecified to UTC is unnecessary. Unspecified is assumed to be UTC for the purposes of ToLocalTime: http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx
I know this is an older question, but I ran into a similar situation, and I wanted to share what I had found for future searchers, possibly including myself :).
DateTime.Parse() can be tricky -- see here for example.
If the DateTime is coming from a Web service or some other source with a known format, you might want to consider something like
DateTime.ParseExact(dateString,
"MM/dd/yyyy HH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)
or, even better,
DateTime.TryParseExact(...)
The AssumeUniversal flag tells the parser that the date/time is already UTC; the combination of AssumeUniversal and AdjustToUniversal tells it not to convert the result to "local" time, which it will try to do by default. (I personally try to deal exclusively with UTC in the business / application / service layer(s) anyway. But bypassing the conversion to local time also speeds things up -- by 50% or more in my tests, see below.)
Here's what we were doing before:
DateTime.Parse(dateString, new CultureInfo("en-US"))
We had profiled the app and found that the DateTime.Parse represented a significant percentage of CPU usage. (Incidentally, the CultureInfo constructor was not a significant contributor to CPU usage.)
So I set up a console app to parse a date/time string 10000 times in a variety of ways. Bottom line:
Parse() 10 sec
ParseExact() (converting to local) 20-45 ms
ParseExact() (not converting to local) 10-15 ms
... and yes, the results for Parse() are in seconds, whereas the others are in milliseconds.
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.
#TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)
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.
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
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
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
This code block uses universal time to convert current DateTime object then converts it back to local DateTime. Works perfect for me I hope it helps!
CreatedDate.ToUniversalTime().ToLocalTime();
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.
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
I've string (variable is fileDate) with Date values in the following format:
2/12/2011 11:58 AM
Now I want to convert this to a date and then to UTC time based as I've problems in comparing dates in different machines, so *I always want to convert all strings (which are getting compared) to Utc_date values.*
I tried this code below but it did not work as I'm not able to convert the above string to Datetime based (as it does not have seconds).
DateTime date = Convert.ToDateTime(fileDate);
date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
fileDate = date.ToString("MM/dd/yyyy hh:mm tt");
Above did not work showing FormatException.
Can you pl help?
To start with, I'd suggest using DateTime.ParseExact or TryParseExact - it's not clear to me whether your sample is meant to be December 2nd or February 12th. Specifying the format may well remove your FormatException.
The next problem is working out which time zone you want to convert it with - are you saying that 11:58 is a local time in some time zone, or it's already a UTC time?
If it's a local time in the time zone of the code which is running this, you can use DateTimeStyles.AssumeLocal | DateTimeStyles.AdjustToUniversal to do it as part of parsing.
If it's already a universal time, use DateTimeStyles.AssumeUniversal
If it's a local time in a different time zone, you'll need to use TimeZoneInfo to perform the conversion.
Also, if it's a local time you'll need to consider two corner cases (assuming you're using a time zone which observes daylight saving time):
A local time may be skipped due to DST transitions, when the clocks go forward. So if the clocks skip from 1am to 2am, then 1:30am doesn't exist at all.
A local time may be ambiguous due to DST transitions, when the clocks go back. So if the clocks go back from 2am to 1am, then 1:30am occurs twice at different UTC times - which occurrence are you interested in?
You should decide how you want to handle these cases, and make sure they're covered in your unit tests.
Another option is to use my date and time library, Noda Time, which separates the concepts of "local date/time" and "date/time in a particular time zone" (and others) more explicitly.
you should be using DateTime.ParseExact to get the value into a proper DateTime instance, and then you can use .ToUniversalTime() to get the UTC time (this would be with respect to the difference of time as in your server machine)
you can use :
DateTime.Now.ToUniversalTime();
i don't mean to say to you should use "DateTime.Now" but you get the point that as a part of the DateTime object you have a method to transform it to Universal time
http://msdn.microsoft.com/en-us/library/system.datetime.touniversaltime.aspx
I have a question about DateTimeOffset and daylight savings time. To explain my question lets assume that right now the date and time is:
11/6/2010 10:15:00 AM
If I run this code:
DateTimeOffset myTime = DateTimeOffset.Now;
Console.WriteLine("Local time: " + myTime.ToLocalTime().DateTime);
Then I get this result:
Local time: 11/6/2010 10:15:00 AM
Meaning that the event happened at 10:15 in the morning (my time zone is Mountain Daylight Time (-6 offset)).
So, then I save this DateTimeOffset to my SQL Server 2008 db (as a DateTimeOffset). The next day I want to display it to the user. But now daylight savings has expired.
If I run the above WriteLine with the saved off value (from the previous day) what will display?
The offset stored in the database is -6. But now that daylight savings is over, the current offset is -7. So as I understand the documentation, it will first convert my time to UTC time (so it takes 10:15 AM and adds 6 hours (4:15 pm). It will then subtract the current offset of the local time (4:15 pm - 7 = 9:15 AM).
So if I my math is right, now when I display my event, it will show that it occurred at 9:15 AM rather than 10:15 AM.
This is not good. I want to store time zone information, but I need my times to stay static in the same time zone. (Meaning that if the event happened at 10:15 AM in Utah, then the next time I look at it (in Utah), I need to see that it was at 10:15 AM, regardless if the daylight savings time change has happened.
I can't think I am the first one to have this issue. What do others do to fix this? (Or do I have the facts wrong?)
No, it won't add the current offset - it'll add the offset at that date which is still -6. So it should still display 10:15AM, because it knows the date involved, and thus the time zone rules in force on that date.
You may well want to store a simple UTC time and the time zone identifier separately, by the way. If you're storing a time zone, then using DateTimeOffset won't be particularly helpful over just a UTC date/time. (On the other hand, it's clearer that it does represent an instant in time - DateTime is a horribly confused type which doesn't let you easily express what you're trying to represent.)
Of course I'd personally encourage you to look at Noda Time which in my very biased opinion is a rather clearer date/time API than the built-in one... but which isn't quite ready for production use. (We're getting there though...)