I have been getting annoyed at how TimeSpan structs in .NET do not support XML serialisation, so I wrote my own immutable LFS time struct, which I've called Elapsed. It includes pretty much everything you may need for describing times in LFS and also supports XML serialisation.
using System;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace DarkTimes.Lfs
{
/// <summary>
/// Represents an elapsed time span in LFS.
/// </summary>
[Serializable]
public struct Elapsed : IComparable<Elapsed>, IXmlSerializable
{
#region Fields
private long totalMilliseconds;
#endregion
#region Properties
public int Hours
{
get { return (int)(this.totalMilliseconds / 3600000); }
}
public int Minutes
{
get { return (int)(this.totalMilliseconds / 60000 % 60); }
}
public int Seconds
{
get { return (int)(this.totalMilliseconds / 1000 % 60); }
}
public int Milliseconds
{
get { return (int)(this.totalMilliseconds % 1000); }
}
public int TotalSeconds
{
get { return (int)(this.totalMilliseconds / 1000); }
}
public long TotalMilliseconds
{
get { return this.totalMilliseconds; }
}
public bool IsZero
{
get { return (this.totalMilliseconds == 0); }
}
#endregion
#region Constructors
public Elapsed(long totalMilliseconds)
{
this.totalMilliseconds = totalMilliseconds;
}
public Elapsed(int hours, int minutes, int seconds, int milliseconds)
{
this.totalMilliseconds = hours * 3600000;
this.totalMilliseconds += minutes * 60000;
this.totalMilliseconds += seconds * 1000;
this.totalMilliseconds += milliseconds;
}
#endregion
#region Methods
public Elapsed Add(Elapsed value)
{
return new Elapsed(this.totalMilliseconds + value.totalMilliseconds);
}
public Elapsed Subtract(Elapsed value)
{
return new Elapsed(this.totalMilliseconds - value.totalMilliseconds);
}
public override string ToString()
{
if (this.Hours > 0)
{
return string.Format("{0}:{1:00}:{2:00}.{3:000}", this.Hours, this.Minutes, this.Seconds, this.Milliseconds);
}
return string.Format("{0}:{1:00}.{2:000}", this.Minutes, this.Seconds, this.Milliseconds);
}
public override bool Equals(object obj)
{
return ((obj is Elapsed) && (this == (Elapsed)obj));
}
public override int GetHashCode()
{
return ((int)totalMilliseconds ^ (int)(totalMilliseconds >> 32));
}
#endregion
#region IComparable
public int CompareTo(Elapsed other)
{
if (this.totalMilliseconds > other.totalMilliseconds)
{
return 1;
}
else if (this.totalMilliseconds < other.totalMilliseconds)
{
return -1;
}
return 0;
}
#endregion
#region IXmlSerializable
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
if (!reader.IsEmptyElement)
{
long ms = 0;
if (long.TryParse(reader.ReadString(), out ms))
{
this.totalMilliseconds = ms;
}
reader.ReadEndElement();
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteString(this.totalMilliseconds.ToString());
}
#endregion
#region Static Methods
public static Elapsed FromSeconds(int seconds)
{
return new Elapsed(seconds * 1000);
}
public static Elapsed FromMilliseconds(long milliseconds)
{
return new Elapsed(milliseconds);
}
public static byte[] ToBinary(Elapsed time)
{
return BitConverter.GetBytes(time.totalMilliseconds);
}
public static Elapsed FromBinary(byte[] bytes)
{
return new Elapsed(BitConverter.ToInt64(bytes, 0));
}
#endregion
#region Operators
public static Elapsed operator +(Elapsed a, Elapsed b)
{
return new Elapsed(a.totalMilliseconds + b.totalMilliseconds);
}
public static Elapsed operator -(Elapsed a, Elapsed b)
{
return new Elapsed(a.totalMilliseconds - b.totalMilliseconds);
}
public static Elapsed operator *(Elapsed a, Elapsed b)
{
return new Elapsed(a.totalMilliseconds * b.totalMilliseconds);
}
public static Elapsed operator /(Elapsed a, Elapsed b)
{
return new Elapsed(a.totalMilliseconds / b.totalMilliseconds);
}
public static bool operator ==(Elapsed a, Elapsed b)
{
return (a.totalMilliseconds == b.totalMilliseconds);
}
public static bool operator !=(Elapsed a, Elapsed b)
{
return (a.totalMilliseconds != b.totalMilliseconds);
}
public static bool operator >(Elapsed a, Elapsed b)
{
return (a.totalMilliseconds > b.totalMilliseconds);
}
public static bool operator <(Elapsed a, Elapsed b)
{
return (a.totalMilliseconds < b.totalMilliseconds);
}
#endregion
}
}