dateTime in Web Services

G

Guest

I am having a problem with a datetime from a web services provider

The provider is sending the following SOAP response

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="urn:JadeWebServices/WebServiceProvider/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:types="urn:JadeWebServices/WebServiceProvider/encodedTypes">
<soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<types:wsReturnTimeStampResponse
<wsReturnTimeStampResult>2001-01-30T14:45:56.000-00:00</wsReturnTimeStampResult>
</types:wsReturnTimeStampResponse>
</soap:Body>
</soap:Envelope>

With a breakpoint on the ws.WsReturnTimeStamp method call, the bubble help
displays

ws.wsReturnTimeStamp #1/31/2001 3:45:56 AM#

when I was expecting to receive

ws.wsReturnTimeStamp #1/30/2001 2:45:56 PM#


The method definition in the WSDL is as follows

<message name="wsReturnTimeStampSoapOut">
<part name="wsReturnTimeStampResult" type="xsd:dateTime"/>

</message>

Any explanations as to what the problem might be?
 
S

scorpion53061

The Special Case of XML
Found at
http://msdn.microsoft.com/netframew.../library/en-us/dndotnet/html/datetimecode.asp



Several people I've talked to recently had the design goal of
serializing time values over Web services such that the XML that
represents the DateTime would be formatted in GMT (e.g., with a zero
offset). While I've heard various reasons ranging from the desire to
simply parse the field as a text string for display in a client to
wanting to preserve the "stored in UCT" assumptions that exist on the
server to the callers of Web services, I've not been convinced that
there is ever a good reason to control the marshalling format on the
wire to this degree. Why? Simply because the XML encoding for a DateTime
type is perfectly adequate for representing an instant in time, and the
XML serializer that is built into the .NET Framework does a fine job of
managing the serialization and deserialization issues associated with
time values.

Further, it turns out that forcing the System.XML.Serialization
serializer to encode a date value in GMT on the wire is not possible in
..NET, at least not today. As a programmer, designer, or project
manager, your job then becomes making sure that the data that is being
passed in your application is performed accurately with a minimum of
cost.

Several of the groups I talked with in the research that went into this
paper had adopted the strategy of defining special classes and writing
their own XML serializers so that they have full control over what the
DateTime values on the wire looked like in their XML. While I admire the
pluck that developers have when making the leap into this brave
undertaking, rest assured that the nuances of dealing with daylight
savings time and time zone conversion issues alone should make a good
manager say, "No way," especially when the mechanisms provided in the
..NET Framework do a perfectly accurate job of serializing time values
already.

There is only one trick you have to be aware of, and as a designer you
MUST understand this and adhere to the rule (see Rule #5).

Code that doesn't work:

Let's first define a simple XML class with a DateTime member variable.
For completeness, this class is the simplified equivalent of the
recommended approach illustrated later in the article.

<XmlType(TypeName:="timeTestDef", _
Namespace:= "http://tempuri.org/Timetester.xsd")>), _
XmlRoot(), Serializable()> _
Public Class timeTestDef
Private __timeVal As DateTime

<XmlIgnore()> _
Public timeValSpecified As Boolean

<XmlElement(ElementName:="timeVal", IsNullable:=False, _
Form:=XmlSchemaForm.Qualified, DataType:="dateTime", _
Namespace:="http://tempuri.org/Timetester.xsd")> _
Public Property timeVal() As DateTime
Get
timeVal = __timeVal
End Get
Set(ByVal Value As DateTime)
__timeVal = Value
timeValSpecified = True
End Set
End Property
End Class

Now, let's use this class to write some XML to a file.

' write out to the file
Dim t As Xml.XmlTextWriter
Dim ser As XmlSerializer
Dim tt As New timeTest ' a class that has a DateTime variable
' set the fields in your class
tt.timeVal = DateTime.Parse("12/12/2003 12:01:02 PM")
tt.timeVal = tt.TimeVal.ToUniversalTime()

' get a serializer for the root type, and serialize this UTC time
ser = New XmlSerializer(GetType(timeTest))
t = New Xml.XmlTextWriter("c:\timetest.xml", System.Text.Encoding.UTF8)
ser.Serialize(t, tt)
t.Close()
t = Nothing
tt = Nothing

When this code runs, the XML that is serialized to the output file
contains an XML DateTime representation as follows:

<timeVal>2003-12-12T20:01:02.0000000-08:00</timeVal>
This is an error: the value encoded in the XML is off by eight hours!
Since this happens to be the time zone offset of my current machine, we
should be suspicious. Looking at the XML itself, the date is right, and
the 20:01:02 date corresponds to the clock time in London for my own
noontime, but the offset portion is not correct for a London-based
clock. When the XML looks like the London time, the offset should also
represent the London viewpoint, which this code doesn't achieve.

The XML serializer always assumes that DateTime values being serialized
represent local machine time, so it applies the machine local time zone
offset as the offset portion of the encoded XML time. When we
deserialize this onto another machine, the original offset is subtracted
from the value being parsed, and the current machine's time-zone offset
is added.

When we start with a local time, the result of serialization (encode to
XML DateTime followed by decode to local machine time) is always
correct-but only if the starting DateTime value being serialized
represents local time when serialization begins. In the case of this
broken code example, we had already adjusted the DateTime value in the
timeVal member variable to UCT time, so when we serialize and
deserialize, the result is off by the number of hours equal to the
time-zone offset of the originating machine. This is bad.

Best Practice #4
When testing, calculate the value you expect to see in the XML string
that is serialized using a machine local time view of the point in time
being tested. If the XML in the serialization stream differs, log a bug!
Fixing this code is simple. Comment out the line that calls
ToUniversalTime().

Best Practice #5
When writing code to serialize classes that have DateTime member
variables, the values must represent local time. If they do not contain
local time, adjust them prior to any serialization step, including
passing or returning types that contain DateTime values in Web services.
The Class Coders Quandary
Earlier we looked at a pretty unsophisticated class that exposed a
DateTime property. In that class, we simply serialized what we stored in
a DateTime, without regard to whether the value represented a local or
universal time viewpoint. Let's look at a more sophisticated approach
that offers programmers an overt choice as to what time-zone assumptions
they desire, while always serializing properly.

When coding a class that will have a member variable of type DateTime, a
programmer has a choice of making the member variable public or writing
the property logic to wrap the member variable with get/set operations.
Choosing to make the type public has several disadvantages that, in the
case of DateTime types, can have consequences that are not under the
class developer's control.

Using what we learned so far, consider instead providing two properties
for each DateTime type.

The following example illustrates the recommended approach to managing
DateTime member variables:

<XmlType(TypeName:="timeTestDef", _
Namespace:= "http://tempuri.org/Timetester.xsd")>), _
XmlRoot(), Serializable(), _
EditorBrowsable(EditorBrowsableState.Advanced)> _
Public Class timeTestDef
Private __timeVal As DateTime

<XmlIgnore()> _
Public timeValSpecified As Boolean

<XmlElement(ElementName:="timeVal", IsNullable:=False, _
Form:=XmlSchemaForm.Qualified, DataType:="dateTime", _
Namespace:="http://tempuri.org/Timetester.xsd")> _
Public Property timeVal() As DateTime
Get
timeVal = __timeVal.ToLocalTime()
End Get
Set(ByVal Value As DateTime)
__timeVal = Value.ToUniversalTime()
timeValSpecified = True
End Set
End Property

<XmlIgnore()> _
Public Property timeValUTC() As DateTime
Get
timeValUTC = __timeVal
End Get
Set(ByVal Value As DateTime)
__timeVal = Value
timeValSpecified = True
End Set
End Property
End Class

This example is the corrected equivalent to the prior class
serialization example. In both class examples (this one and the earlier
one), the classes are implementations that are described with the
following schema:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="Timetester"
targetNamespace="http://tempuri.org/Timetester.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/Timetester.xsd"
xmlns:mstns="http://tempuri.org/Timetester.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:element name="timeTest" type="timeTestDef"/>
<xs:complexType name="timeTestDef">
<xs:sequence>
<xs:element name="timeVal" type="xs:dateTime"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
In this schema, and in any class implementations, we define a member
variable that represents an optional time value. In our recommended
example, we have provided two properties with both getters and
setters-one for the universal time and one for local time. The
angle-bracketed attributes that you see in the code tell the XML
serializer to use the local time version for serialization, and
generally make the class implementation result in schema-compliant
output. To make the class properly deal with the optional lack of
expression when no value is set in the instance, the timeValSpecified
variable and associated logic in the property setter controls whether
the XML element is expressed at serialization time or not. This optional
behavior exploits a feature in the serialization subsystem that was
designed to support optional XML content.

Using this approach to managing DateTime values in your .NET classes
gives you the best of both worlds-you get storage access based on
universal time so that calculations are accurate, and you get proper
serialization of local time views.

Best Practice #6
When coding, make DateTime member variables private and provide two
properties for manipulating your DateTime members in either local or
universal time. Bias the storage in the private member as UCT time by
controlling the logic in your getters and setters. Add the XML
serialization attributes to the local time property declaration to make
sure that the local time value is what is serialized (see example).
Caveats to this approach
The recommended approach of managing a DateTime in Universal time within
your private member variables is sound, as is the recommendation to
provide dual properties to allow coders to deal with the versions of
time that they are most comfortable with. One issue that a developer
using this or any other approach that exposes any local time to a
program continues to be the 25-hour-day issue around daylight savings
time. This will continue to be an issue for programs that use CLR
version 1.0 and 1.1, so you have to be aware as to whether your program
falls into this special case (the added or missing hour for the time
being represented), and adjust manually. For those who cannot tolerate a
one-hour per year issue window, the current recommendation is to store
your dates as strings or some other self-managed approach. (Unix long
integers are a good option.)

For CLR version 2.0 (available in the upcoming release of Visual StudioR
code-named "Whidbey"), awareness of whether a DateTime contains a local
time or a universal time value is being added to the .NET Framework. At
that point, the recommended pattern will continue to work, but for
programs that interact with member variables via the UTC properties,
these errors in the missing/extra hour period will be eliminated. For
this reason, the best practice for coding using dual properties is
strongly suggested today, so that your programs will migrate cleanly to
CLR version 2.0.
 
C

Cor Ligthert

Herfried,

I never saw this article, a pity the writer did not include those powerful
Microsoft.VisualBasic methods and the Convert as well.

While he did use completly localized US samples (What I could understand
from Kelly of course)
\\\
Dim d As DateTime
d = DateTime.Parse("Oct 26, 2003 12:00:00 AM") 'date assignment
d = d.ToUniversalTime().AddHours(3.0).ToLocalTime()
' - displays 10/26/2003 02:00:00 AM – Correct!
MsgBox(d.ToString
///

While it could have been so easy the same and not localized with

\\\
Dim d As DateTime
d = New DateTime(2003, 10, 26, 12, 0, 0)
d = d.ToUniversalTime().AddHours(3.0).ToLocalTime()
' - displays 10/26/2003 02:00:00 AM – Correct!
MsgBox(d.ToString)
///

That while this code

\\\
Public Class Test
Public Shared Sub main()
Dim ds As New DataSet
Dim dt As New DataTable
ds.Tables.Add(dt)
dt.Columns.Add("DateTime", GetType(System.DateTime))
For i As Integer = 0 To 5
dt.Rows.Add(dt.NewRow)
dt.Rows(i)(0) = Now.AddDays(i)
Next
ds.WriteXml("C:\Herfried.XML", XmlWriteMode.WriteSchema)
End Sub
End Class
///
Creates today 11 februari 2005 this dataset
\\\
<?xml version="1.0" standalone="yes"?>
<NewDataSet>
<xs:schema id="NewDataSet" xmlns=""
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true"
msdata:Locale="nl-NL">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="Table1">
<xs:complexType>
<xs:sequence>
<xs:element name="DateTime" type="xs:dateTime" minOccurs="0"
/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<Table1>
<DateTime>2005-02-11T14:47:01.2656250+01:00</DateTime>
</Table1>
<Table1>
<DateTime>2005-02-12T14:47:01.2656250+01:00</DateTime>
</Table1>
<Table1>
<DateTime>2005-02-13T14:47:01.2656250+01:00</DateTime>
</Table1>
<Table1>
<DateTime>2005-02-14T14:47:01.2656250+01:00</DateTime>
</Table1>
<Table1>
<DateTime>2005-02-15T14:47:01.2656250+01:00</DateTime>
</Table1>
<Table1>
<DateTime>2005-02-16T14:47:01.2656250+01:00</DateTime>
</Table1>
</NewDataSet>
///

However, thanks for showing the link.

:)

Cor
 
S

scorpion53061

Cor,

I copied and pasted the sections of the article I thought applicable to
the posters question. I included the link at the top of the post where
it came from.
 
S

scorpion53061

No problem. I just didn't want people to think I was trying to take
credit for someone else's work.
 
H

Herfried K. Wagner [MVP]

Cor,

Cor Ligthert said:
While he did use completly localized US samples (What I could understand
from Kelly of course)
\\\
Dim d As DateTime
d = DateTime.Parse("Oct 26, 2003 12:00:00 AM") 'date assignment
d = d.ToUniversalTime().AddHours(3.0).ToLocalTime()
' - displays 10/26/2003 02:00:00 AM – Correct!
MsgBox(d.ToString
///

While it could have been so easy the same and not localized with

\\\
Dim d As DateTime
d = New DateTime(2003, 10, 26, 12, 0, 0)
d = d.ToUniversalTime().AddHours(3.0).ToLocalTime()
' - displays 10/26/2003 02:00:00 AM – Correct!
MsgBox(d.ToString)
///

There are people who think that there are no cultures other than the US...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top