Click here to Skip to main content
15,848,264 members
Articles / Web Development / ASP.NET

Custom ShortDate type (struct: IComparable)

Rate me:
Please Sign up or sign in to vote.
3.24/5 (12 votes)
19 Feb 20052 min read 61.6K   88   14   10
A struct example

Introduction

Here is a custom C# .NET Date type (struct) that will help you deal with conversions to and from strings. The struct implements the IComparable interface and overrides the ToString, Equals and GetHashCode() functions.

Background

Defining a struct is almost similar to that of defining a class:

C#
[attributes] [modifiers] struct <structName> [: interfaces]
{
    [struct-body]
}[;]

Like a class, a struct can contain other types and is sometimes referred to as a lightweight version of a class because internally a struct is a value type. Creating a struct instance will not cause garbage collection (unless the constructor directly or indirectly creates a reference type instance) whereas creating a reference type instance can cause garbage collection. Another thing about a struct is that it always has a built-in public default constructor and no destructor.

How is a struct different from a class:

  • a struct is implicitly sealed, a class isn't
  • a struct can't be abstract, a class can
  • a struct can't call : base() in its constructor whereas a class with no explicit base class can
  • a struct can't extend another class, a class can
  • a struct can't declare protected members (e.g. fields, nested types) a class can
  • a struct can't declare abstract function members, an abstract class can
  • a struct can't declare virtual function members, a class can
  • a struct can't declare sealed function members, a class can
  • a struct can't declare override function members, a class can. The only exception to this rule is that a struct can override the virtual methods of System.Object, viz, Equals(), and GetHashCode(), and ToString() (more info on StructsVsClasses)

Struct Code

The Date struct looks like this:

C#
using System;
using System.Text.RegularExpressions;

[Serializable]
public struct Date : IComparable
{
    //private members
    
    //public members
    
    //constructor
    
    //functions
    
    //IComparable & overridden implementation
}

I want to build a ShortDate struct with three fields:

C#
private UInt16 _day;
private UInt16 _month;
private UInt16 _year;

Next, I make those fields public, so now my struct has three properties:

C#
public UInt16 Day
{
    get { return _day; }
    set
    {
        if (value > 31)
        {
            throw new ArgumentException("Day out of range[31]");
        }
        else
        {
            _day = value;
        }
    }
}

public UInt16 Month
{
    get { return _month; }
    set
    {
        if (value > 12)
        {
            throw new ArgumentException("Month out of range[12]");
        }
        else
        {
            _month = value;
        }
    }
}

public UInt16 Year
{
    get { return _year; }
    set
    {
        if (value.ToString().Length > 4)
        {
            throw new ArgumentException("Year out of range.");
        }
        else
        {
            _year = value;
        }
    }
}

Besides the default constructor that makes all the fields equal to zero, I've made another one that will fill the fields with data from the DateTime parameter:

C#
public Date(DateTime dt)
{
    _month = Convert.ToUInt16(dt.Month);
    _day = Convert.ToUInt16(dt.Day);
    _year = Convert.ToUInt16(dt.Year);
}

IComparable implementation and overrides of the ToString, Equals and GetHashCode() functions:

C#
public int CompareTo(object obj)
{
    Date dt = (Date) obj;
    UInt16 i = 0;
    if (dt._day == _day)
    {
        i += 100;
    }
    if (dt._month == _month)
    {
        i += 010;
    }
    if (dt._year == _year)
    {
        i += 001;
    }

    return i;
}

public override bool Equals(object obj)
{
    if (obj == null    || ! (obj is Date))        
        return false;    
    try     
    {        
        Date other = (Date)obj;        
        return this._day == other._day            
            && this._month == other._month            
            && this._year == other._year;    
    }    
    catch    
    {            
        return false;    
    }
}

public override int GetHashCode()
{
    return (_day ^ _month ^ _year);
}

public override string ToString() 
{
    return (_day.ToString() + "/" + _month.ToString() + "/" + _year.ToString());
}
public string ToString(string separator)
{
    string s1="", s2="";
    if(_day.ToString().Length == 1 )
    {
        s1="0";
    }
    if( _month.ToString().Length == 1 )
    {
        s2="0";        
    }
    return (s1+_day.ToString()+separator+s2+_month.ToString()+separator+_year);
}
public static bool operator >(Date d1,Date d2)
{
    if( d1.ToDateTime() > d2.ToDateTime() )
    {
        return true;
    }
    return false;
}
public static bool operator <(Date d1,Date d2)
{
    if( d1.ToDateTime() < d2.ToDateTime() )
    {
        return true;
    }
    return false;
}

And finally some useful functions to help us manage this Date type:

C#
public bool IsDateTime(string dt)
{
    Regex rgx = new 
      Regex(@"(?<Day>\d{1,2})/(?<Month>\d{1,2})/(?<Year>(?:\d{4}|\d{2}))");
    if (rgx.IsMatch(dt))
    {
        return true;
    }
    else
    {
        return false;
    }
}

public DateTime StringToDate(string dt)
{
    if (IsDateTime(dt))
    {
        _day = Convert.ToUInt16(dt.Split('/')[0]);
        _month = Convert.ToUInt16(dt.Split('/')[1]);
        _year = Convert.ToUInt16(dt.Split('/')[2]);
    }
    else
    {
        throw new ArgumentException("The string can't be converted to a date");
    }

    return new DateTime(Year, Month, Day);

}

public DateTime StringToDate(string dt, char separator)
{
    try
    {
        _day = Convert.ToUInt16(dt.Split(separator)[0]);
        _month = Convert.ToUInt16(dt.Split(separator)[1]);
        _year = Convert.ToUInt16(dt.Split(separator)[2]);
    
        return new DateTime(Year, Month, Day);
    }
    catch (Exception)
    {
        throw new InvalidCastException("The input string is not a date format");
    }
}

public DateTime ToDateTime()
{
    return new DateTime(Year, Month, Day);
}

public static DateTime ToDateTime(Date dt)
{
    return new DateTime(dt.Year, dt.Month, dt.Day);
}

Using the Code

You can now include the struct into a project and try it out, here are some examples:

C#
private void Page_Load(object sender, System.EventArgs e)
{
    //default constructor
    Date d1 = new Date();
    Response.Write("Luna: "+d1.Month+" Zi: "+d1.Day+" An: "+d1.Year);

    //defined constructor
    Date d2 = new Date(DateTime.Now);
    Response.Write(d2.ToString());
    
    //StringToDate will fill the fields
    Date d3 = new Date();
    Response.Write(d3.StringToDate("01/02/05"));


    Response.Write(d1.ToString()+" -d1- "+ d1.GetHashCode());
    Response.Write(d2.ToString()+" -d2- "+ d2.GetHashCode());
    Response.Write(d3.ToString()+" -d3- "+ d3.GetHashCode());

    Date d4 = new Date();
    d4.StringToDate("01/02/05");
    Date d5 = new Date();
    d5.Day = 1;
    d5.Month = 5;
    d5.Year = 05;

    Response.Write(d4.ToString()+" Equals "+d5.ToString()+ 
                                              "? "+d4.Equals(d5));
    Response.Write(d4.ToString()+" CompareTo "+d5.ToString()+
                                           "? "+d4.CompareTo(d5));
}

History

  • Version 2.0

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.


Written By
CEO VeriTech.io
Romania Romania
Co-Founder at VeriTech.io. Passionate about software architecture, SOA, domain driven design, continuous integration, .NET and Javascript programming. I write on www.stefanprodan.com.

Comments and Discussions

 
GeneralEquals not implemented correctly. Pin
Marc Brooks15-Feb-05 8:09
Marc Brooks15-Feb-05 8:09 
GeneralRe: Equals not implemented correctly. Pin
Stefan Prodan15-Feb-05 20:28
Stefan Prodan15-Feb-05 20:28 
GeneralRe: Equals not implemented correctly. Pin
Marc Brooks16-Feb-05 13:30
Marc Brooks16-Feb-05 13:30 
GeneralRe: Equals not implemented correctly. Pin
Stefan Prodan19-Feb-05 11:04
Stefan Prodan19-Feb-05 11:04 
GeneralRe: Equals not implemented correctly. Pin
Uwe Keim1-Jan-07 4:06
sitebuilderUwe Keim1-Jan-07 4:06 
GeneralGood point, but caution... Pin
Marc Brooks1-Jan-07 8:54
Marc Brooks1-Jan-07 8:54 
GeneralAdvantages Pin
Buzza6914-Feb-05 14:21
Buzza6914-Feb-05 14:21 
GeneralRe: Advantages Pin
Stefan Prodan14-Feb-05 15:24
Stefan Prodan14-Feb-05 15:24 
Well working on asp.net applications I got problems with validate dates, convert them to string and back to DateTime type, so first it was a validation function, then a RegEx and so on. So I decide to make this a custom type because in these applications the dates looks like this dd/mm/yy and c# has mm/dd/yy, another thing was the long date that I was getting from the sql server when I needed only the short form.. so it may not look useful to many but at some point it can help.
About the '/' separator is not a big issue, you can always overload:
C#
<br />
public  string ToString(string separator) <br />
{<br />
	return (_day.ToString() + separator + _month.ToString() + separator + _year.ToString());<br />
}<br />


Thanks for your post. I didn't need a different separator but it's a good thing to make it more flexible.
GeneralRe: Advantages Pin
Buzza6914-Feb-05 15:30
Buzza6914-Feb-05 15:30 
GeneralRe: Advantages Pin
mcarbenay22-Feb-05 6:25
mcarbenay22-Feb-05 6:25 

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.