Reputation: 25278
I have an Entity with the following property:
[ScriptIgnore]
public DateTime Date
{
get { return new DateTime(this.Ticks); }
set { this.Ticks = value.Ticks; }
}
It sets the field Ticks, which is how the DateTime is stored in the database.
The problem is, I see XML come in with a Date field formatted like this:
<Date>2011-04-08T12:29:00.000Z</Date>
Which is a perfectly fine string representation of a DateTime. And in testing, this string parses to the DateTime you would expect it to.
But the date in the system gets set to 2011-04-10 12:29, or exactly two days into the future.
This happens inconsistenly, and unpredictably. I can makes hundreds of submissions, and all of them are correct. But when the client submits them in the wee hours of the morning, a handful of them end up with dates two days into the future. This leads me to several, currently unanswerable, questions:
What constructor or parsing method is used when the XML is deserialized into a DateTime?
The client is in Central time, our server is on Eastern time,(even though all times are always converted to GMT) does this have any impact?
What clues does the fact that most of the errors occur in the early morning hours, but cannot be reproduced the rest of the day, give me, or is this just a strange coincidence?
UPDATE:
After reading the following blog post: http://blogs.msdn.com/b/bclteam/archive/2005/03/07/387677.aspx It appears that .net DateTime does indeed have issues with deserialization. And it does indeed use the local server time, even when it shouldn't. This doesn't explain ALL my issues, but it does lead me to the decision to stop using the DateTime type in XML services, and instead have the client submit datetimes as ticks instead. As long as .Net doesn't fail at parsing longs, we should be ok.
UPDATE 2:
The client was against submitting datetimes as Ticks, as each OS has a different definition of what Ticks are, (client is on Java, not sure what OS) So we decided to use strings. The client did not change their submitted XML at all, but we would be able to manually parse the string to a datetime instead of letting the deserializer do it.
I have updated the Property like so:
[ScriptIgnore]
public string Date
{
get { return new DateTime(this.Ticks).ToString(); }
set
{
if (string.IsNullOrEmpty(value))
{
this.Ticks = 0;
}
else
{
this.Ticks = DateTime.Parse(value).ToUniversalTime().Ticks;
}
}
}
And the problem still exists, at 1AM this morning the client imported approximately 20 records, and only 2 of them had the date mysteriously moved forward. After looking at the raw XML of both the successfully parsed date and those that weren't, I see no difference in the format at all.
Any help at tis point greatly appreciated.
Upvotes: 1
Views: 1102
Reputation: 139256
You can fix these issues by stating that all transported dates (moved between remote systems) are in fact UTC, for example:
public DateTime StartDateUtc
{
...
}
public DateTime EndDateUtc
{
...
}
And you leave the responsability of conversion to and from local time only to user interfaces code (or the highest layers of your application).
Note you can't really enforce this technically, only by convention / documentation, and making sure properties and methods are semantically marked "as UTC". A bad programmer will always be able to pass a local date to these properties:
StartDateUtc = DateTime.UtcNow; // this is correct
StartDateUtc = DateTime.Now; // this is wrong
Upvotes: 1
Reputation: 2526
That's definitely a weird case. A couple of things can alter the result of the parse.
My suggestions to try:
On your setter try this:
if (string.IsNullOrEmpty(value))
{
this.Ticks = 0;
}
else
{
CultureInfo provider = CultureInfo.InvariantCulture;
var dateTime = DateTime.ParseExact(value, "yyyy-MM-dd hh:mm:ss", provider);
this.Ticks = dateTime.ToUniversalTime().Ticks;
}
Good luck!
Edit 1
Something else came to my mind: ThreadSafety. If you are accessing the reflected object of Ticks (in your setter you do this.Ticks) from multiple threads you should make it an atomic operation.
//same setter code except last line...
var oldTicks = this.Ticks;
var newTicks = dateTime.ToUniversalTime().Ticks;
while(oldTicks != Interlocked.Exchange(ref this.Ticks, newTicks))
{ //your break code here }
Sorta :)
Let me know if any of this helped to solve the mystery.
Upvotes: 2
Reputation: 46394
I haven't seen issues such as this, but did encounter problems where timezone information is lost from a .NET 2.0 SOAP webservice when the WebService method returns a DateTime
rather than an object which contains DateTime
.
From that experience I learned that you should quickly focus on ensuring you can isolate and reproduce the problem. I would suggest you build a test-harness.
Using a sample DateTime value which you know has failed in your full system, emulate a client sending infinite requests. Run the service test-harness in Debug mode with a breakpoint to pause execution the moment it receives a value different from what was inspected. Using a tool such as Fiddler to also capture the Client traffic can provide additional insight.
If that fails to turn up the issue, then you should re-evaluate the evidence you have and again repeat the process as the next candidate is identified. Don't be afraid to retry something you believe you've tested or looked through before, the devil is nearly always in the details with these.
One other thought -- is it possible that this could be a concurrency/multithreading issue? If you have hard evidence that a particular value was submitted and a different value was received, then have you looked to ensure the received value was sent by a different client--or could be set from a different location within the system?
Upvotes: 2