Click here to Skip to main content
11,924,800 members (48,457 online)
Click here to Skip to main content
Add your own
alternative version


6 bookmarked

Serialize System.TimeSpan to XML

, 22 Oct 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Two ways to serialize and deserialize a property of type TimeSpan to XML


As many before me have discovered, it is not possible to directly serialize an object of type TimeSpan using XmlSerializer.

This tip presents two ways to do it in an easy fashion.

There is no source code to download for this tip, but all code is included in the tip.


My first approach to this problem, many years ago, was to create my own TimeSpan struct. As TimeSpan is a sealed struct and cannot be inherited, I had to implement all methods and properties in the original TimeSpan and there are quite a few when you start to look into it.

Another reason for doing this was also because in .NET 1.1, TimeSpan.ToString() contained a bug that omitted the millisecond part, if the value was less than 1 second. The effect of this bug was that values < 1 second were written as 'PT0S' when using DataSet.WriteXml. (This was fixed in .NET 2.0.)

Lessons learned from that experience were that it was a real pain to do and maintain.

Using the Code

One assumption is that you already have a class with a TimeSpan property that you want to serialize to an XML file or stream.

For further reading about XML Serialization, refer to this MSDN article: Introducing XML Serialization.

Solution 1

This is a very simple and straightforward solution and this is the biggest advantage.

What is done here is that one substitute property is added for the only purpose to be serialized. For all other purposes, the original property is used.

The original property is the one that will be visible for browsers and the substitute property will be serialized.

// Original property
[XmlIgnore] // This attribute prevents the property from being serialized by the XmlSerializer
public TimeSpan ReadTimeout 
        return XmlConvert.ToTimeSpan(ReadTimeout_string);
        ReadTimeout_string = XmlConvert.ToString(value);

// Substitute property
[Browsable(false)] // Hides the property from, for example, a PropertyGrid
[XmlElement("ReadTimeout")] // Overrides the default name of property. 
                            // In this case ReadTimeout_string will become ReadTimeout 
public string ReadTimeout_string { get; set; }

Only public properties will be serialized, hence the string variant of ReadTimeout cannot be a private field.
The annoying part of this is that users of this class can also access the substitute property, which may be confusing.

Solution 2

In this case, we create a new class for the purpose of serialization.

This solution requires a little bit more work, but the major benefit is that this class can be reused and it makes for a more elegant solution as no substitute property has to be added.

public class XmlTimeSpan
    private TimeSpan m_internal = TimeSpan.Zero;

    public XmlTimeSpan()
        : this(TimeSpan.Zero)

    public XmlTimeSpan(TimeSpan input)
        m_internal = input;

    public static implicit operator TimeSpan(XmlTimeSpan input)
        return (input != null) ? input.m_internal : TimeSpan.Zero;

    // Alternative to the implicit operator TimeSpan(XmlTimeSpan input)
    public TimeSpan ToTimeSpan()
        return m_internal;

    public static implicit operator XmlTimeSpan(TimeSpan input)
        return new XmlTimeSpan(input);

    // Alternative to the implicit operator XmlTimeSpan(TimeSpan input)
    public void FromTimeSpan(TimeSpan input)
        this.m_internal = input;

    public string Value
            return XmlConvert.ToString(m_internal);
            m_internal = XmlConvert.ToTimeSpan(value);

The XmlText attribute prevents an extra child node from being created. The output will be:


instead of:


In this case, we only need one property in the class to be serialized.

The only thing that has to be added is the attribute XmlElement. This will redirect the serialization to use the class specified as the argument.

public TimeSpan ReadTimeout { get; set; }

Points of Interest

In both solutions, I have opted for saving the value as an xsd:duration string.

2 minutes and 4.5 seconds will be represented as PT2M4.5S. This is a personal choice and my reasoning is that it is more human readable.

Another option is to use TimeSpan.Ticks and save the value as a long.

public long Value
        return m_internal.Ticks;
        m_internal = new TimeSpan(value);

One can, of course, always argue why the data has to be human readable, but when comparing configuration files between each other, I find it easier and faster to find strange values when using the XML format compared to using ticks.


Revision Date Comment
1 2014-10-22 First release
2 2014-10-23 Corrected the XML formatting and some spelling errors


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

George Jonsson
Philippines Philippines
I have worked with mobile phone testing in manufacturing for 20 years.
Originally from Sweden, but moved to China in 2012.
At the moment I am on a sabbatical year in the Philippines.

Over the years I have used various programming languages such as Pascal, C, C++, C#, SQL, XML, XSLT.

You may also be interested in...

Comments and Discussions

SuggestionAdditional methods for DataGridView data binding. Pin
Member 475507315-Feb-15 21:55
memberMember 475507315-Feb-15 21:55 
GeneralRe: Additional methods for DataGridView data binding. Pin
George Jonsson15-Feb-15 22:45
professionalGeorge Jonsson15-Feb-15 22:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.151125.3 | Last Updated 22 Oct 2014
Article Copyright 2014 by George Jonsson
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid