Skip to main content

Time Utilities class provides date and time related routines.

using System;
using System.Globalization;

namespace Utils
{
    public static class TimeHelper
    {
        public static DateTime MinDateValue = new DateTime(1900, 1, 1, 0, 0, 0, 0, CultureInfo.InvariantCulture.Calendar, DateTimeKind.Utc);

        /// <summary>
        /// Displays a date in friendly format.
        /// </summary>
        /// <param name="Date"></param>
        /// <param name="ShowTime"></param>
        /// <returns>Today,Yesterday,Day of week or a string day (Jul 15, 2008)</returns>
        public static string FriendlyDateString(DateTime date, bool showTime)
        {
            if (date < TimeHelper.MinDateValue)
            {
                return string.Empty;
            }

            string formattedDate = string.Empty;
            if (date.Date.Equals(DateTime.Today))
            {
                formattedDate = "Today";
            }
            else if (date.Date == DateTime.Today.AddDays(-1))
            {
                formattedDate = "Yesterday";
            }
            else if (date.Date > DateTime.Today.AddDays(-6))
            {
                // Show the Day of the week
                formattedDate = date.ToString("dddd").ToString();
            }
            else
            {
                formattedDate = date.ToString("MMMM dd, yyyy");
            }

            if (showTime)
            {
                formattedDate += string.Format(" @ {0}", date.ToString("t").ToLower().Replace(" ", ""));
            }

            return formattedDate;
        }

        /// <summary>
        /// Returns a short date time string
        /// </summary>
        /// <param name="date"></param>
        /// <param name="showTime"></param>
        /// <returns></returns>
        public static string ShortDateString(DateTime date, bool showTime=false)
        {
            if (date < TimeHelper.MinDateValue)
            {
                return string.Empty;
            }

            string dateString = date.ToString("MMM dd, yyyy");
            if (!showTime)
            {
                return dateString;
            }

            return string.Format("{0} - {1}", dateString, date.ToString("h:mmtt").ToLower());
        }

        /// <summary>
        /// Returns a short date time string
        /// </summary>
        /// <param name="date"></param>
        /// <param name="ShowTime"></param>
        /// <returns></returns>
        public static string ShortDateString(DateTime? date, bool showTime)
        {
            if (date == null || !date.HasValue)
            {
                return string.Empty;
            }

            return ShortDateString(date.Value, showTime);
        }

        /// <summary>
        /// Displays a number of milliseconds as friendly seconds, hours, minutes
        /// Pass -1 to get a blank date.
        /// </summary>
        /// <param name="milliSeconds">the elapsed milliseconds to display time for</param>
        /// <returns>string in format of just now or 1m ago, 2h ago</returns>
        public static string FriendlyElapsedTimeString(int milliSeconds)
        {
            if (milliSeconds < 0)
            {
                return string.Empty;
            }

            if (milliSeconds < 60000)
            {
                return "just now";
            }

            if (milliSeconds < 3600000)
            {
                return string.Format("{0}m ago", ((milliSeconds / 60000)).ToString());
            }

            return string.Format("{0}h ago", ((milliSeconds / 3600000)).ToString());
        }

        /// <summary>
        /// Displays the elapsed time  friendly seconds, hours, minutes
        /// </summary>
        /// <param name="elapsed">Timespan of elapsed time</param>
        /// <returns>string in format of just now or 1m ago, 2h ago</returns>
        public static string FriendlyElapsedTimeString(TimeSpan elapsed)
        {
            return FriendlyElapsedTimeString((int)elapsed.TotalMilliseconds);
        }

        /// <summary>
        /// Converts a fractional hour value like 1.25 to 1:15  hours:minutes format
        /// </summary>
        /// <param name="hours">Decimal hour value</param>
        /// <param name="format">An optional format string where {0} is hours and {1} is minutes (ie: "{0}h:{1}m").</param>
        /// <returns></returns>
        public static string FractionalHoursToString(decimal hours, string format)
        {
            if (string.IsNullOrEmpty(format))
            {
                format = "{0}:{1}";
            }

            TimeSpan tspan = TimeSpan.FromHours((double)hours);

            // Account for rounding error
            int minutes = tspan.Minutes;
            if (tspan.Seconds > 29)
            {
                minutes++;
            }

            return string.Format(format, tspan.Hours + tspan.Days * 24, minutes);
        }

        /// <summary>
        /// Converts a fractional hour value like 1.25 to 1:15  hours:minutes format
        /// </summary>
        /// <param name="hours">Decimal hour value</param>
        public static string FractionalHoursToString(decimal hours)
        {
            return FractionalHoursToString(hours, null);
        }

        /// <summary>
        /// Rounds an hours value to a minute interval
        /// 0 means no rounding
        /// </summary>
        /// <param name="minuteInterval">Minutes to round up or down to</param>
        /// <returns></returns>
        public static decimal RoundDateToMinuteInterval(decimal hours, int minuteInterval,
            RoundingDirection direction)
        {
            if (minuteInterval == 0)
            {
                return hours;
            }

            decimal fraction = 60 / minuteInterval;

            switch (direction)
            {
                case RoundingDirection.Round:
                    return Math.Round(hours * fraction, 0) / fraction;
                case RoundingDirection.RoundDown:
                    return Math.Truncate(hours * fraction) / fraction;
            }
            return Math.Ceiling(hours * fraction) / fraction;
        }

        /// <summary>
        /// Rounds a date value to a given minute interval
        /// </summary>
        /// <param name="time">Original time value</param>
        /// <param name="minuteInterval">Number of minutes to round up or down to</param>
        /// <returns></returns>
        public static DateTime RoundDateToMinuteInterval(DateTime time, int minuteInterval,
            RoundingDirection direction)
        {
            if (minuteInterval == 0)
            {
                return time;
            }

            decimal interval = minuteInterval;
            decimal actMinute = time.Minute;

            if (actMinute == 0.00M)
            {
                return time;
            }

            int newMinutes = 0;

            switch (direction)
            {
                case RoundingDirection.Round:
                    newMinutes = (int)(Math.Round(actMinute / interval, 0) * interval);
                    break;
                case RoundingDirection.RoundDown:
                    newMinutes = (int)(Math.Truncate(actMinute / interval) * interval);
                    break;
                case RoundingDirection.RoundUp:
                    newMinutes = (int)(Math.Ceiling(actMinute / interval) * interval);
                    break;
            }

            // strip time
            time = time.AddMinutes(time.Minute * -1);
            time = time.AddSeconds(time.Second * -1);
            time = time.AddMilliseconds(time.Millisecond * -1);

            // add new minutes back on
            return time.AddMinutes(newMinutes);
        }

        /// <summary>
        /// Creates a DateTime value from date and time input values.
        /// </summary>
        /// <param name="Date"></param>
        /// <param name="Time"></param>
        /// <returns></returns>
        public static DateTime DateTimeFromDateAndTime(string date, string time)
        {
            return DateTime.Parse(string.Format("{0} {1}", date, time));
        }

        /// <summary>
        /// Creates a DateTime Value from a DateTime date and a string time value.
        /// </summary>
        /// <param name="Date"></param>
        /// <param name="Time"></param>
        /// <returns></returns>
        public static DateTime DateTimeFromDateAndTime(DateTime date, string time)
        {
            return DateTime.Parse(string.Format("{0} {1}", date.ToShortDateString(), time));
        }

        /// <summary>
        /// Converts the passed date time value to Mime formatted time string.
        /// </summary>
        /// <param name="Time"></param>
        public static string MimeDateTime(DateTime time)
        {
            TimeSpan offset = TimeZone.CurrentTimeZone.GetUtcOffset(time);

            string sOffset = null;
            if (offset.Hours < 0)
            {
                sOffset = string.Format("-{0}", (offset.Hours * -1).ToString().PadLeft(2, '0'));
            }
            else
            {
                sOffset = string.Format("+{0}", offset.Hours.ToString().PadLeft(2, '0'));
            }

            sOffset += offset.Minutes.ToString().PadLeft(2, '0');

            return string.Format("Date: {0} {1}", time.ToString("ddd, dd MMM yyyy HH:mm:ss",
                System.Globalization.CultureInfo.InvariantCulture), sOffset);
        }

        /// <summary>
        /// Truncates a DateTime value to the nearest partial value.
        /// </summary>
        /// <remarks>
        /// From: http://stackoverflow.com/questions/1004698/how-to-truncate-milliseconds-off-of-a-net-datetime
        /// </remarks>
        /// <param name="date"></param>
        /// <param name="resolution"></param>
        /// <returns></returns>
        public static DateTime Truncate(DateTime date, DateTimeResolution resolution = DateTimeResolution.Second)
        {
            switch (resolution)
            {
                case DateTimeResolution.Year:
                    return new DateTime(date.Year, 1, 1, 0, 0, 0, 0, date.Kind);
                case DateTimeResolution.Month:
                    return new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind);
                case DateTimeResolution.Day:
                    return new DateTime(date.Year, date.Month, date.Day, 0, 0, 0, date.Kind);
                case DateTimeResolution.Hour:
                    return date.AddTicks(-(date.Ticks % TimeSpan.TicksPerHour));
                case DateTimeResolution.Minute:
                    return date.AddTicks(-(date.Ticks % TimeSpan.TicksPerMinute));
                case DateTimeResolution.Second:
                    return date.AddTicks(-(date.Ticks % TimeSpan.TicksPerSecond));
                case DateTimeResolution.Millisecond:
                    return date.AddTicks(-(date.Ticks % TimeSpan.TicksPerMillisecond));
                case DateTimeResolution.Tick:
                    return date.AddTicks(0);
                default:
                    throw new ArgumentException("unrecognized resolution", "resolution");
            }
        }

        /// <summary>
        /// Returns TimeZone adjusted time for a given from a Utc or local time.
        /// Date is first converted to UTC then adjusted.
        /// </summary>
        /// <param name="time"></param>
        /// <param name="timeZoneId"></param>
        /// <returns></returns>
        public static DateTime ToTimeZoneTime(this DateTime time, string timeZoneId = "Central Standard Time")
        {
            TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);

            return time.ToTimeZoneTime(tzi);
        }

        /// <summary>
        /// Returns TimeZone adjusted time for a given from a Utc or local time.
        /// Date is first converted to UTC then adjusted.
        /// </summary>
        /// <param name="time"></param>
        /// <param name="timeZoneId"></param>
        /// <returns></returns>
        public static DateTime ToTimeZoneTime(this DateTime time, TimeZoneInfo tzi)
        {
            return TimeZoneInfo.ConvertTimeFromUtc(time, tzi);
        }
    }
}