1

I made a little parking application in C#. There are some different pricings depending on vehicle type and time zone. Day can be divided into time zones (for example morning, day, evening and night). Now if customer stops parking I want to calculate in which time zones customer has parked and how long.

For example morning time zone starts at 6:00 and ends 12:00, day time zone starts at 12:00 and ends 16:00, evening time zone starts at 16:00 and ends at 23:00 and night time zone starts at 23:00 and ends at 6:00. Customer started parking his car at 00:30 and ends parking at 6:32. Currently I have 4 variables for that: parking start time, parking end time and timezone starting time and timezone ending time.

Second example would be like customer parks 24H, then the parking time has all time zones covered.

How is the simplest way to calculate how many hours and minutes customer parked his car in different time zones?

Regards, evilone

EDIT:

Got this answer from MSDN and post it here so others can learn from it too.

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            DateTime start = new DateTime(2011, 7, 25, 0, 30, 0);
            DateTime end = new DateTime(2011, 7, 26, 6, 32, 0);
            List<DateTime> listTimeZones = CalculateTotalTime(start, end);

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < listTimeZones.Count; i++)
            {
                sb.AppendLine(String.Format("{0}. {1}: from {2} to {3}",
                                            i + 1,
                                            GetZoneInWords(listTimeZones[i].Hour),
                                            String.Format("{0:dd.MM.yyyy hh:mm}", listTimeZones[i]),
                                            (i + 1) < listTimeZones.Count
                                                ? String.Format("{0:dd.MM.yyyy hh:mm}", listTimeZones[i + 1])
                                                : "Parking ended"));
            }
            MessageBox.Show(sb.ToString());
        }

        private List<DateTime> CalculateTotalTime(DateTime start, DateTime end)
        {
            DateTime temp = start;

            int hour = start.Hour;
            int minute = start.Minute;

            int morning = 6;
            int day = 12;
            int evening = 17;
            int night = 23;

            List<DateTime> timeZones = new List<DateTime>();

            do
            {
                temp = temp.AddHours(1);
                if (temp.Hour == morning || temp.Hour == day ||
                    temp.Hour == evening || temp.Hour == night)
                {
                    timeZones.Add(temp);
                }
            } while (temp < end);

            return timeZones;
        }

        private string GetZoneInWords(int time)
        {
            string timeOfDay = "";
            if (time.Equals(6))
                timeOfDay = "Morning";
            else if (time.Equals(12))
                timeOfDay = "Day";
            else if (time.Equals(17))
                timeOfDay = "Evening";
            else if (time.Equals(23))
                timeOfDay = "Night";

            return timeOfDay + " parking";
        }
    }
evilone
  • 22,410
  • 7
  • 80
  • 107

1 Answers1

4

Iterate through all the "time zones" and for each, work out the overlap between that and the customer's parking. For example, as pseudo-code:

private static TimeSpan FindOverlap(ParkingTime parkingTime, TimeZone timeZone)
{
    // Handle wraparound zones like 23-6. Note that this doesn't attempt
    // to handle *parking* which starts at 11.30pm etc.
    if (timeZone.Start > timeZone.End)
    {
        return FindOverlap(parkingTime,
                     new TimeZone(timeZone.Start.Date, timeZone.End)
             + FindOverlap(parkingTime,
                     new TimeZone(timeZone.End, timeZone.Start.Date.AddDays(1));
    }

    DateTime overlapStart = Max(parkingTime.Start, timeZone.Start);
    DateTime overlapEnd = Min(parkingTime.End, timeZone.End);
    TimeSpan overlap = overlapEnd - overlapStart;

    // If the customer arrived after the end or left before the start,
    // the overlap will be negative at this point.
    return overlap < TimeSpan.Zero ? TimeSpan.Zero : overlap;
}

private static DateTime Min(DateTime x, DateTime y)
{
    return x < y ? x : y;
}

private static DateTime Max(DateTime x, DateTime y)
{
    return x > y ? x : y;
}

By the way, I would strongly encourage you to rename your "time zone" concept, given that it already has a well-known (if not well-understood :) meaning.

Perhaps you should call it ParkingInterval? Or ParkingPriceInterval if the difference is really in terms of cost?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Does C# really not have built-in methods for min/max on `IComparable` objects? – jdmichal Jul 25 '11 at 09:52
  • @jdmichal: Well, C# is a language - it would be .NET which had those methods. They may exist somewhere in a generic form, but I'm not aware of them. You could use the LINQ methods of course. – Jon Skeet Jul 25 '11 at 10:02
  • @Jon Skeet This doesn't work for me...I have ranges 6-12, 12-17, 17-23 and 23-6. If if park a car from 00:30 till 6:32, then first compare only gets me the right 32 minutes. Others are all zeros. Although this parking time is in range 23-6 and for 5 hours and 30 minutes What I'm missing here? – evilone Jul 26 '11 at 22:56
  • @evilone: Ah, I missed the idea of a range starting at 23 and ending at 6. That's basically a pain. Are you passing in a zone with the start before the end, or a start on the day before the normal parking day, or an end on the next day? We can adjust the code appropriately, but I'll need to know how you'll call it. – Jon Skeet Jul 27 '11 at 05:19
  • I made a ParkingTimeZone class. In the class constructor there's too parameters. Zone start and zone end. I played with DateTime.Today.AddHours too, but still no luck. – evilone Jul 27 '11 at 05:32
  • @evilone: What are the types of start and end though? Basically I suspect you'll have to handle wraparound ranges by internally adding the results of two "subranges", 0-6 and 23-24. – Jon Skeet Jul 27 '11 at 05:57
  • Nothing certain. I can adapt your solution, if you have something in mind. – evilone Jul 27 '11 at 06:01
  • @evilone: You may well want to take a TimeSpan to represent the time within the day - then you'd just need to take the time of day for the actual parking time too. But as I say, when computing for a wraparound range (as denoted by one which starts after it ends) I'd just call the method recursively twice and add the results. Will edit with an idea of how that might work. – Jon Skeet Jul 27 '11 at 06:15
  • Oh, still something wrong. Customer parked from today 20:40 till tomorrow morning 06:32. Then this algorithm calculates time from 17-23 timezone 23:00-20:40 = 2:20 hours and from 23-06 timezone 7 hours, but those 32 minutes goes missing somewhere. – evilone Jul 27 '11 at 17:46
  • @evilone: I'd been assuming that a customer parked within a single day, or at least that that was what you'd be passing in. If they can start on the previous day, you'll also need to consider if they've been parking for *multiple* days. Basically it all gets very complicated. Can you split their parking into separate days, i.e. 20:40-midnight + midnight-06:32? – Jon Skeet Jul 27 '11 at 17:57
  • @evilone: No, I don't have time for that. I'm trying to do many, many things at roughly the same time. Chat's great for when you've got a block of time to dedicate to something, but I don't. I can give you a few minutes at a time, and that's better done in comments IMO. – Jon Skeet Jul 27 '11 at 18:40
  • I give you an example: customer started parking lets say 25.07.2011 11:00:00 and stopped parking 27.07.2011 21:30:00. First thing I think is to find when parking time zones starts. The period is from 6 to 6. Then I need to compare parking start time with parking time zone start time and I find which is the starting parking time zone. Then I count how many days customer parked. If there's less than a day then something needed to do, otherwise count days etc...Am I on the right path? – evilone Jul 27 '11 at 20:07
  • @evilone: That sounds like you're making it more complicated than it needs to be. I would just take every day between the start date of the customer parking and the end date. For each day, if it *is* the start date, use the start time - otherwise the midnight at the start of the day. If it *is* the end date, use the end time - otherwise the midnight at the end of the day. Compute all the parking times using that date and start/end times. Then move on to the next day. – Jon Skeet Jul 27 '11 at 20:09
  • edited my answer :) thanks again for being with me and helping me – evilone Jul 27 '11 at 20:52