iVikD
iVikD

Reputation: 296

How to calculate a price based on time passed in c#?

I'm currently working on an app that calculates the price a user has to pay for a service based on how much time he's spent using said service. The fee is $3.30 for the first hour and then $1.15 for every half hour after that. My timer looks something like this:

    private void timer()
    {
        DispatcherTimer timer = new DispatcherTimer();

        timer.Tick +=
            delegate(object s, EventArgs args)
            {
                TimeSpan time = (DateTime.Now - StartTime);

                this.Time.Text = string.Format("{0:D2}:{1:D2}:{2:D2}", time.Hours, time.Minutes, time.Seconds);
            };
        timer.Interval = new TimeSpan(0, 0, 1); // one second
        timer.Start();
    }

The point being that the timer AND the price to pay should be shown on screen and update automatically with the passing of time (the timer already does that.)

As for the price itself, I thought of using a combination of if/else and foreach, but so far, I've accomplished nothing...

Upvotes: 0

Views: 1627

Answers (3)

CodesInChaos
CodesInChaos

Reputation: 108790

A few notes:

  1. Use UTC times, not local times (See Eric Lippert's comment)
  2. Use decimal and not double to represent money
  3. To calculate started time intervals, Math.Ceiling is great. No need to jump through hoops to get Math.Round to do what you want.
  4. Encapsulate your pricing logic into a simple side-effect free method that doesn't interact with external services, such as the clock.

    This makes it easy to test the method in isolation.

  5. Use a single timer for all periodic UI updates (unless they require different intervals)

I'd write it like this:

public decimal CostByTime(TimeSpan t)
{
  if(t < TimeSpan.Zero)
      throw new ArgumentOutOfRangeExeception("t", "Requires t>=0");
  if(t ==  TimeSpan.Zero)
      return 0;
  double hours = t.TotalHours;
  if(hours <= 1)
      return 3.30m;
  else
      return 3.30m + (int)Math.Ceiling((hours-1)*2) * 1.15m
}

And then in your view you can use:

TimeSpan billableTime = DateTime.UtcNow - StartTime;
decimal cost = CostByTime(billableTime);

Time.Text = billableTime.ToString(...);
Cost.Text = cost.ToString();

Upvotes: 1

odyss-jii
odyss-jii

Reputation: 2699

If the scheme is such that the cost is added as soon as a segment starts then you can calculate the number of half hours that have started, after the initial hour, as:

Math.Round((hours - 1) / 0.5 + 0.5)

And the cost is then calculated as:

double hours = (DateTime.UtcNow - StartTime).TotalHours;
double cost;
if (hours < 1)
    cost = 3.30;
else
    cost = 3.30 + Math.Round((hours - 1) / 0.5 + 0.5) * 1.15;

Upvotes: 1

Jonathan Wood
Jonathan Wood

Reputation: 67193

Something like this. (You left out how partial hours are handled so I ignored them.)

double span = (DateTime.Now - StartTime).TotalHours;
decimal cost = 0.0;
if (span > 0)
    cost = 3.30 + ((span - 1) * 1.15);

Upvotes: 2

Related Questions