The Essential .NET Data Types

  • 8/15/2011

The Date Data Type

The Date data type stores and manipulates date values. It helps you to calculate time differences, parse date values from strings, and convert date values into formatted strings.

.NET data type: System.DateTime

Represents: Dates and time from 1.1.0001 (0 hours) to 31.12.9999 (23:59:59 hours) with a resolution of 100 nanoseconds (this unit is called a tick)

Type literal: Enclosed in pound-signs, always United States culture format (#MM/dd/yyyy HH:mm:ss#)

Memory requirements: 8 bytes

For the United States, you write date values in the format month/day/year, separated by a slash. This syntax can easily cause confusion if you are not familiar with it. For example, the date 12/17/2011 is clearly a United States date, because there is no month 17. However, the date 12/06/2011 could be interpreted as either June 12th or December 6th. There is a similar issue with the time of day. In the United States, you might find a 24-hour display for a bus schedule or in the military, but otherwise, the postfixes “AM” (for “ante meridian”—before noon) and “PM” (for “post meridian”—after noon) defines which 3 o’clock is intended. The formatting scheme becomes even more problematic with 12:00 (there is no 0:00!). Maybe you’ve had your own experience when trying to program a video recorder that recorded not your desired TV program, but another that was broadcast 12 hours later (or earlier). 12:00 AM corresponds to midnight or the 0:00 hour on the 24-hour clock; 12:00 PM corresponds to noon.

Value assignments to a Date data type in program code occur by surrounding the date/time string characters with “#” (hash) characters. The following example shows how this works:

Dim Midnight As Date = #12:00:00 AM#
Dim Noon As Date = #12:00:00 PM#
Dim NewYearsEve As System.DateTime = #12/31/2010#
Dim TimeForChampagne As System.DateTime = #12/31/2010 11:58:00 PM#
Dim TimeForAspirin As System.DateTime = #1/1/2011 11:58:00 AM#

The editor helps you to find the correct format by translating the 24-Hour format into the 12-hour format, automatically. For example, it converts the expression #0:00# to #12:00:00 AM# automatically.

It also adds missing entries for minutes and seconds if you inadvertently enter a value that contains only the hours portion. You can enter times in the 24-hour format; the editor will automatically convert them to the 12-hour format.

TimeSpan: Manipulating Time and Date Differences

What’s unique about the Date data type is that it supports calculations to determine time differences, representing a length of time with TimeSpan objects. These objects represent time intervals, not time values. Unlike the Date type, TimeSpan is not a .NET base data type.

The TimeSpan data type is quite easy to use. You can subtract one data value from another to determine the time span between the two dates, or add a time span to a date, or subtract it from a date (see the following example) to calculate the date after so many months, days or hours.

Dim locDate1 As Date = #3:15:00 PM#
Dim locDate2 As Date = #4:23:32 PM#
Dim locTimeSpan As TimeSpan = locDate2.Subtract(locDate1)
Console.WriteLine("The time span between {0} and {1} is", _
                  locDate1.ToString("HH:mm:ss"), _
                  locDate2.ToString("HH:mm:ss"))
Console.WriteLine("{0} second(n) or", locTimeSpan.TotalSeconds)
Console.WriteLine("{0} minute(n) and {1} second(n) or", _
                   Math.Floor(locTimeSpan.TotalMinutes), _
                   locTimeSpan.Seconds)
Console.WriteLine("{0} hour(s), {1} minute(s) and {2} second(s) or", _
                   Math.Floor(locTimeSpan.TotalHours), _
                   locTimeSpan.Minutes, locTimeSpan.Seconds)
Console.WriteLine("{0} Ticks", _
                   locTimeSpan.Ticks)

A Library with Useful Functions for Date Manipulation

In the same example, you will find a class file called DateCalcHelper.vb that contains a static class of the same name. This class provides some useful functions that simplify the calculation of certain relative points in time, and shows how to perform calculations with date values.

Thanks to the XML comments in the example, the class is self-explanatory. When you develop you own programs that make intensive use of relative point-in-time calculations, just add this code file to your project (or the assembly of your project).

The following code shows the function names along with their explanations (in bold):

Public NotInheritable Class DateCalcHelper

    ''' <summary>
    ''' Calculates the date which corresponds to the 1st of the month,
    ''' which results from the specified date.
    ''' </summary>
    ''' <param name="CurrentDate">Date, whose month the calculation is based on.
httpatomoreillycomsourcemspimages882161.jpg</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function FirstDayOfMonth(ByVal CurrentDate As Date) As Date
        Return New Date(CurrentDate.Year, CurrentDate.Month, 1)
    End Function

    ''' <summary>
    ''' Calculates the date which corresponds to the last day of the month,
    ''' which results from the specified date.
    ''' </summary>
    ''' <param name="CurrentDate">Date, whose month the calculation is based on.
httpatomoreillycomsourcemspimages882161.jpg</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function LastDayOfMonth(ByVal CurrentDate As Date) As Date
        Return New Date(CurrentDate.Year, CurrentDate.Month, 1).AddMonths(1).
httpatomoreillycomsourcemspimages882161.jpgAddDays(-1)
    End Function

    ''' <summary>
    ''' Calculates the date which corresponds to the first of the year,
    ''' which results from the specified date.
    ''' </summary>
    ''' <param name="CurrentDate">Date, whose year the calculation is based on.
    ''' </param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function FirstOfYear(ByVal CurrentDate As Date) As Date
        Return New Date(CurrentDate.Year, 1, 1)
    End Function

    ''' <summary>
    ''' Calculates the date which corresponds to the first Monday of the first week of
    ''' the month, which results from the specified date.
    ''' </summary>
    ''' <param name="CurrentDate">Date, whose week the calculation is based on.
    ''' </param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function MondayOfFirstWeekOfMonth(ByVal CurrentDate As Date) As Date
        Dim locDate As Date = FirstDayOfMonth(CurrentDate)
        If Weekday(locDate) = DayOfWeek.Monday Then
            Return locDate
        End If
        Return locDate.AddDays(6 - Weekday(CurrentDate))
    End Function

    ''' <summary>
    ''' Calculates the date which corresponds to the Monday of the week,
    ''' which results from the specified date.
    ''' </summary>
    ''' <param name="CurrentDate">Date, whose week the calculation is based on.
    ''' </param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function MondayOfWeek(ByVal CurrentDate As Date) As Date
        If Weekday(CurrentDate) = DayOfWeek.Monday Then
            Return CurrentDate
        Else
            Return CurrentDate.AddDays(-Weekday(CurrentDate) + 1)
        End If
    End Function

    ''' <summary>
    ''' Calculates the date which corresponds to the first Monday
    ''' of the second week of the month,
    ''' which results from the specified date.
    ''' </summary>
    ''' <param name="CurrentDate">Date, whose week the calculation is based on.
    ''' </param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function MondayOfSecondWeekOfMonth(ByVal currentDate As Date) As
httpatomoreillycomsourcemspimages882161.jpgDate
        Return MondayOfFirstWeekOfMonth(currentDate).AddDays(7)
    End Function

    ''' <summary>
    ''' Calculates the date which corresponds to Monday of the last week of the month,
    ''' which results from the specified date.
    ''' </summary>
    ''' <param name="CurrentDate">Date, whose week the calculation is based on.
    ''' </param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function MondayOfLastWeekOfMonth(ByVal CurrentDate As Date) As Date
        Dim locDate As Date = FirstDayOfMonth(CurrentDate).AddDays(-1)
        If Weekday(locDate) = DayOfWeek.Monday Then
            Return locDate
        End If
        Return locDate.AddDays(-Weekday(CurrentDate) + 1)
    End Function

    ''' <summary>
    ''' Results in the date of the next work day.
    ''' </summary>
    ''' <param name="CurrentDate">Date the calculation is based on</param>
    ''' <param name="WorkOnSaturdays">True, if Saturday is a work day.</param>
    ''' <param name="WorkOnSundays">True, if Sunday is a work day.</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function NextWorkday(ByVal CurrentDate As Date,
             ByVal WorkOnSaturdays As Boolean, _
             ByVal WorkOnSundays As Boolean) As Date
        CurrentDate = CurrentDate.AddDays(1)
        If Weekday(CurrentDate) = DayOfWeek.Saturday And Not WorkOnSaturdays Then
            CurrentDate = CurrentDate.AddDays(1)
        End If
        If Weekday(CurrentDate) = DayOfWeek.Sunday And Not WorkOnSundays Then
            CurrentDate = CurrentDate.AddDays(1)
        End If
        Return CurrentDate
    End Function

    ''' <summary>
    ''' Results in the date of the previous work day.
    ''' </summary>
    ''' <param name="CurrentDate">Date the calculation is based on</param>
    ''' <param name="WorkOnSaturdays">True, if Saturday is a work day.</param>
    ''' <param name="WorkOnSundays">True, if Sunday is a work day.</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function PreviousWorkday(ByVal CurrentDate As Date,
            ByVal WorkOnSaturdays As Boolean, _
            ByVal WorkOnSundays As Boolean) As Date
        CurrentDate = CurrentDate.AddDays(-1)
        If Weekday(CurrentDate) = DayOfWeek.Sunday And Not WorkOnSundays Then
            CurrentDate = CurrentDate.AddDays(-1)
        End If
        If Weekday(CurrentDate) = DayOfWeek.Saturday And Not WorkOnSaturdays Then
            CurrentDate = CurrentDate.AddDays(-1)
        End If
        Return CurrentDate
    End Function
End Class

Converting Strings to Date Values

Just like the base numeric data types, you can also convert strings that represent date values into a Date data type. The Date data type provides two functions, Parse and ParseExact, that analyze a string and build the actual date value from it.

Conversions with Parse

When using Parse, the parser uses every trick in the book to convert a date, a time, or a combination of both into a time value, as shown in the following example:

Dim locToParse As Date
locToParse = Date.Parse("13.12.10") ' OK, basic European setting is processed.
locToParse = Date.Parse("6/7/10")   ' OK, but European date is used in spite of "/".
locToParse = Date.Parse("13/12/10") ' OK, as above.
locToParse = Date.Parse("06.07")    ' OK, is extended by the year.
locToParse = Date.Parse("06,07,10") ' OK, comma is acceptable.
locToParse = Date.Parse("06,07")    ' OK, comma is acceptable; year is added.
'locToParse = Date.Parse("06072010") ' --> Exception: was not recognized as a valid date!
'locToParse = Date.Parse("060705")   ' --> Exception: was not recognized as a valid date!
locToParse = Date.Parse("6,7,4")    ' OK, comma is acceptable; leading zeros are added.

locToParse = Date.Parse("14:00")    ' OK, 24-hour display is acceptable.
locToParse = Date.Parse("PM 11:00") ' OK, PM may be in front of...
locToParse = Date.Parse("11:00 PM") ' ...and behind the time specification.
'locToParse = Date.Parse("12,00 PM") ' --> Exception: was not recognized as a valid date!

'Both date/time combinations work:
locToParse = Date.Parse("6.7.10 13:12")
locToParse = Date.Parse("6,7,10 11:13 PM")

As you can see here, a format entry that is very common in European locales is not recognized: when the individual value groups of the date are written sequentially but without a separating character. However, there is a solution to this problem as well.

Conversion with ParseExact

If, in spite of all its flexibility, Parse fails to recognize a valid date/time format, you can still set a recognition pattern for the entry by using the method ParseExact for string conversions.

This is especially true when you need to differentiate between time and date values. For a field in which the users of your program must enter a time, your program knows, for example, that the value 23:12 refers to the time 23:12:00, and not to the date 23.12.2000. Parse wouldn’t work here, because it can’t recognize the context.

Apart from the string to be analyzed, ParseExact requires at least two additional parameters: a string that contains the specific recognition pattern, and a format provider that provides further formatting requirements. There are several different format providers that you can use—but you can access them only after inserting the following line, which imports the required namespace at the beginning of your module or class file:

Imports System.Globalization

The simplest version that will recognize a time entry as the time of day, if it has been entered in the above format, it would look like this:

locToParse = Date.ParseExact("12,00", "HH,mm", CultureInfo.CurrentCulture)

The string “HH” specifies that hours are expressed in the 24-hour format. If you use the lower-case pattern “hh” instead, the parser will recognize only the 12-hour format. Next, the input contains a comma, which becomes the separator character, and finally the format specifies that minutes come last, using the string “mm.”

In practice, it’s rare that users follow specific requirements; therefore, your program should ideally recognize several different versions of time entries. With the ParseExact function, you can specify a range of possible formats for the parser to perform the conversion. All you need to do is define a String array containing the permitted formats, and then pass it to the ParseExact method along with the string to be parsed. If you decide to use this method, however, you also need to specify a parameter that regulates the parsing flexibility (for example, if the input strings that will be analyzed are allowed to contain whitespace, which then will be ignored). The entry is regulated by a parameter of the type DateTimeStyles which allows the settings listed in Table 6-5.

Table 6-5 The Extended Settings That Can Be Used with Parse

Member name

Description

Value

AdjustToUniversal

Date and time must be converted to Universal Time or Greenwich Mean Time (GMT)

16

AllowInnerWhite

Additional whitespaces within the string are ignored during parsing, unless the DateTimeFormatInfo format patterns contain spaces

4

AllowLeadingWhite

Leading whitespaces are ignored during parsing, unless the DateTimeFormatInfo format patterns contain spaces

1

AllowTrailingWhite

Trailing whitespaces are ignored during parsing, unless the DateTimeFormatInfo format patterns contain spaces

2

AllowWhiteSpaces

Additional whitespaces, which are located at any position within the string, are ignored during parsing, unless the DateTimeFormatInfo format patterns contain spaces. This value is equivalent to the combination of AllowLeadingWhite, AllowTrailingWhite, and AllowInnerWhite

7

NoCurrentDateDefault

Date and time are inseparately combined in the Date data type. Even if only a time is assigned, the Date value will always show a valid date. This setting specifies that the DateTime.Parse method and the DateTime.ParseExact method use a date according to the Gregorian calendar with year = 1, month = 1, and day = 1, when the string only contains the time, but not the date. If this value isn’t specified, the current date is used.

8

None

Specifies that the default formatting options must be used; for instance, the default format for DateTime.Parse, and DateTime.ParseExact.

0

The following lines of code show how to use ParseExact to convert strings into date values with specific requirements for date formats:

Imports System.Globalization

Module Module1

    Sub Main()

        Dim locToParseExact As Date
        Dim locTimePattern As String() = {"H,m", "H.m", "ddMMyy", "MM\/dd\/yy"}

        'Works: it's in the time pattern.
        locToParseExact = Date.ParseExact("12,00", _
                            locTimePattern, _
                            CultureInfo.CurrentCulture, _
                            DateTimeStyles.AllowWhiteSpaces)

        'Works: it's in the time pattern, and whitespaces are permitted.
        locToParseExact = Date.ParseExact(" 12 , 00 ", _
                            locTimePattern, _
                            CultureInfo.CurrentCulture, _
                            DateTimeStyles.AllowWhiteSpaces)

        'Doesn't work: it's in the time pattern, but whitespaces are not permitted.
        'locToParseExact = Date.ParseExact(" 12 , 00 ", _
        '                    locTimePattern, _
        '                    CultureInfo.CurrentCulture, _
        '                    DateTimeStyles.None)

        'Works: it's in the time pattern.
        locToParseExact = Date.ParseExact("1,2", _
                            locTimePattern, _
                            CultureInfo.CurrentCulture, _
                            DateTimeStyles.None)
        'Works: it's in the time pattern.
        'But the date corresponds to 1.1.0001 and is therefore
        'not displayed as a Tooltip, contrary to all the other
        'examples shown here.
        locToParseExact = Date.ParseExact("12.2", _
                            locTimePattern, _
                            CultureInfo.CurrentCulture, _
                            DateTimeStyles.NoCurrentDateDefault)

        'Works: it's not in the time pattern, because seconds are used
        'locToParseExact = Date.ParseExact("12,2,00", _
        '                    locTimePattern, _
        '                    CultureInfo.CurrentCulture, _
        '                    DateTimeStyles.NoCurrentDateDefault)

        'Doesn't work: the colon is not in the time pattern.
        'locToParseExact = Date.ParseExact("1:20", _
        '                    locTimePattern, _
        '                    CultureInfo.CurrentCulture, _
        '                    DateTimeStyles.None)

        'Now it works, because it's used as date in the time pattern.
        '(third element in the string array)
        locToParseExact = Date.ParseExact("241205", _
                            locTimePattern, _
                            CultureInfo.CurrentCulture, _
                            DateTimeStyles.AllowWhiteSpaces)

        'Works: US format is used,
        'as defined by the slashes and the group order.
        '(fourth element in the string array).
        locToParseExact = Date.ParseExact("12/24/05", _
                            locTimePattern, _
                            CultureInfo.CurrentCulture, _
                            DateTimeStyles.AllowWhiteSpaces)
    End Sub

End Module

When you define slashes as group separators, remember to always put a backslash in front so the separators aren’t processed as control characters.