Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

A Time Class to Manipulate Time Values

3.77/5 (3 votes)
7 Jul 2013Ms-PL5 min read 26.2K   294  
I needed a simple class to handle adding and subtracting CD song length times, and ended up with a gigantic class

Introduction

I was putting together a program to keep track of CDs. I needed a way to store the length of each track, and I tried using a DateTime variable to hold the values. Needless to say, the DateTime data type just wasn't cutting it. I did a search for a Class I could use to hold the values, but came up empty. There were a lot of people recommending using TimeSpan, but it was wholly useless for what I needed. So I started small, creating a Time data type that would hold hours, minutes and seconds. Then I realized I might have negative amounts when subtracting values, so I added a Sign. Then, things just spiraled out of control...

Background

I'm putting together a training course for Visual Basic and Visual C#. I need multiple example programs, so I've been writing them continuously for the last two months or so. Well, the over-arching project is a CD tracking program that will be expanded and enhanced as each section of the training is finished.

In Part 1: Fundamentals, I have the Console program using a Structure that holds the Artist Name and Album Title, and it saves the data to a text file.
In Part 2: Object Oriented Programming, I upgrade the program to use classes and XML files. This is where this code comes in. Part 2 stores not only Artist and Album, but also Song Titles and Lengths, year of Release and computes Total Length.
In Part 3: GUI Programming, I will convert the program to a GUI interface and add album covers and lyrics.
Finally, in Part 4: Database Programming, I will upgrade again, and convert the whole thing to use a database.

Using the Code

Add the Time class to your project and it exposes the following:

Properties

  • Hours - An Integer value holding the hours portion
  • Minutes - An Integer value holding the minutes portion. Range: 0 (zero) to 59 (fifty-nine)
  • Seconds - An Integer value holding the seconds portion. Range: 0 (zero) to 59 (fifty-nine)
  • Sign - An Integer value holding the sign value of the time. Range: -1 for negative numbers, 0 for zero, +1 for positive numbers

Constructors

  • New() - Parameterless constructor. Sets a default value of +00:00:00.
  • New(seconds) - Parameterized constructor. Will convert the seconds to hours, minutes and seconds.
  • New(minutes, seconds) - Parameterized constructor. Will create a value of the form S00:MM:SS where the first S is the sign of the number.
  • New(hours, minutes, seconds) - Parameterized constructor. Will create a value of the form SHH:MM:SS where the first S is the Sign of the number.
  • New(sign, hours, minutes, seconds) - Parameterized constructor. Like previous constructor, but you can explicitly set the Sign.
  • New(string) - Parameterized constructor. Will parse the String, using the colon (:) as a delimiter. Will fill the value from right to left if parameters are missing from the String.
  • New(string, delimeter) - Parameterized constructor. Like previous constructor, but you can give an explicit delimiter to use for parsing the String.

Relational Operators

The relational operators you can use are:

  • CompareTo(time) - Implements System.IComparable(Of Time).CompareTo
  • Compare(time, time) - Implements System.Collections.Generic.IComparer(Of Time).Compare
  • Equals1(time) - Implements System.IEquatable(Of Time).Equals
  • GetHashCode() - Because we have an Equals() method
  • < (less than) > (greater than) <= (less than or equal) >= (greater than or equal) = (equal) <> (not equal) - All the relational operators that make sense are included

Overloaded Operators

The overloaded mathematical operators you can use are:

  • - (Negate) - returns the negative value of positive time, positive value of negative time
  • + (Addition) - (Subtraction) * (Multiplication) / (Division) - The whole reason I wrote the class in the first place
  • ToShort ToUShort ToInt16 ToUInt16 ToInteger ToUInteger ToInt32 ToUInt32 ToLong ToUlong ToInt64 ToUint64 - Standard conversion methods
  • ToSingle ToDouble ToDecimal - The whole part of these numbers are converted to hours and minutes, while the seconds are from the decimal part (i.e. 30 seconds = .5)
  • ToTime() and CType() - Will convert between pretty much everything else it can

Miscellaneous Methods

The other mathematical operations you can use are:

  • Abs() - Absolute value
  • Min() and Max() - Minimum and Maximum of two values
  • Ceiling(), Floor() and Round() - These work like the standard rounding methods, and round to the minute
  • ToString() - Outputs either #0:00:00 or -#0:00:00

On the possible narrowing conversions, I used some range checking and throw an OverflowException if it is out of range. I used standard exceptions so you could easily incorporate the code into your programs.

VB
Public Shared Function ToShort(ByVal time As Time) As Short
    Dim temp As Integer = time.Sign * ((time.Hours * 3600) + (time.Minutes * 60) + time.Seconds)
    If temp < -32768 Or temp > 32767 Then
        Throw New OverflowException
    End If
    Return CShort(temp)
End Function

I realized that trying to compare two Time values would be tedious and overly verbose, so in the Compare() methods, I convert the Time to Integer and then compare them as integers:

VB
Public Function CompareTo(other As Time) As Integer _
    Implements System.IComparable(Of Time).CompareTo
    Dim time1, time2 As Integer
    time1 = Me.Sign * ((Me.Hours * 3600) + (Me.Minutes * 60) + Me.Seconds)
    time2 = other.Sign * ((other.Hours * 3600) + (other.Minutes * 60) + other.Seconds)
    Return time1.CompareTo(time2)
End Function

And in the Addition/Subtraction/Multiplication methods, I convert them to Integers, do the math, then convert them back to Time:

VB
Public Shared Operator +(ByVal time1 As Time, ByVal time2 As Time) As Time
    Dim temp As Integer
    temp = ToInteger(time1) + ToInteger(time2)
    Return ToTime(temp)
End Operator

And the ToTime method:

VB
Public Shared Function ToTime(ByVal value As Integer) As Time
    Dim hours, minutes, seconds As Integer
    hours = value \ 3600
    seconds = value Mod 3600
    minutes = seconds \ 60
    seconds = seconds Mod 60
    Return New Time(Math.Sign(value), hours, minutes, seconds)
End Function

But in the Division method, I convert them to Decimal first, then do the division:

VB
Public Shared Operator /(ByVal time1 As Time, ByVal time2 As Time) As Time
    Dim temp As Decimal
    temp = ToDecimal(time1) / ToDecimal(time2)
    Return ToTime(temp)
End Operator

It would be an odd result otherwise.

A Quick Test

I tested this with the small program below. It passed for the limited tests I've done, but extensive testing might (and probably will) reveal flaws. If you find one, please let me know!

VB
Module TestTimeClass
    Sub Main()
        Dim i As Time
        Dim j As New Time(275)
        Dim k As New Time("4:15")
        i = j + k
        Console.WriteLine(i.ToString())
        i = k - j
        Console.WriteLine(i.ToString())
        Console.WriteLine(Time.ToDecimal(i))
        If k < j Then
            Console.WriteLine("k is smaller than j.")
        Else
            Console.WriteLine("j is smaller than k.")
        End If
        Console.ReadLine()
    End Sub
End Module

Points of Interest

I tried to squeeze the code to as small as I could get it without driving myself mad. That's why so many places call other places, mainly because I not only hate typing, but because I also know if code works for one routine, re-using it for another I'm assured it will work there, too. I started small: just wanting to add and subtract time values. Then, as I added one little thing to make the code work just a little bit better, eventually I found I had a huge Time Class that would handle just about anything you'd need to do with Time values.

I'm posting it here so you can hopefully use it in your own programs, but also to see how I could improve it, expand it, and/or optimize it.

Any questions are very welcome!

History

  • 2013-07-04 - Version 0.0.0.1 (pre-pre-pre-alpha)
  • 2013-07-07 - Added the C# version of the Time Class

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)