C# calculate total time worked with TimeSpan So there is an existing codebase which calculates the total time worked per month.

What the code is doing:


iterates through all the working days entries
retreives the value of net work time which is in format: HH:mm
calculates the minutes out of the net work time string
formats the net work time to the report day row (back again) 
adds the daily calculated minutes to monthly total
formats the total monthly minutes to the report totals row


Problem

The code randomly calculates the data properly.
When data is calculated properly:


the total is calculated properly,
the day rows adds up to the total value


When data is not properly calculated:


the total has always 1h less
every entry which does not have minutes part has 1h less which means that it does not even adds up to the wrongly calculated total


To resolve the problem we obviously need to get rid of type conversion from double (ts.TotalHours) to int

But the questions are:


Why the calculations are wrong randomly (or this is just my impression)?
How to mimic the wrong calculation behavour in unit tests?


Based on this SO thread: https://stackoverflow.com/q/47302758/1498401 it seems that floating point calculation might be platform specific so additional information about server configuration:


There are 2 fronted servers with win 2k12r2


static int totalMinutesCounter { get; set; }

public static string GetFormatedTime(string s, out int minutesCounter)
{
    minutesCounter = 0;

    if (s == null)
    {
        return string.Empty;
    }

    int workedTimeMinutes = GetNumberOfMinutes(s);

    if (workedTimeMinutes &lt; 0)
    {
        throw new ArgumentOutOfRangeException("workedTimeHours", "Negative values not allowed");
    }

    minutesCounter = workedTimeMinutes;

    TimeSpan ts = new TimeSpan(0, workedTimeMinutes, 0);

    return ((int)ts.TotalHours).ToString() + ts.ToString(@"\:mm");
}

static string GetFormatedTimeFromMinutes(int minutes, ref int minutesCounter)
{
    TimeSpan ts = new TimeSpan(0, minutes, 0);

    minutesCounter += minutes;

    return ((int)ts.TotalHours).ToString() + ts.ToString(@"\:mm");
}

private static int GetNumberOfMinutes(string s)
{
    if (string.IsNullOrWhiteSpace(s))
    {
        return 0;
    }

    char[] charSeparators = new char[] { ':' };
    string[] result = s.Split(charSeparators, StringSplitOptions.None);
    string sh = result[0].ToString();
    string sm = result[1].ToString();
    int hh = int.Parse(sh);
    int mm = int.Parse(sm);

    return mm + 60 * hh;
}



My unit test i would like to see failing ;)


[TestMethod]
public void TestMethod1()
{
    List&lt;string&gt; times = new List&lt;string&gt;()
    {
        "11:30","12:00","08:00","09:30","07:00","11:00","09:30","11:00","11:30","07:00","08:00","09:30","10:30","09:00","09:45","08:00","10:15","11:00","10:00"
    };

    for (int i = 0; i &lt; 10000; i++)
    {
        foreach (var s in times)
        {
            int minutesCounter = 0;

            var rowValue = GetFormatedTime(s, out minutesCounter);

            totalMinutesCounter += minutesCounter;
        }

        int temp = 0;
        var result = GetFormatedTimeFromMinutes(totalMinutesCounter, ref temp);
        totalMinutesCounter = 0;

        Assert.AreEqual("184:00", result);
    }
}
```
Why does your code pointlessly reinvent the wheel? Microsoft provides methods to convert strings to DateTimes; why not use those instead of your own clunky GetNumberOfMinutes? Or perhaps even better: convert the string directly to a TimeSpan.

On the subject of GetNumberOfMinutes: why even do string sh = result[0].ToString();? The result of spring.Split is an array of strings, why then the need to convert a string to a string? And that's just one of the things that are wrong with that method, there are several more.



The whole flow of your code is IMHO needlessly complex. Why not simply convert each time to a TimeSpan, thus getting a List&lt;TimeSpan&gt;, then calculate the amount of minutes, add those up, and then convert those to a "readable" format?

I don't even understand why you do this: ((int)ts.TotalHours).ToString() + ts.ToString(@"\:mm");. Surely this can be expressed much more elegantly and readable.



On a general note: use proper, self-explanatory names for your variables, and not ts or sh etc.