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

Custom ShortDate type (struct: IComparable)

0.00/5 (No votes)
19 Feb 2005 1  
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:

[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:

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:

private UInt16 _day;
private UInt16 _month;
private UInt16 _year;

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

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:

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:

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:

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:

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.

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