Hey lovely programmers,

Today I had a big frustration with an XML file I had to parse containing 25 hours for the 30/10/2016.

All these hours where as String, and no way to use TimeSpan with 25 hours otherwise its the next day…

The best way I found is to generate a list with all the hours of the day but as DateTimeOffset to have the shift of hour at 2 AM for that particular day. And even better, every hour will contain an Index…

using System;
using System.Collections.Generic;
using System.Diagnostics;
 
namespace Common.Helpers
{
    [DebuggerDisplay("{StringRepresentation}")]
    public class DateTimeWrapper
    {
        public DateTime LocalTime { get; private set; }
        public DateTime UtcTime { get; private set; }
        public bool IsSecondOccurrence { get; set; }
        public int Index { get; private set; }

        public DateTimeWrapper(DateTime dateTime)
        {
            UtcTime = dateTime.ToUniversalTime();
            LocalTime = dateTime.ToLocalTime();
            IsSecondOccurrence = CheckIsSecondOccurrence(dateTime);
            CalculateIndex();
        }

        private void CalculateIndex()
        {
            var utcStart = LocalTime.Date.ToUniversalTime();
            var utc = UtcTime;
            var difference = utc - utcStart;
            Index = (int)difference.TotalMinutes / 60;
        }

        private bool CheckIsSecondOccurrence(DateTime dateTime)
        {
            var localStart = dateTime.ToLocalTime().Date;
            var localEnd = localStart.AddDays(1);
            var utc = localStart.ToUniversalTime();

            var localTimes = new List<DateTime>();

            while (utc.ToLocalTime() < localEnd)
            {
                if (dateTime.ToUniversalTime() == utc)
                {
                    if (localTimes.Contains(utc.ToLocalTime()))
                    {
                        return true;
                    }
                }
                localTimes.Add(utc.ToLocalTime());
                utc = utc.AddMinutes(60);
            }

            return false;
        }

        public static List<DateTimeWrapper> GetHoursAvailableFor(DateTime day)
        {
            var localStart = day.ToLocalTime().Date;
            var localEnd = localStart.AddDays(1);
            var utc = localStart.ToUniversalTime();
            var available = new List<DateTimeWrapper>();

            while (utc.ToLocalTime() < localEnd)
            {
                available.Add(new DateTimeWrapper(utc));
                utc = utc.AddMinutes(60);
            }

            return available;
        }

        public override int GetHashCode()
        {
            return UtcTime.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            if (obj == null) return false;
            return UtcTime.Equals(((DateTimeWrapper)obj).UtcTime);
        }

        public string StringRepresentation { get { return string.Format("{0:yyyyMMdd HH:mm}{1}", LocalTime, IsSecondOccurrence ? " *" : ""); } }

        public string StringRepresentationTimePartOnly { get { return string.Format("{0:HH:mm}{1}", LocalTime, IsSecondOccurrence ? " *" : ""); } }
    }
}

If you need the Quarters instead of Hours, just replace the 60 by 15 at the two places…

Enjoy!