Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to fix DateTime values after .NET Xml Serialization

0.00/5 (No votes)
15 Sep 2004 1  
This will explain what happens when you use the .NET XmlSerializer class directly or indirectly (via .NET Remoting or Web Service calls) to send DataTables with DateTime values in them across different time zones. .NET framework automatically converts the values to the caller's timezone.

Introduction

The new .NET technologies, Remoting and Web Services has made life much easier than the days of trying to get DCOM to work. Although with anything that has been made easier there are some details that have been made too easy. In the case of Remoting or calling a web service, the Microsoft .NET Framework includes an automatic feature that converts all returned DataTables with DateTime values to the caller's time zone. So if you're in Seattle and need to find out a certain DateTime value in a database table row (let's say sale_date) on a server that runs in New York City you can make a web service call to find out. What happens is the sale_date value may have a value of 8/22/2004 9:05 am on the server in New York, but your web service call will result in a value of 8/22/2004 6:05 am. Which is clearly wrong. This article will tell you how to fix this problem.

Background

The problem seems to only occur whenever you send a DataTable as a return value. This is because .NET Framework will automatically serialize the DataTable into xml using it's System.Xml.Serialization.XmlSerializer class. The XmlSerializer will convert the DateTime values upon deserialization on the client. The idea here is to take control of the xml serialization process and manipulate the xml using regular expressions to give us the correct result.

Using the code

1. In the web service we first need to convert the DataTable to an xml string and send back the string. We use the System.IO.StringWriter class to write out the xml string:

  using System.Data;
  using System.IO;
  using System.Web.Services;
  ...
  namespace NYDataServices
  {
  ...
  // Web service is running in New York City

  public class MyWebService : System.Web.Services.WebService
  {
  ...

  [WebMethod]
  public string GetData()
  {
    DataTable dataTable = null;
    // Get data from database as a DataTable

    ...

    // Now convert the DataTable to an xml string and return it to client

    return convertDataTableToString( dataTable );
  }

  private string convertDataTableToString( DataTable dataTable )
  {
    DataSet dataSet = new DataSet();
    dataSet.Tables.Add( dataTable );
    StringWriter writer = new StringWriter();
    dataSet.WriteXml( writer, XmlWriteMode.WriteSchema );

    return writer.ToString();
  }

2. On the client side we make the call to get the data and receive the data as an xml string.

  using System.Data;
  using System.Text.RegularExpressions;
  ...
  namespace SeattleClient
  {
  ...
  // Client program running in Seattle

  public class MyClient : System.Windows.Form
  {
  ...

  public void GetDataFromServer()
  {
    NYDataServices.MyWebService ws = new NYDataServices.MyWebService();
    string xmlString = ws.GetData();
    DataTable dataTable = convertStringToDataTable( xmlString );
    // Do something with dataTable

    ...
  }

3. Converting the xml string back to a DataTable requires the use of regular expressions to search, adjust time values and replace. The DateTime values take on the form of 2004-08-22T00:00:00.0000000-05:00. The last 5 characters in the string indicate the UTC (Universal Time Coordinate) time. During xml deserialization back into a DataTable, the XmlSerializer class reads this value and creates an offset value based on the client's UTC time. It then adds this offset into all DateTime values upon deserialization. The kicker here is that if the DateTime value happens to be on DST (Daylight Savings Time) and the client is not on DST it will adjust for this too. We use some of the magic of the System.Text.RegularExpression namespace such as the Regex.Replace() function, Match class and MatchEvaluator delegate.

  private DataTable convertStringToDataTable( string xmlString )
  {
   // Search for datetime values of the format 

   // --> 2004-08-22T00:00:00.0000000-05:00

   string rp = @"(?<DATE>\d{4}-\d{2}-\d{2})(?<TIME>T\d{2}:\d{2}:\d{2}."+
         "\d{7}-)(?<HOUR>\d{2})(?<LAST>:\d{2})";
   // Replace UTC offset value

   string fixedString = Regex.Replace( xmlString, rp, 
        new MatchEvaluator( getHourOffset ) );
   
   DataSet dataSet = new DataSet();
   StringReader stringReader = new StringReader( fixedString );
   dataSet.ReadXml( stringReader );
 
   return dataSet.Tables[ 0 ];
  }

  private static string getHourOffset( Match m )
  {
   // Need to also account for Daylights Savings 

   // Time when calculating UTC offset value

   DateTime dtLocal = DateTime.Parse( m.Result( "${date}" ) );
   DateTime dtUTC = dtLocal.ToUniversalTime();
   int hourLocalOffset = dtUTC.Hour - dtLocal.Hour;
   int hourServer = int.Parse( m.Result( "${hour}" ) );
   string newHour = ( hourServer + ( hourLocalOffset - 
       hourServer ) ).ToString( "0#" );
   string retString = m.Result( "${date}" + "${time}" +
      newHour + "${last}" );
 
   return retString;
  }

Points of Interest

I know this problem happens when sending back DataTables. I'm not sure if the same applies to custom classes, although I suspect it does.

Here are links that I found very useful --

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here