0
\$\begingroup\$

Consider we have to pay the usage of a machine with the following hourly cost:

  • 00:00 - 01:00 --> $4 per hour
  • 01:00 - 05:00 --> $1 per hour
  • 05:00 - 22:00 --> $2 per hour
  • 22:00 - 24:00 --> $3 per hour

If we use that machine from a date of x to another date of y with the given weighted time interval w,

var w = new List<Tuple<TimeSpan, double>>
{
    new Tuple<TimeSpan, double>(new TimeSpan(1,0,0),1),// hourly cost is 1 from 01:00 to the next boundary
    new Tuple<TimeSpan, double>(new TimeSpan(5,0,0),2),// hourly cost is 2 from 05:00 to the next boundary
    new Tuple<TimeSpan, double>(new TimeSpan(22,0,0),3),// hourly cost is 3 from 22:00 to the next boundary
    new Tuple<TimeSpan, double>(new TimeSpan(24,0,0),4)// hourly cost is 4 from 00:00 to the next boundary

};

the following is my attempt at calculating the total cost between two dates based on a weighted time interval scheme. Please kindly review it and make it much better.

class Program
{
    // x cannot be later than y.
    // the TimeSpan of w must be in ascending order

    static double Total(DateTime x, DateTime y, List<Tuple<TimeSpan, double>> w)
    {
        double sum = 0;

        int days = (int)(y - x).TotalDays;

        if (days > 0)
            sum += Sum(new TimeSpan(), new TimeSpan(24, 0, 0), w) * days;

        sum += Sum(x.TimeOfDay, y.TimeOfDay, w);

        return sum;
    }

    // x may be later than y.
    // the TimeSpan of w must be in ascending order

    static double Sum(TimeSpan x, TimeSpan y, List<Tuple<TimeSpan, double>> w)
    {
        double sum = 0;

        if (x > y)
        {
            sum += Sum(x, new TimeSpan(24, 0, 0), w);
            sum += Sum(new TimeSpan(0, 0, 0), y, w);
            return sum;
        }


        w.Insert(0, new Tuple<TimeSpan, double>(new TimeSpan(0, 0, 0), w.Last().Item2));

        for (int i = 0; i < w.Count - 1; i++)
        {
            if (x >= w[i + 1].Item1)
                continue;
            else
            {
                for (int j = i; j < w.Count - 1; j++)
                {
                    if (y >= w[j + 1].Item1)
                    {
                        if (i == j)
                            sum += (w[j + 1].Item1 - x).TotalHours * w[j].Item2;
                        else
                            sum += (w[j + 1].Item1 - w[j].Item1).TotalHours * w[j].Item2;
                        continue;
                    }
                    else
                    {
                        if (i == j)
                            sum += (y - x).TotalHours * w[j].Item2;
                        else
                            sum += (y - w[j].Item1).TotalHours * w[j].Item2;
                        break;
                    }
                }
                break;
            }
        }

        w.RemoveAt(0);
        return sum;
    }


    static void Print(DateTime start, DateTime stop, List<Tuple<TimeSpan, double>> w)
    {
        Console.WriteLine($"x = {start:yyyy/MM/dd hh:mm:ss:fff  tt}\ny = {stop:yyyy/MM/dd hh:mm:ss:fff  tt}\ncost = {Total(start, stop, w)}\n");
    }

    static void Main(string[] args)
    {
        var w = new List<Tuple<TimeSpan, double>>
        {
            new Tuple<TimeSpan, double>(new TimeSpan(1,0,0),1),// hourly cost is 1 from 01:00 to the next boundary
            new Tuple<TimeSpan, double>(new TimeSpan(5,0,0),2),// hourly cost is 2 from 05:00 to the next boundary
            new Tuple<TimeSpan, double>(new TimeSpan(22,0,0),3),// hourly cost is 3 from 22:00 to the next boundary
            new Tuple<TimeSpan, double>(new TimeSpan(24,0,0),4)// hourly cost is 4 from 00:00 to the next boundary
        };


        Print(new DateTime(2016, 1, 1, 0, 30, 0, 1), new DateTime(2016, 1, 2, 0, 30, 0, 0), w);
        Print(new DateTime(2016, 1, 1, 0, 30, 0, 1), new DateTime(2016, 1, 3, 0, 30, 0, 0), w);

        Print(new DateTime(2016, 1, 1, 23, 0, 0, 0), new DateTime(2016, 1, 2, 22, 59, 59, 999), w);
        Print(new DateTime(2016, 1, 1, 23, 0, 0, 0), new DateTime(2016, 1, 3, 22, 59, 59, 999), w);

        Print(new DateTime(2016, 1, 1, 0, 0, 0, 0), new DateTime(2016, 1, 2, 0, 0, 0, 0), w);


        Print(new DateTime(2016, 1, 1, 5, 0, 0, 0), new DateTime(2016, 1, 1, 22, 0, 0, 0), w);
        Print(new DateTime(2016, 1, 1, 5, 0, 0, 0), new DateTime(2016, 1, 2, 22, 0, 0, 0), w);

        Print(new DateTime(2016, 1, 1, 0, 0, 0, 0), new DateTime(2016, 1, 1, 0, 0, 0, 0), w);
        Print(new DateTime(2016, 1, 1, 0, 0, 0, 0), new DateTime(2017, 1, 1, 0, 0, 0, 0), w);

    }


}

enter image description here

\$\endgroup\$
  • 2
    \$\begingroup\$ Your 'Question' is off-topic, we can review the rest. \$\endgroup\$ – Mast Aug 7 '16 at 10:03
  • \$\begingroup\$ instead of your Print function calls use unit tests \$\endgroup\$ – Disappointed Aug 8 '16 at 12:02
2
\$\begingroup\$

Create a Class for the DateTime pair

Make the constructor ensure the pair is in the correct order and the "x > y" type code goes away.

And the constructor will check for null, strip milliseconds, etc. Client code can then presume clean data.

Override CompareTo so sorting to proper order is trivial. And even more code goes away.

This class should contain its own Sum()


Create a class for the List of DateTime pairs

This class will keep its elements in order. More code goes away.

This class should contain its own Sum().


Variable Names!

The sheer complexity of Sum demands meaningful variable names.


Ambiguous Weighted Ranges

Is exactly 1 hour charged at $1/hr or $2/hr? Ditto for 5 and 22 hours. What about more than 24 hrs?

Don't say "just read the code." I'll just say the code is wrong, or the documentation is wrong. I can't tell which.


'rithmatic

Use decimal for money calculations. A quote of a quote from an SO question

The Decimal value type is appropriate for financial calculations requiring large numbers of significant integral and fractional digits and no round-off errors. The Decimal type does not eliminate the need for rounding. Rather, it minimizes errors due to rounding.

Display results consistently.

\$\endgroup\$
  • \$\begingroup\$ For ambiguous Weighted Ranges: See the output at the end, one year cost is presented. \$\endgroup\$ – Money Oriented Programmer Aug 7 '16 at 22:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.