Reputation: 1924
I've been given a wsdl and I have to create a web service following its specs; I'm using visual studio 2010. Among the others there is also the definition of this complex type:
<xsd:complexType name="Person">
<xsd:sequence>
<xsd:element name="surname" type="xsd:string"/>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="birthDate" nillable="true" type="xsd:dateTime"/>
</xsd:sequence>
</xsd:complexType>
Using VS I got the following cs (I don't recall exaclty how I did but I followed instructions found on the web):
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://XXX/Submitter/")]
public partial class Person {
private string surnameField;
private string nameField;
private System.Nullable<System.DateTime> birthDateField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string surname {
get {
return this.surnameField;
}
set {
this.surnameField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string name {
get {
return this.nameField;
}
set {
this.nameField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true)]
public System.Nullable<System.DateTime> birthDate {
get {
return this.birthDateField;
}
set {
this.birthDateField = value;
}
}
And everything is correct: it compiles, runs and gives expected result; the only problem is that the other party that gave me the wsdl, when calling my web service, expects to get the birthdate field as
2013-02-15T17:28:00+01:00
with the time zone information, while the result they receive is like
2015-11-17T18:30:11
without timezone.
My problem is that I have a DateTime?
type and that is the one I pass to my object instantiated from the class; should I override the serialization or there is another most common solution?
Thanks
Upvotes: 5
Views: 4186
Reputation: 241475
Unfortunately, there's a significant mismatch between XSD and what .NET supports in SOAP web services.
.NET only has two types for this sort of information - DateTime
and DateTimeOffset
. Technically, it would be best if xs:datetime
mapped to DateTimeOffset
, but that's not how it works. Instead xs:datetime
is mapped to DateTime
, and relies heavily on the Kind
property of the value to determine how it's serialized.
When the DateTime.Kind
is DateTimeKind.Unspecified
, no offset information is passed.
When the DateTime.Kind
is DateTimeKind.Utc
, then Z
is passed, which is the same as +00:00
.
When the DateTime.Kind
is DateTimeKind.Local
, then the local offset corresponding to the given date is passed.
You can apply a kind by using DateTime.SpecifyKind
, or if you want to convert the value in the process, you can use .ToUniversalTime()
or .ToLocalTime()
. Or, if you know the values belong to a specific time zone, you can use the conversion methods from the TimeZoneInfo
object. As long as the resulting kind is anything other than Unspecified
it will include an offset.
Unfortunately, there is no option to specify an arbitrary offset. That would require the DateTimeOffset
type, which isn't supported in SOAP services.
Also, consider that really the xs:datetime
type is the wrong type for a birth date. It should be using xs:date
, which is just a date, such as "2013-02-15"
. There's currently no type that supports this in .NET, though there is one coming soon, as Date
in the System.Time package in corefxlab package. This will be useful for scenarios like these, but it remains to be seen if we can go back and fix the SOAP serializers to use them.
Really, the best idea is to not use XML and SOAP. Design your web services around JSON and REST instead. There you can have full control over things like this.
Upvotes: 3
Reputation: 2565
You might want to take a look at Jon Skeet's answer. He suggests a structure like this:
public struct DateTimeWithZone
{
private readonly DateTime utcDateTime;
private readonly TimeZoneInfo timeZone;
public DateTimeWithZone(DateTime dateTime, TimeZoneInfo timeZone)
{
utcDateTime = TimeZoneInfo.ConvertTimeToUtc(dateTime, timeZone);
this.timeZone = timeZone;
}
public DateTime UniversalTime { get { return utcDateTime; } }
public TimeZoneInfo TimeZone { get { return timeZone; } }
public DateTime LocalTime
{
get
{
return TimeZoneInfo.ConvertTime(utcDateTime, timeZone);
}
}
}
Upvotes: 0