using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Swan
{
///
/// Provides extension methods for .
///
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 on which this method is called.
/// The concatenation of date.Year, date.Month and date.Day.
public static string ToSortableDate(this DateTime @this)
=> $"{@this.Year:0000}-{@this.Month:00}-{@this.Day:00}";
///
/// Converts the date to a YYYY-MM-DD HH:II:SS string.
///
/// The on which this method is called.
/// The concatenation of date.Year, date.Month, date.Day, date.Hour, date.Minute and date.Second.
public static string ToSortableDateTime(this DateTime @this)
=> $"{@this.Year:0000}-{@this.Month:00}-{@this.Day:00} {@this.Hour:00}:{@this.Minute:00}:{@this.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 @this)
{
if (string.IsNullOrWhiteSpace(@this))
throw new ArgumentNullException(nameof(@this));
var hour = 0;
var minute = 0;
var second = 0;
var dateTimeParts = @this.Split(' ');
try
{
if (dateTimeParts.Length != 1 && dateTimeParts.Length != 2)
throw new Exception();
var dateParts = dateTimeParts[0].Split('-');
if (dateParts.Length != 3) throw new Exception();
var year = int.Parse(dateParts[0]);
var month = int.Parse(dateParts[1]);
var day = int.Parse(dateParts[2]);
if (dateTimeParts.Length > 1)
{
var timeParts = dateTimeParts[1].Split(':');
if (timeParts.Length != 3) throw new Exception();
hour = int.Parse(timeParts[0]);
minute = int.Parse(timeParts[1]);
second = int.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(@this));
}
}
///
/// Creates a date 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 on which this method is called.
/// Seconds since Unix epoch.
public static long ToUnixEpochDate(this DateTime @this) => new DateTimeOffset(@this).ToUniversalTime().ToUnixTimeSeconds();
///
/// 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 on which this method is called.
/// 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 bool AsCronCanRun(this DateTime @this, int? minute = null, int? hour = null, int? dayOfMonth = null, int? month = null, int? dayOfWeek = null)
{
var results = new List
{
GetElementParts(minute, @this.Minute),
GetElementParts(hour, @this.Hour),
GetElementParts(dayOfMonth, @this.Day),
GetElementParts(month, @this.Month),
GetElementParts(dayOfWeek, (int) @this.DayOfWeek),
};
return results.Any(x => x != false);
}
///
/// Compare the Date elements(Months, Days, Hours, Minutes).
///
/// The on which this method is called.
/// 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 bool AsCronCanRun(this DateTime @this, string minute = "*", string hour = "*", string dayOfMonth = "*", string month = "*", string dayOfWeek = "*")
{
var results = new List
{
GetElementParts(minute, nameof(minute), @this.Minute),
GetElementParts(hour, nameof(hour), @this.Hour),
GetElementParts(dayOfMonth, nameof(dayOfMonth), @this.Day),
GetElementParts(month, nameof(month), @this.Month),
GetElementParts(dayOfWeek, nameof(dayOfWeek), (int) @this.DayOfWeek),
};
return results.Any(x => x != false);
}
///
/// Converts a to the RFC1123 format.
///
/// The on which this method is called.
/// The string representation of according to RFC1123.
///
/// If is not a UTC date / time, its UTC equivalent is converted, leaving unchanged.
///
public static string ToRfc1123String(this DateTime @this)
=> @this.ToUniversalTime().ToString("R", CultureInfo.InvariantCulture);
private static bool? GetElementParts(int? status, int value) => status.HasValue ? status.Value == value : (bool?) null;
private static bool? GetElementParts(string parts, string type, int value)
{
if (string.IsNullOrWhiteSpace(parts) || parts == "*")
return null;
if (parts.Contains(","))
{
return parts.Split(',').Select(int.Parse).Contains(value);
}
var stop = DateRanges[type];
if (parts.Contains("/"))
{
var multiple = int.Parse(parts.Split('/').Last());
var start = type == "dayOfMonth" || type == "month" ? 1 : 0;
for (var i = start; i <= stop; i += multiple)
if (i == value) return true;
return false;
}
if (parts.Contains("-"))
{
var range = parts.Split('-');
var start = int.Parse(range.First());
stop = Math.Max(stop, int.Parse(range.Last()));
if ((type == "dayOfMonth" || type == "month") && start == 0)
start = 1;
for (var i = start; i <= stop; i++)
if (i == value) return true;
return false;
}
return int.Parse(parts) == value;
}
}
}