Question

I want to accumulate a string array of time values and check if the total time is greater than one day. In this test case, I start at 12:00 am and add 12 hours. I then add 12 hours and 5 minutes. the total time should be 24 hours and 5 minutes.

The problem is that the calculation from 12:00 PM to 12:05 AM is calculating 11 hours and 55 minutes instead of 12 hours and 5 minutes.

How can I accumulate these times correctly for my use case?

string[] times = {"12:00 AM", "12:00 PM", "12:05 AM"};
const string timePattern = "h:mm tt";

double totalMillis = 0;
var prevTimeSpan = new TimeSpan(0);

foreach (var time in times)
{
    var parsedDate = DateTime.ParseExact(time, timePattern, null, DateTimeStyles.None);
    var currTimeSpan = parsedDate.TimeOfDay;
    var millis = Math.Abs((prevTimeSpan - currTimeSpan).TotalMilliseconds);
    prevTimeSpan = currTimeSpan;
    totalMillis += millis;
}

// 24 hours = 86400000
Console.WriteLine("Result is more than 24 hours? {0}", totalMillis >= 86400000 ? "Yes" : "No");
Was it helpful?

Solution

One thing to note is that you are making assumptions about your times. You are assuming that each time is later than the last time and therefore if it's an earlier time of day that is must be in the next day.

Based on that assumption here is how you can calculate the total TimeSpan from the set of times of day:

string[] times = { "12:00 AM", "12:00 PM", "12:05 AM" };
const string timePattern = "h:mm tt";

DateTime[] dates = times.Select(x => DateTime.ParseExact(x, timePattern, null)).ToArray();

TimeSpan totalTimeSpan = new TimeSpan();

for (int i = 1; i < dates.Length; i++)
{
    // Assume that each time is at least the same date as the previous time
    dates[i] = dates[i - 1].Date + dates[i].TimeOfDay;

    // If this time is earlier than the previous time then assume it must be on the next day
    if (dates[i - 1].TimeOfDay > dates[i].TimeOfDay)
        dates[i] = dates[i].AddDays(1);

    totalTimeSpan += dates[i] - dates[i - 1];
}

Console.WriteLine("Result is more than 24 hours? {0}", totalTimeSpan.TotalHours > 24);

OTHER TIPS

Problem is that you're ignoring the fact that 12:05 AM after 12:00 PM is next day.

Try this

string[] times = { "12:00 AM", "12:00 PM", "12:05 AM" };
const string timePattern = "h:mm tt";
TimeSpan prev = TimeSpan.Zero;
var spans = times.Select(x =>
{
    var span = DateTime.ParseExact(x, timePattern, null, DateTimeStyles.None).TimeOfDay;
    if (span < prev)
    {
        span = span.Add(TimeSpan.FromDays(1.0) - prev);
    }
    prev = span;
    return span;
});
var totalMillis = spans.Sum(x => x.TotalMilliseconds);

// 24 hours = 86400000
Console.WriteLine("Result is more than 24 hours? {0}", totalMillis >= 86400000 ? "Yes" : "No");

You can do this easily with LINQ:

string[] times = { "12:00 AM", "12:00 PM", "12:05 AM" };
const string timePattern = "h:mm tt";

var timesOfDay = times.Select(x => DateTime.ParseExact(x, timePattern, CultureInfo.InvariantCulture).TimeOfDay).ToList();
var elapsedTimes = timesOfDay.Zip(timesOfDay.Skip(1), (a, b) => b - a + TimeSpan.FromDays(a > b ? 1 : 0));
TimeSpan totalElapsedTime = elapsedTimes.Aggregate((s, t) => s.Add(t));

It's calculating it correctly if you add a date part.

1/1/2013 12:00 - 1/1/2013 00:05 = 11 hours 55 mins.

I think you are expecting it to be 1/2/2013 00:05 - one day ahead. 12 hours 5 mins.

A solution for this specific use case is:

string[] times = {"12:00 AM", "12:00 PM", "12:00 AM"};
const string timePattern = "h:mm tt";

double totalMillis = 0;
var prevTimeSpan = new TimeSpan(0);

foreach (var time in times)
{
    var parsedDate = DateTime.ParseExact(time, timePattern, null, DateTimeStyles.None);
    var currTimeSpan = parsedDate.TimeOfDay;
    var millis = (prevTimeSpan - currTimeSpan).TotalMilliseconds;
    prevTimeSpan = currTimeSpan;
    totalMillis += millis;
}

// 24 hours = 86400000
Console.WriteLine("Result is more than 24 hours? {0}", totalMillis >= 0 ? "No" : "Yes");

Notice that instead of checking for totalMillis being greater than a day, I just check to see if it becomes negative. I still think it would be interesting to convert the string array into a time span that totals without a date involved.

So maybe operate on DateTime:

string[] times = { "12:00 AM", "12:00 PM", "12:05 AM" };
         const string timePattern = "h:mm tt";

         var dateTime = new DateTime(2010,1,1,0,0,0,0);
         foreach (var time in times)
         {
            var parsedDate = DateTime.ParseExact(time, timePattern, null, DateTimeStyles.None);
            dateTime = dateTime.AddHours(parsedDate.Hour);
            dateTime = dateTime.AddMinutes(parsedDate.Minute);
         }
         Console.WriteLine(a.ToShortTimeString());
         Console.ReadKey();

now the computer know you are on a new day

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top