using System; using System.Collections.Generic; using System.Linq; namespace Unosquare.Swan { /// /// Provides various extension methods for dates. /// public static class DateExtensions { private static readonly Dictionary DateRanges = new Dictionary() { { "minute", 59}, { "hour", 23}, { "dayOfMonth", 31}, { "month", 12}, { "dayOfWeek", 6}, }; /// /// Converts the date to a YYYY-MM-DD string. /// /// The date. /// The concatenation of date.Year, date.Month and date.Day. public static String ToSortableDate(this DateTime date) => $"{date.Year:0000}-{date.Month:00}-{date.Day:00}"; /// /// Converts the date to a YYYY-MM-DD HH:II:SS string. /// /// The date. /// The concatenation of date.Year, date.Month, date.Day, date.Hour, date.Minute and date.Second. public static String ToSortableDateTime(this DateTime date) => $"{date.Year:0000}-{date.Month:00}-{date.Day:00} {date.Hour:00}:{date.Minute:00}:{date.Second:00}"; /// /// Parses a YYYY-MM-DD and optionally it time part, HH:II:SS into a DateTime. /// /// The sortable date. /// /// A new instance of the DateTime structure to /// the specified year, month, day, hour, minute and second. /// /// sortableDate. /// /// Represents errors that occur during application execution. /// /// /// Unable to parse sortable date and time. - sortableDate. /// public static DateTime ToDateTime(this String sortableDate) { if(String.IsNullOrWhiteSpace(sortableDate)) { throw new ArgumentNullException(nameof(sortableDate)); } Int32 hour = 0; Int32 minute = 0; Int32 second = 0; String[] dateTimeParts = sortableDate.Split(' '); try { if(dateTimeParts.Length != 1 && dateTimeParts.Length != 2) { throw new Exception(); } String[] dateParts = dateTimeParts[0].Split('-'); if(dateParts.Length != 3) { throw new Exception(); } Int32 year = Int32.Parse(dateParts[0]); Int32 month = Int32.Parse(dateParts[1]); Int32 day = Int32.Parse(dateParts[2]); if(dateTimeParts.Length > 1) { String[] timeParts = dateTimeParts[1].Split(':'); if(timeParts.Length != 3) { throw new Exception(); } hour = Int32.Parse(timeParts[0]); minute = Int32.Parse(timeParts[1]); second = Int32.Parse(timeParts[2]); } return new DateTime(year, month, day, hour, minute, second); } catch(Exception) { throw new ArgumentException("Unable to parse sortable date and time.", nameof(sortableDate)); } } /// /// Creates a date's range. /// /// The start date. /// The end date. /// /// A sequence of integral numbers within a specified date's range. /// public static IEnumerable DateRange(this DateTime startDate, DateTime endDate) => Enumerable.Range(0, (endDate - startDate).Days + 1).Select(d => startDate.AddDays(d)); /// /// Rounds up a date to match a timespan. /// /// The datetime. /// The timespan to match. /// /// A new instance of the DateTime structure to the specified datetime and timespan ticks. /// public static DateTime RoundUp(this DateTime date, TimeSpan timeSpan) => new DateTime((date.Ticks + timeSpan.Ticks - 1) / timeSpan.Ticks * timeSpan.Ticks); /// /// Get this datetime as a Unix epoch timestamp (seconds since Jan 1, 1970, midnight UTC). /// /// The date to convert. /// Seconds since Unix epoch. public static Int64 ToUnixEpochDate(this DateTime date) { #if NETSTANDARD2_0 return new DateTimeOffset(date).ToUniversalTime().ToUnixTimeSeconds(); #else Int64 epochTicks = new DateTime(1970, 1, 1).Ticks; return (date.Ticks - epochTicks) / TimeSpan.TicksPerSecond; #endif } /// /// Compares a Date to another and returns a DateTimeSpan. /// /// The date start. /// The date end. /// A DateTimeSpan with the Years, Months, Days, Hours, Minutes, Seconds and Milliseconds between the dates. public static DateTimeSpan GetDateTimeSpan(this DateTime dateStart, DateTime dateEnd) => DateTimeSpan.CompareDates(dateStart, dateEnd); /// /// Compare the Date elements(Months, Days, Hours, Minutes). /// /// The date. /// The minute (0-59). /// The hour. (0-23). /// The day of month. (1-31). /// The month. (1-12). /// The day of week. (0-6)(Sunday = 0). /// Returns true if Months, Days, Hours and Minutes match, otherwise false. public static Boolean AsCronCanRun(this DateTime date, Int32? minute = null, Int32? hour = null, Int32? dayOfMonth = null, Int32? month = null, Int32? dayOfWeek = null) { List results = new List { GetElementParts(minute, date.Minute), GetElementParts(hour, date.Hour), GetElementParts(dayOfMonth, date.Day), GetElementParts(month, date.Month), GetElementParts(dayOfWeek, (Int32) date.DayOfWeek), }; return results.Any(x => x != false); } /// /// Compare the Date elements(Months, Days, Hours, Minutes). /// /// The date. /// The minute (0-59). /// The hour. (0-23). /// The day of month. (1-31). /// The month. (1-12). /// The day of week. (0-6)(Sunday = 0). /// Returns true if Months, Days, Hours and Minutes match, otherwise false. public static Boolean AsCronCanRun(this DateTime date, String minute = "*", String hour = "*", String dayOfMonth = "*", String month = "*", String dayOfWeek = "*") { List results = new List { GetElementParts(minute, nameof(minute), date.Minute), GetElementParts(hour, nameof(hour), date.Hour), GetElementParts(dayOfMonth, nameof(dayOfMonth), date.Day), GetElementParts(month, nameof(month), date.Month), GetElementParts(dayOfWeek, nameof(dayOfWeek), (Int32) date.DayOfWeek), }; return results.Any(x => x != false); } private static Boolean? GetElementParts(Int32? status, Int32 value) => status.HasValue ? status.Value == value : (Boolean?)null; private static Boolean? GetElementParts(String parts, String type, Int32 value) { if(String.IsNullOrWhiteSpace(parts) || parts == "*") { return null; } if(parts.Contains(",")) { return parts.Split(',').Select(Int32.Parse).Contains(value); } Int32 stop = DateRanges[type]; if(parts.Contains("/")) { Int32 multiple = Int32.Parse(parts.Split('/').Last()); Int32 start = type == "dayOfMonth" || type == "month" ? 1 : 0; for(Int32 i = start; i <= stop; i += multiple) { if(i == value) { return true; } } return false; } if(parts.Contains("-")) { String[] range = parts.Split('-'); Int32 start = Int32.Parse(range.First()); stop = Math.Max(stop, Int32.Parse(range.Last())); if((type == "dayOfMonth" || type == "month") && start == 0) { start = 1; } for(Int32 i = start; i <= stop; i++) { if(i == value) { return true; } } return false; } return Int32.Parse(parts) == value; } } }