.Net Code Monkey RSS 2.0
 Saturday, January 07, 2012
Operators for System.DateTime, continued from Part 3

In this article I shall continue to deal with operators, but those specifically which will allow assignment of System.DateTime values and System.Data.SqlTypes.SqlDateTime values to the DateTimeNull object or vice-versa.
#Region "Operators for System.DateTime"

    ''' <summary>
    ''' Converts a System.DateTime structure to a DateTimeNull object.
    ''' </summary>
    ''' <param name="value">A DateTime structure. </param>
    ''' <returns>A DateTimeNull object whose Value is equal to the combined Date and TimeOfDay properties of the supplied System.DateTime structure.</returns>
    Public Shared Widening Operator CType(ByVal value As System.DateTime) As DateTimeNull
        Return New DateTimeNull(value)
    End Operator

    ''' <summary>
    ''' Converts a DateTimeNull object to a System.DateTime structure.
    ''' </summary>
    ''' <param name="value">a DateTimeNull</param>
    ''' <returns>A System.DateTime structure whose Value is equal to the combined Date and TimeOfDay properties of the supplied DateTimeNull object.</returns>
    Public Shared Widening Operator CType(ByVal value As DateTimeNull) As System.DateTime
        If value.HasValue Then
            Return value.InternalDate
        Else
            Throw New InvalidCastException("Cannot convert Null DateTimeNull to System.DatetTime.")
        End If
    End Operator

#End Region
These two widening operators allow us to write the following code without compile errors:
Dim nullableDate As DateTimeNull = Nothing
Dim systemDate As System.DateTime

nullableDate = systemDate
systemDate = nullableDate
The first widening operator lets us cast from a System.DateTime to our DateTimeNull object, and the second one back again. Even with these two operators we cant yet do comparison checks between the two types. The code below will not compile.
If nullableDate = systemDate Then
End If
If systemDate = nullableDate Then
End If
For that to happen we need to add an equality operators. The code below will allow the first equality check to compile. Please note for every "equals" operator, you must provide a "not equals" operator too.
    ''' <summary>
    ''' Determines whether one specified DateTimeNull is equal to another specified DateTime.
    ''' </summary>
    ''' <param name="d1">A DateTimeNull.</param>
    ''' <param name="d2">A System.DateTime.</param>
    ''' <returns><c>true</c> if d1 is not null and d1 is equal to d2; otherwise, false.</returns>
    Public Shared Operator =(ByVal d1 As DateTimeNull, ByVal d2 As System.DateTime) As Boolean
        If d1.HasValue Then
            Return (d1.InternalDate = d2)
        Else
            Return False
        End If
    End Operator

    ''' <summary>
    ''' Determines whether one specified DateTimeNull is not equal to another specified DateTime.
    ''' </summary>
    ''' <param name="d1">A DateTimeNull.</param>
    ''' <param name="d2">A System.DateTime.</param>
    ''' <returns><c>true</c> if d1 is not null and d1 is not equal to d2; otherwise, false.</returns>
    Public Shared Operator <>(ByVal d1 As DateTimeNull, ByVal d2 As System.DateTime) As Boolean
        If d1.HasValue Then
            Return Not (d1.InternalDate = d2)
        Else
            Return False
        End If
    End Operator
We now need another pair of operators for the second condition.
    ''' <summary>
    ''' Determines whether one specified System.DateTime is equal to another specified DateTimeNull.
    ''' </summary>
    ''' <param name="d1">A System.DateTime.</param>
    ''' <param name="d2">A DateTimeNull.</param>
    ''' <returns><c>true</c> if d1 is not null and d1 is equal to d2; otherwise, false.</returns>
    Public Shared Operator =(ByVal d1 As System.DateTime, ByVal d2 As DateTimeNull) As Boolean
        If d2.HasValue Then
            Return (d1 = d2.InternalDate)
        Else
            Return False
        End If
    End Operator

    ''' <summary>
    ''' Determines whether one specified System.DateTime is not equal to another specified DateTimeNull.
    ''' </summary>
    ''' <param name="d1">A System.DateTime.</param>
    ''' <param name="d2">A DateTimeNull.</param>
    ''' <returns><c>true</c> if d1 is not null and d1 is not equal to d2; otherwise, false.</returns>
    Public Shared Operator <>(ByVal d1 As System.DateTime, ByVal d2 As .DateTimeNull) As Boolean
        If d2.HasValue Then
            Return Not (d1 = d2.InternalDate)
        Else
            Return False
        End If
    End Operator
We may now wish to take the time to add in some "greater-than-or-equal-to" and "less-than-or-equal-to" operators for these two data types.
    ''' <summary>
    ''' Determines whether one specified DateTimeNull is less than or equal to another specified System.DateTime.
    ''' </summary>
    ''' <param name="d1">A DateTimeNull.</param>
    ''' <param name="d2">A System.DateTime.</param>
    ''' <returns><c>true</c> if d1 is not null and d1 is less than or equal to d2; otherwise, false.</returns>
    Public Shared Operator <=(ByVal d1 As DateTimeNull, ByVal d2 As System.DateTime) As Boolean
        If d1.HasValue Then
            Return (d1.InternalDate <= d2)
        Else
            Return False
        End If
    End Operator

    ''' <summary>
    ''' Determines whether one specified DateTimeNull is greater than or equal to another specified System.DateTime.
    ''' </summary>
    ''' <param name="d1">A DateTimeNull.</param>
    ''' <param name="d2">A System.DateTime.</param>
    ''' <returns><c>true</c> if d1 is not null and d1 is greater than or equal to d2; otherwise, false.</returns>
    Public Shared Operator >=(ByVal d1 As DateTimeNull, ByVal d2 As System.DateTime) As Boolean
        If d1.HasValue Then
            Return (d1.InternalDate >= d2)
        Else
            Return False
        End If
    End Operator
Remember to provide operators for both left and right evaluation of the DateTimeNull object.

Casting to and from System.Data.SqlTypes.SqlDateTime

To allow cross casting between our DateTimeNull object and the System.Data.SqlTypes.SqlDateTime we just follow the same process and add in the widening and comparison operators as we did for the System.DateTime, but we do need to keep in mind the upper and lower date limits of the System.Data.SqlTypes.SqlDateTime structure or an out of range error could happen at run-time. So evaluate the value and throw an appropriate exception as necessary in the operator.

In Part 5, we will look at some of the methods we may need to implement

Saturday, January 07, 2012 9:00:42 AM (GMT Standard Time, UTC+00:00)  #    Comments [0] -
.Net | Asp.Net | DateTime | Operators | System.Data.SqlTypes.SqlDateTime | VB.Net | Widening Operators
 Wednesday, January 04, 2012

Background

Something I have always been a bit disappointed with in .net is the handling for dates with null values. I know you can use Nullable(Of Date) or Nullable(Of DateTime) (or i C# date? or Datetime?), but what I want is a date object that I can set a date value, set to Nothing or set to be equal to DBNull.Value.

So I thought I would document my path to creating the object I need. The code will be in VB.Net as that is what I curently have to use at my place of work.

Nullable Base Object.

First of all as I may need other objects like Int32s and Strings to be nullable in the future I'll create a nullable base object which all my new objects can inherit from and will contain some basic implementation. First I am going to create a class and declare it MustInherit (abstract)so that cannot be used on its own. This will be the base object that any or all of my nullable objects will inherit from.

Option Explicit On
Option Strict On

''' <summary>
''' This object is the object that all nullable objects should inherit from.
''' </summary>
Public MustInherit Class NullableBaseObject
    Implements System.Data.SqlTypes.INullable

#Region "Declarations"

    Protected _isNull As Boolean

#End Region

#Region "Properties"

    ''' <summary>
    ''' Gets a value indicating whether this instance has value.
    ''' </summary>
    ''' <value><c>true</c> if this instance has value; otherwise, false.</value>
    Public ReadOnly Property HasValue() As Boolean
        Get
            Return Not Me._isNull
        End Get
    End Property

    ''' <summary>
    ''' Gets a value indicating whether this instance is null.
    ''' </summary>
    ''' <value><c>true</c> if this instance is null; otherwise, false.</value>
    Public ReadOnly Property IsNull() As Boolean Implements System.Data.SqlTypes.INullable.IsNull
        Get
            Return Me._isNull
        End Get
    End Property

#End Region

#Region "Methods"

    ''' <summary>
    ''' Sets the internal is-null flag.
    ''' </summary>
    ''' <param name="value">if set to <c>true</c> [value].
    Protected Sub SetIsNull(ByVal value As Boolean)
        Me._isNull = value
    End Sub 

#End Region

End Class

There is a private boolean field _isNull which will hold the nullable state of the object. There are two properties that refer to this; IsNull and HasValue. These are in essence opposites of each other but have both been implemented to make the code more English when using. The IsNull property is implementation required from the "System.Data.SqlTypes.INullable" interface that the class is going to implement.

There is one method, The SetIsNull which sets the object state Null and can be called from inside any classes that inherit from this object.

Nullable DateTime Object

The nullable DateTime object will inherit from the NullableBaseObject. There will be a single private field _internalDate of type System.DateTime. This will hold our DateTime information when the object is not Null. This will be accessed by all internal code using a private property InternalDate. Unlike the System.DateTime which is Structure, I want to be able to set this object to Nothing, so I will use a Class, not a Structure.

Option Explicit On
Option Strict On

''' <summary>
''' Represents nullable an instance in time, typically represented as a date and time.
''' </summary>
<serializable()> _
Public Class DateTimeNull
    Inherits NullableBaseObject
    
#Region "Declarations"

    Private _internalDate As System.DateTime

#End Region

#Region "Properties"

    ''' <summary>
    ''' Privately gets or sets the internal date. When set IsNull property is set to false.
    ''' </summary>
    ''' <value>The internal date.</value>
    Private Property InternalDate() As System.DateTime
        Get
            Return Me._internalDate
        End Get
        Set(ByVal value As System.DateTime)
            Me._internalDate = value
            If Me.IsNull Then
                Me.SetIsNull(False)
            End If
        End Set
    End Property

#End Region

End Class
We will provide an overridden ToString() method which will return the default ToString() value for either the System.DateTime object or the System.DBNull.Value, depending if our object is in a Null state or not.
#Region "Methods"

    ''' <summary>
    ''' Converts the value of this instance to its equivalent string representation.
    ''' </summary>
    ''' <returns>
    ''' A <see cref="System.String"> that represents this instance.
    ''' </returns>
    Public Overrides Function ToString() As String
        If Me.HasValue Then
            Return Me.InternalDate.ToString
        Else
            Return DBNull.Value.ToString
        End If
    End Function

#End Region

We will now look at the constructors we wish to use to allow creation of our object. First we'll look at the default constructor.This constructor initialises the base object, and then calls the base object's SetIsNull() method to set a null state.
#Region "Constructors"

    ''' <summary>
    ''' Initializes a new instance of the  class, with IsNull = true.
    ''' </summary>
    Public Sub New()
        MyBase.New()
        Mybase.SetIsNull(True)
    End Sub
    
#End Region

In Part 2, we'll look at adding more constructors that we will need when creating casting operators later.
Wednesday, January 04, 2012 1:20:09 PM (GMT Standard Time, UTC+00:00)  #    Comments [0] -
.Net | Asp.Net | DateTime | Nullable | VB.Net
Archive
<February 2012>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910
Blogroll
 Clemens Vasters
 Harry Pierson
Passion * Technology * Ruthless Competence
 Joshua Flanagan
A .NET Software Developer
 Michael Schwarz's Blog
Developing applications on the Microsoft platform since Windows 3.1!
 Omar Shahine
Yet another Microsoft blogger
 Scot GU
Scott Guthrie lives in Seattle and builds a few products for Microsoft
 Scott Hanselman
Programming Life and the Zen of Computers
 Tom Mertens
Tom's corner
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2012
Duane Wingett
Sign In
Statistics
Total Posts: 40
This Year: 5
This Month: 1
This Week: 0
Comments: 39
Themes
Pick a theme:
All Content © 2012, Duane Wingett
DasBlog theme 'Business' created by Christoph De Baene (delarou)