2

I have a series of times that are coming to me as strings from a web service. The times are formated as HH:MM:SS:000 (3 milisecond digits). I need to compare two times to determine if one is more than twice as long as the other:

if ( timeA / timeB > 2 )

What's the simplest way to work with the time strings?


If I was writing in Python this would be the answer to my question: Difference between two time intervals?

(Except the operator I need is division, not subtraction)


Edit: What I'm really looking for is a way to get the ratio of timeA to timeB, which requires division, not subtraction. Unfortunately, the DateTime structure doesn't appear to have a division operator. Updated the question title and body to reflect this.


Solution:

Based on the answer I picked below, which was the simplest of all the proposed methods so far, here is the working solution:

DateTime timeA;
DateTime timeB;
DateTime.TryParse(webServiceTimeString_A, out timeA);
DateTime.TryParse(webServiceTimeString_B, out timeB);

// TimeA is more than twice the duration of TimeB.
if ( (double)timeA.TimeOfDay.Ticks / (double)timeB.TimeOfDay.Ticks > 2.0f )
{
    // Do stuff.
}
else
{
    // Do different stuff.
}

JavaScript:

Recently, this functionality was also required in JavaScript for an AJAX call, so, I had to write a conversion function after all (just not in C#). In case it's needed:

if (_timeInSeconds(timeA) / _timeInSeconds(timeB) > 2) {
    // Do stuff.
}

// Convert HH:MM:SS:000 string to a number of seconds so we can do math on it.
function _timeInSeconds(stringTime) {
    var timeArray = stringTime.split(":");
    var timeInSeconds = 0;

    //HH
    timeInSeconds += (parseInt(timeArray[0], 10) * 3600);

    //MM
    timeInSeconds += (parseInt(timeArray[1], 10) * 60);

    //SS
    timeInSeconds += (parseInt(timeArray[2], 10));

    //Milliseconds
    timeInSeconds += (parseInt(timeArray[3], 10) / 1000);

    return timeInSeconds;
}

Word to the wise: Make sure to specify the second argument of parseInt...

parseInt(string, 10)

...to specify that the string is a Base-10 number. Otherwise, if the string starts with 0 (common in HH:MM:SS formats), JavaScript decides it's a Base-8 number. This causes the strings "08" and "09" to be converted to decimal integer 0 (because 8 and 9 don't exist in Base-8), and the calculations get thrown off.

Community
  • 1
  • 1
Jake
  • 4,829
  • 2
  • 33
  • 44

5 Answers5

3

First you create a DateTime by parsing the string and then the math is easy :)

Note that subtracting two dates with the - operator will return a TimeSpan, check the MSDN docs for what those look like.

Daniel DiPaolo
  • 55,313
  • 14
  • 116
  • 115
3

See the TimeSpan structure and then Calculate period of time with .NET

And actually, your code could be simplified thusly:

    DateTime timeA = DateTime.Now;
    DateTime timeB = DateTime.Now.AddHours(-10.0);

    if ( (double)timeA.TimeOfDay.Ticks / (double)timeB.TimeOfDay.Ticks > 2.0f )
        Console.WriteLine("Time A is more than twice time B");
    else
        Console.WriteLine("Time A is NOT more than twice time B");
Community
  • 1
  • 1
Paul Sasik
  • 79,492
  • 20
  • 149
  • 189
  • It looks like the constructors only take integers. I'd have to split the string by the colon characters into an array of integers and then manually insert each array member as an argument, right? – Jake Jan 11 '11 at 21:54
  • No, your best bet is still to parse the strings into `DateTime` objects and do your math on those. – Jim Brissom Jan 11 '11 at 21:59
  • @Jake: TimeSpan is the result of comparison of two different DateTime objects. You rarely, if ever, create them directly. – Paul Sasik Jan 11 '11 at 22:06
  • FYI: For the ticks example I added a cast to double because int division truncates results (was always value of 2) – Paul Sasik Jan 11 '11 at 23:15
  • Thanks. It worked perfectly. By the way, the format I used for parsing the web service's Strings into DateTimes was **DateTime timeA; DateTime.TryParse(webServiceTimeString, out timeA);** – Jake Jan 13 '11 at 15:40
1

I think the easiest way to parse the strings is with TimeSpan.ParseExact in .Net 4:

    public static bool MoreThanDouble(string t1, string t2)
    {
        const string format = @"%h\:mm\:ss\:fff";
        long ticks1 = TimeSpan.ParseExact(t1, format, null).Ticks,
             ticks2 = TimeSpan.ParseExact(t2, format, null).Ticks;
        return ticks1 - ticks2 > ticks2;
    }

    static void Main(string[] args)
    {
        Console.WriteLine(MoreThanDouble("10:11:12:123", "1:23:45:000"));
        Console.WriteLine(MoreThanDouble("10:11:12:123", "9:23:45:000"));
    }

That will print True False. If you don't have .Net 4, you can use DateTime:

    public static bool MoreThanDouble2(string t1, string t2)
    {
        const string format = @"%h\:mm\:ss\:fff";
        long ticks1 = DateTime.ParseExact(t1, format, null,
             System.Globalization.DateTimeStyles.NoCurrentDateDefault).Ticks,
             ticks2 = DateTime.ParseExact(t2, format, null,
             System.Globalization.DateTimeStyles.NoCurrentDateDefault).Ticks;
        return ticks1 - ticks2 > ticks2;
    }
Gabe
  • 84,912
  • 12
  • 139
  • 238
  • Be careful with using Ticks. It returns long and you'll loose precision when during devision. For example try the following numbers in your test. MoreThanDouble( "02:00:00:001", "01:00:00:000" ) – Aaron Carlson Jan 11 '11 at 22:56
  • Also, The TimeSpan.ParseExact (with the format provided) throws an exception when the hours component is greater then 23. – Aaron Carlson Jan 11 '11 at 23:05
  • @Aaron: I removed the division, but it doesn't matter because there's no loss of precision with it. Also, the OP's phrasing implies that he's actually measuring times, which leads me to believe that his hours will never be more than 23. – Gabe Jan 11 '11 at 23:25
  • @Gabe: the 23 hours is an edge case. I more just wanted the OP to be aware of the limitation of your solution. – Aaron Carlson Jan 12 '11 at 00:32
  • @Gabe - I poorly worded my first comment. You'll drop the decimals when dividing two longs which can give you the wrong answer when seeing if the result is greater than 2. For example : 5L / 2L > 2 == false where as 5D / 2D > 2 == true. The OP may not care either way. – Aaron Carlson Jan 12 '11 at 00:41
  • @Aaron: Yes, I noticed the problem with the division, which is why I removed it. I could have just made it `ticks1 / (double)ticks2` or used `>=` as you did. – Gabe Jan 12 '11 at 00:47
0

Use DateTime.FormatExact() to convert from your string to DateTime, then by differencing them you obtain a TimeSpan to play with.

Felice Pollano
  • 32,832
  • 9
  • 75
  • 115
0

I would just parse the strings into a timespan rather then converting to a DateTime first

Here's a sample of how you could do this:

class Program
{
    static void Main( string[] args )
    {
        Console.WriteLine( ( "02:00:00:001".ToTimeSpan().TotalMilliseconds / "01:00:00:000".ToTimeSpan().TotalMilliseconds ) > 2 );
        Console.WriteLine( ( "02:00:00:001".ToTimeSpan().TotalMilliseconds / "00:60:00:000".ToTimeSpan().TotalMilliseconds ) > 2 );
        Console.WriteLine( ( "02:00:00:000".ToTimeSpan().TotalMilliseconds / "01:00:00:001".ToTimeSpan().TotalMilliseconds ) > 2 );
        Console.WriteLine( ( "25:12:60:002".ToTimeSpan().TotalMilliseconds / "12:12:60:002".ToTimeSpan().TotalMilliseconds ) > 2 );
    }
}

public static class Helpers
{
    public static TimeSpan ToTimeSpan(this string time )
    {
        var split = time.Split( ':' );
        if( split.Length != 4 )
        {
            throw new InvalidOperationException("Invalid format");
        }
        //First posistion is days.
        return new TimeSpan(0, split[ 0 ].ToInt(), split[ 1 ].ToInt(), split[ 2 ].ToInt(), split[ 3 ].ToInt() );
    }

    public static int ToInt( this string str )
    {
        return Convert.ToInt32( str );
    }
}

You could add some more validation to the above code as necessary based on how consistent you can expect the time strings to be.

Aaron Carlson
  • 5,522
  • 4
  • 31
  • 35