Click here to Skip to main content
Click here to Skip to main content
Go to top

Custom ShortDate type (struct: IComparable)

, 19 Feb 2005
Rate this:
Please Sign up or sign in to vote.
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 & overrided 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

Share

About the Author

Stefan Prodan
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.
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
GeneralEquals not implemented correctly. PinmemberMarc Brooks15-Feb-05 7:09 
GeneralRe: Equals not implemented correctly. PinmemberStefan Prodan15-Feb-05 19:28 
GeneralRe: Equals not implemented correctly. PinmemberMarc Brooks16-Feb-05 12:30 
GeneralRe: Equals not implemented correctly. PinmemberStefan Prodan19-Feb-05 10:04 
GeneralRe: Equals not implemented correctly. PinsitebuilderUwe Keim1-Jan-07 3:06 
GeneralGood point, but caution... PinmemberMarc Brooks1-Jan-07 7:54 
GeneralAdvantages PinmemberBuzza6914-Feb-05 13:21 
GeneralRe: Advantages PinmemberStefan Prodan14-Feb-05 14:24 
GeneralRe: Advantages PinmemberBuzza6914-Feb-05 14:30 
GeneralRe: Advantages Pinmembermcarbenay22-Feb-05 5:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    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 | Mobile
Web01 | 2.8.140922.1 | Last Updated 19 Feb 2005
Article Copyright 2005 by Stefan Prodan
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid