WCF - Store and restore UTC DateTime
WCF - Store and restore UTC DateTime
We expose to our customer a WCF webservice allowing them to store DateTime in SQL Server databases.
Extract of the WSDL :
<xs:complexType name="TimePeriod">
<xs:sequence>
<xs:element minOccurs="0" name="endDateTime" nillable="true" type="xs:dateTime"/>
<xs:element minOccurs="0" name="startDateTime" nillable="true" type="xs:dateTime"/>
</xs:sequence>
For exemple, my customer send me UTC Datetime :
<af:effectivePeriod>
<af:startDateTime>2018-01-16T10:32:28Z</af:startDateTime>
</af:effectivePeriod>
This is stored in a SQL Server database in a datetime
field.
datetime
But in the output of the read service, I don't have the UTC indicator :
<af:effectivePeriod>
<af:startDateTime>2018-01-16T10:32:28</af:startDateTime>
</af:effectivePeriod>
"Z" is kind of a unique case for DateTimes. The literal "Z" is actually part of the ISO 8601 DateTime standard for UTC times. When "Z" (Zulu) is tacked on the end of a time, it indicates that that time is UTC, so really the literal Z is part of the time
How is it possible for me to have the Z
at the output of the read method? Do I have to modify the type of data stored in SQL Server? Do I have an impact in the WSDL of the services?
Z
This question has not received enough attention.
FORMAT
FORMAT(effectivePeriod, 'yyyy-MM-dd"T"HH:mm:ssZ')
I read data from SQL server using EntityFramework and the database first model
– Xavier W.
Jul 4 at 11:43
How does the "read method" look like? Will it be possible to format the returned value in the WSDL?
– Kim Rasmussen
Jul 4 at 12:20
See DateTime.Kind set to unspecified, not UTC, upon loading from database. You can use the approach from the link only if all your
DateTime
values stored in the datetime
database columns are supposed to be UTC.– Ivan Stoev
Jul 4 at 13:13
DateTime
datetime
2 Answers
2
Whenever you need to keep time offset you should work with DateTimeOffset (.net & SqlServer)
In your WCF datacontract, you can add a DateTimeOffset DataMember however it will have an impact on client side and your service wsdl will change as a complex type DateTimeOffset will be created:
<xs:complexType name="DateTimeOffset">
<xs:annotation>
<xs:appinfo>
<IsValueType>true</IsValueType>
</xs:appinfo>
</xs:annotation>
<xs:sequence>
<xs:element name="DateTime" type="xs:dateTime"/>
<xs:element name="OffsetMinutes" type="xs:short"/>
</xs:sequence>
</xs:complexType>
And with the
DateTimeOffset
in WSDL and in DB, will I be able to have the date with the Z
at the end automatically ?– Xavier W.
Jul 4 at 7:48
DateTimeOffset
Z
Microsoft have an explanation of how to "round trip" dates and times when converting them to and from strings; and your problem (which I'll get to below) is that you've lost the Kind
.
Kind
The DateTime
ToString
method's "o" standard format will give you the "round trip" kind that you are looking for.
DateTime
ToString
Use DateTime
Parse() with DateTimeStyles of RoundTripKind
to read the value back.
DateTime
RoundTripKind
The fact that your DateTime
is missing the 'Z' indicator means (as documented on the Standard Format page for "o") that the DateTime
's Kind
is Unspecified
. If you have read those DateTime
s from your database, the Kind
will be Unspecified
(you can use a debugger to examine the Kind
property after it has been read to confirm this).
DateTime
DateTime
Kind
Unspecified
DateTime
Kind
Unspecified
Kind
The way around that is that you have to know what kind of DateTime
s you are storing, so you can set the Kind when you read it (because if you don't do that, the Kind will be Unspecified). For example, if you only ever store UTC
Kind
DateTime
s, then when you read them from your database, you can set the Kind
on the DateTime appropriately. You can do that like this:
DateTime
UTC
Kind
DateTime
Kind
var myUtcDateTime = DateTime.SpecifyKind(myUnspecifiedDateTime, DateTimeKind.Utc);
I have an extension method to encapsulate this from me, so I can simply call:
var myDateTime = dataReader.GetUtcDateTime(ordinal);
which is implemented like this:
public static class DataReaderExtensions
{
public static DateTime GetUtcDateTime(this IDataReader reader, int ordinal)
{
var readDateTime = reader.GetDateTime(ordinal);
return DateTime.SpecifyKind(readDateTime, DateTimeKind.Utc);
}
}
Or you can look at this answer to see how to do it with Entity Framework.
(Note that this approach works for UTC; but you can't decide to do the same thing with Kind of Local
, since there's no guarantee that the local you are reading the value in is the same one that it was written in. For example, Daylight savings time may have just started or ended.)
Local
But I have to say, if you're really interested in the actual times, the better solution to this kind of problem (if you forgive the pun!) is to use DateTimeOffset
s. These store the DateTime and the Offset, and guarantee you get back out what you put in, without you needing to tinker with things.
DateTimeOffset
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
How do you read data from the SQL server? I you control the formatting of the response you could perhaps use the
FORMAT
function - ex.FORMAT(effectivePeriod, 'yyyy-MM-dd"T"HH:mm:ssZ')
– Kim Rasmussen
Jul 4 at 11:28