Simone
Simone

Reputation: 1924

Webservice - How to pass timezone info in a datetime element

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

Answers (2)

Matt Johnson-Pint
Matt Johnson-Pint

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

Amadeus Sanchez
Amadeus Sanchez

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

Related Questions