using Sarand.Net.Standard.Base.App; using System; using System.Collections.Generic; using System.Globalization; namespace Sarand.Net.Standard.Base.Utils { //======================================================================= public static class DateUtil { //------------------------------------------------------------------------- // Definitions. public static String Format_SqlDate { get; } = "yyyy-MM-dd"; public static String Format_SqlTime { get; } = "HH:mm:ss.fff"; public static String Delim_SqlTime { get; } = " "; public static String Format_SqlDateTime { get; } = Format_SqlDate + Delim_SqlTime + Format_SqlTime; public static String Format_XmlDate { get; } = "yyyy-MM-dd"; public static String Format_XmlTime { get; } = "HH:mm:ss"; public static String Delim_XmlTime { get; } = "T"; public static String Format_XmlDateTime { get; } = Format_XmlDate + Delim_XmlTime + Format_XmlTime; public static String Format_JavascriptDate { get; } = "yyyy-MM-dd"; public static String Format_JavascriptTime { get; } = "HH:mm:ss"; public static String Delim_JavascriptTime { get; } = "T"; //------------------------------------------------------------------------- // P o p u l a t i o n //------------------------------------------------------------------------- /// Combine date and time. public static DateTime SetTime(this DateTime value, DateTime time) { if (!IsValid(value)) return value; return value.Date.Add(time.TimeOfDay); } /// Combine date and time. public static DateTime SetTime(this DateTime value, TimeSpan time) { if (!IsValid(value)) return value; return value.Date + time; } //------------------------------------------------------------------------- public static DateTime ClearSeconds(this DateTime value) { return new DateTime(value.Year, value.Month, value.Day, value.Hour, value.Minute, 0, value.Kind); } //------------------------------------------------------------------------- public enum RoundingDirection { /// Set to next minutes interval if past previous. Up, /// Set to previous minutes interval if not reached next. Down, /// Set to nearest minutes interval. Nearest, } /// /// Roud the time portion of the value to the appropriate multiple of the interval minutes. /// Seconds can be included in the algorythm but are cleared in the return value. /// Interval minutes are capped at 60. /// public static DateTime RoundTime(this DateTime value, ushort intervalMinutes, RoundingDirection direction, bool ignoreSeconds = true) { intervalMinutes = (ushort)Math.Min(60, (int)intervalMinutes); // Number of minutes over the interval. double minsDifference = (value.Minute % intervalMinutes) + (ignoreSeconds ? 0.0 : (value.Second / 60.0)); // Value rounded down to the interval minutes. DateTime newValue = value.Date + new TimeSpan(value.Hour, value.Minute - (value.Minute % intervalMinutes), 0); switch (direction) { case RoundingDirection.Up: if (minsDifference > 0.0) newValue += new TimeSpan(0, intervalMinutes, 0); break; case RoundingDirection.Down: // Nothng to do. break; case RoundingDirection.Nearest: if (minsDifference > (intervalMinutes / 2.0)) newValue += new TimeSpan(0, intervalMinutes, 0); break; } return newValue; } //------------------------------------------------------------------------- // C o n v e r t F r o m S t r i n g //------------------------------------------------------------------------- /// /// Parse date value. /// Format: eg. 'yyyyMMdd', 'dd/MM/yyyy' /// public static DateTime ParseExact(String value, String format, CultureInfo culture = null) { try { return DateTime.ParseExact(value, format, culture ?? CultureInfo.InvariantCulture, DateTimeStyles.None); } catch (Exception) { } return DateTime.MinValue; } //------------------------------------------------------------------------- /// Parse XML date: 'yyyy-MM-dd' public static DateTime ParseXmlDate(String value) { return ParseExact(value, Format_XmlDate); } /// Parse XML time: 'HH:mm:ss' public static DateTime ParseXmlTime(String value) { return ParseExact(value, Format_XmlTime); } /// Parse XML datetime: 'yyyy-MM-ddTHH:mm:ss' public static DateTime ParseXmlDateTime(String value) { return ParseExact(value, Format_XmlDateTime); } //------------------------------------------------------------------------- // V a l i d a t i o n //------------------------------------------------------------------------- /// Vaidate that date is between MinDate & MaxDate. public static bool IsValid(this DateTime value) { if (value <= DateTime.MinValue) return false; if (value >= DateTime.MaxValue) return false; return true; } /// Vaidate that date is between MinDate & MaxDate and after null date. public static bool IsValid(this DateTime value, DateTime nullDate) { if (!IsValid(value)) return false; if (value <= nullDate) return false; return true; } /// Vaidate that date is not null and between MinDate & MaxDate. public static bool IsValid(this DateTime? value) { return (value != null && IsValid((DateTime)value)); } //------------------------------------------------------------------------- // I n v a l i d //------------------------------------------------------------------------- public static DateTime OnInvalid(this DateTime? value, DateTime defaultValue) { if (!IsValid(value)) return defaultValue; return (DateTime)value; } //------------------------------------------------------------------------- // C o m p a r i s o n //------------------------------------------------------------------------- public static DateTime Min(DateTime time1, DateTime time2) { return new DateTime(Math.Min(time1.Ticks, time2.Ticks), time1.Kind); } public static DateTime Max(DateTime time1, DateTime time2) { return new DateTime(Math.Max(time1.Ticks, time2.Ticks), time1.Kind); } //------------------------------------------------------------------------- // P e r i o d s //------------------------------------------------------------------------- public enum Period { Period, Day, Today, Yesterday, Week, WeekCurrent, WeekPrevious, WorkingWeek, WorkingWeekCurrent, WorkingWeekPrevious, Month, MonthCurrent, MonthPrevious, Quarter, QuarterCurrent, QuarterPrevious, Year, YearCurrent, YearPrevious, } public static String GetPeriodCaption(Period period) { switch (period) { case Period.Period: return "Period"; case Period.Day: return "Day"; case Period.Today: return "Today"; case Period.Yesterday: return "Yesterday"; case Period.Week: return "Week"; case Period.WeekCurrent: return "This week"; case Period.WeekPrevious: return "Last week"; case Period.WorkingWeek: return "Working week"; case Period.WorkingWeekCurrent: return "This working week"; case Period.WorkingWeekPrevious: return "Last working week"; case Period.Month: return "Month"; case Period.MonthCurrent: return "This month"; case Period.MonthPrevious: return "Last month"; case Period.Quarter: return "Quarter"; case Period.QuarterCurrent: return "This quarter"; case Period.QuarterPrevious: return "Last quarter"; case Period.Year: return "Year"; case Period.YearCurrent: return "This year"; case Period.YearPrevious: return "Last year"; } return period.ToString(); } public static bool IsPeriodFromFixed(Period period) { switch (period) { case Period.Period: case Period.Day: case Period.Week: case Period.WorkingWeek: case Period.Month: case Period.Quarter: case Period.Year: return false; } return true; } public static bool IsPeriodToFixed(Period period) { switch (period) { case Period.Period: return false; } return true; } public class ComboBoxItemPeriod { public DateUtil.Period Period { get; } public ComboBoxItemPeriod(DateUtil.Period period) { Period = period; } public override String ToString() { return GetPeriodCaption(Period); } // Comparison. public override int GetHashCode() { return 23 + Period.GetHashCode(); } public override bool Equals(object obj) { if (obj is ComboBoxItemPeriod) return Equals((ComboBoxItemPeriod)obj); if (obj is Period) return Equals((Period)obj); return base.Equals(obj); } public bool Equals(ComboBoxItemPeriod item) { return (Period == item.Period); } public bool Equals(Period period) { return (Period == period); } } public static DateTime StartOfPeriod(Period period, DateTime from, DateTime to) { switch (period) { case Period.Period: return from; case Period.Day: return from; case Period.Today: return DateTime.Today; case Period.Yesterday: return DateTime.Today.AddDays(-1); case Period.Week: return from.StartOfWeek(); case Period.WeekCurrent: return DateTime.Today.StartOfWeek(); case Period.WeekPrevious: return DateTime.Today.StartOfWeek().AddDays(-7); case Period.WorkingWeek: return from.StartOfWorkingWeek(); case Period.WorkingWeekCurrent: return DateTime.Today.StartOfWorkingWeek(); case Period.WorkingWeekPrevious: return DateTime.Today.StartOfWorkingWeek().AddDays(-7); case Period.Month: return from.StartOfMonth(); case Period.MonthCurrent: return DateTime.Today.StartOfMonth(); case Period.MonthPrevious: return DateTime.Today.StartOfMonth().AddMonths(-1); case Period.Quarter: return from.StartOfQuarter(); case Period.QuarterCurrent: return DateTime.Today.StartOfQuarter(); case Period.QuarterPrevious: return DateTime.Today.StartOfQuarter().AddMonths(-3); case Period.Year: return from.StartOfYear(); case Period.YearCurrent: return DateTime.Today.StartOfYear(); case Period.YearPrevious: return DateTime.Today.StartOfYear().AddYears(-1); } return DateTime.Today; } public static DateTime EndOfPeriod(Period period, DateTime from, DateTime to) { DateTime startOfPeriod = StartOfPeriod(period, from, to); switch (period) { case Period.Period: if (to.CompareTo(from) < 0) return from; return to; case Period.Day: case Period.Today: case Period.Yesterday: return startOfPeriod; case Period.Week: case Period.WeekCurrent: case Period.WeekPrevious: case Period.WorkingWeek: case Period.WorkingWeekCurrent: case Period.WorkingWeekPrevious: return startOfPeriod.AddDays(6); case Period.Month: case Period.MonthCurrent: case Period.MonthPrevious: return startOfPeriod.AddMonths(1).AddDays(-1); case Period.Quarter: case Period.QuarterCurrent: case Period.QuarterPrevious: return startOfPeriod.AddMonths(3).AddDays(-1); case Period.Year: case Period.YearCurrent: case Period.YearPrevious: return startOfPeriod.AddYears(1).AddDays(-1); } return DateTime.Today; } //------------------------------------------------------------------- // R e l a t i v e D a t e s //------------------------------------------------------------------- // Relative dates - day. public static DateTime PreviousDay(this DateTime value) { return value.AddDays(-1); } public static DateTime NextDay(this DateTime value) { return value.AddDays(1); } public static DateTime IncrementDate(this DateTime value, int days) { return value.AddDays(days); } public static DateTime IncrementMonth(this DateTime value, int months) { return value.AddMonths(months); } //------------------------------------------------------------------- // Relative dates - week. /// /// Sunday at start of week including specified date. /// /// Date in week for which starting Sunday is required /// public static DateTime StartOfWeek(this DateTime value) { // value.DayOfWeek is number of days to go back to Sunday (0 for Sunday, 1 for Monday etc). return value.AddDays(-(int)value.DayOfWeek); } public static DateTime StartOfWorkingWeek(this DateTime value) { // Go back number to nearest Monday. return value.AddDays(-((((int)value.DayOfWeek) + 6) % 7)); } //------------------------------------------------------------------- // Relative dates - month. // Ref. http://stackoverflow.com/questions/24245523/getting-the-first-and-last-day-of-a-month-using-a-given-datetime-object for performance analysis. public static DateTime StartOfMonth(this DateTime value) { return new DateTime(value.Year, value.Month, 1, 0, 0, 0, value.Kind); } public static int DaysInMonth(this DateTime value) { return DateTime.DaysInMonth(value.Year, value.Month); } public static DateTime EndOfMonth(this DateTime value) { return new DateTime(value.Year, value.Month, value.DaysInMonth(), 0, 0, 0, value.Kind); } //------------------------------------------------------------------- // Relative dates - quarter. public static DateTime StartOfQuarter(this DateTime value) { return new DateTime(value.Year, (((value.Month - 1) / 3) * 3) + 1, 1, 0, 0, 0, value.Kind); } public static int DaysInQuarter(this DateTime value) { DateTime startOfQuarter = value.StartOfQuarter(); return DateTime.DaysInMonth(startOfQuarter.Year, startOfQuarter.Month) + DateTime.DaysInMonth(startOfQuarter.Year, startOfQuarter.Month + 1) + DateTime.DaysInMonth(startOfQuarter.Year, startOfQuarter.Month + 2); } public static DateTime EndOfQuarter(this DateTime value) { DateTime startOfQuarter = value.StartOfQuarter(); return startOfQuarter.EndOfMonth().IncrementDate(1).EndOfMonth().IncrementDate(1).EndOfMonth(); } //------------------------------------------------------------------- // Relative dates - year. public static DateTime StartOfYear(this DateTime value) { return new DateTime(value.Year, 1, 1, 0, 0, 0, value.Kind); } public static int DaysInYear(this DateTime value) { // To take leap year into account. return 365 - 28 + DateTime.DaysInMonth(value.Year, 2); } public static DateTime EndOfYear(this DateTime value) { return new DateTime(value.Year, 12, 31, 0, 0, 0, value.Kind); } //------------------------------------------------------------------- // Age calculation. //------------------------------------------------------------------- // Age calculation (returns 0 as a result of any exceptions): public static ushort CalculateAge(DateTime dob, DateTime ageOn) { int ageInYears = 0; if (IsValid(dob) && IsValid(ageOn) && dob < ageOn) { ageInYears = ageOn.Year - dob.Year; if (ageOn.Month < dob.Month) ageInYears--; if (ageOn.Month == dob.Month && ageOn.Day < dob.Day) ageInYears--; } return (ushort)ageInYears; } public static ushort CalculateAgeMonths(DateTime dob, DateTime ageOn) { int ageInMonths = 0; if (IsValid(dob) && IsValid(ageOn) && dob < ageOn) { ageInMonths = (ageOn.Year - dob.Year) * 12; // Will decrement the value if birth month > age month. ageInMonths += ageOn.Month - dob.Month; if (ageOn.Day < dob.Day) ageInMonths--; } return (ushort)ageInMonths; } //------------------------------------------------------------------- // C o n v e r s i o n //------------------------------------------------------------------- // Convert date to 32bit integer value. /// /// Convert Date (yyyyMMdd) to Int32 (C++ long). /// Used by legacy database columns to store date as 4 byte integer. /// public static Int32 DateToInt32(this DateTime value) { if (value.Equals(DateTime.MinValue)) return 0; Int32 dateValue = (value.Year << 16) + (value.Month << 8) + value.Day; return dateValue; } /// /// Convert Int32 (C++ long) to Date (yyyyMMdd). /// Used by legacy database columns to store date as 4 byte integer. /// public static DateTime Int32ToDate(Int32 dateValue) { if (dateValue == 0) return DateTime.MinValue; int year = ((dateValue >> 16) & 0xffff); int month = ((dateValue >> 8) & 0xff); int day = (dateValue & 0xff); return new DateTime(year, month, day, 0, 0, 0, DateTimeKind.Utc); } //------------------------------------------------------------------- /// /// Convert Time (HHmmssnnn) to Int32 (C++ long). /// Used by legacy database columns to store time as 4 byte integer. /// public static Int32 TimeToInt32(this DateTime value) { if (value.Equals(DateTime.MinValue)) return 0; Int32 timeValue = (value.Hour << 24) + (value.Minute << 16) + (value.Second << 8) + (value.Millisecond / 10); return timeValue; } /// /// Convert Int32 (C++ long) to Time (HHmmssnnn). /// Used by legacy database columns to store time as 4 byte integer. /// public static DateTime Int32ToTime(Int32 timeValue) { if (timeValue == 0) return DateTime.MinValue; int year = DateTime.Today.Year; int month = DateTime.Today.Month; int day = DateTime.Today.Day; int hour = (timeValue >> 24) & 0xff; int minute = (timeValue >> 16) & 0xff; int second = (timeValue >> 8) & 0xff; int millis = timeValue & 0xff; return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc).AddMilliseconds(millis); } //------------------------------------------------------------------- // F o r m a t t i n g //------------------------------------------------------------------- /// /// Format Date/time using format string. /// d, dd: Day of month /// ddd, dddd: Day of week /// M, MM: Month (1 to 12) /// MMM, MMMM: Month name /// y - yyyy: Year /// H, HH: hour (24 hour clock) /// h, hh: hour (12 hour clock) /// t, tt: AM/PM designator /// m, mm: minute /// s, ss: second /// f: 10ths second /// fff: 1,000ths second /// ffffff: 1,000,000ths second /// F: 10ths second (non zero) etc /// g, gg: Period or era /// /// /// /// public static String Format(this DateTime value, String format, CultureInfo cultureInfo = null) { return Format((DateTime?)value, format, cultureInfo); } public static String Format(this DateTime? value, String format, CultureInfo cultureInfo = null) { if (!IsValid(value)) return String.Empty; try { if (!String.IsNullOrEmpty(format)) { return ((DateTime)value).ToString(format, cultureInfo ?? CultureInfo.InvariantCulture); } return ((DateTime)value).ToString(cultureInfo ?? CultureInfo.InvariantCulture); } catch (Exception exc) { SaLogger.Debug("DateUtil.Format Exception: " + exc.Message); } return String.Empty; } //------------------------------------------------------------------- /// Format as SQL date: 'yyyy-MM-dd' public static String FormatSqlDate(this DateTime value) { return Format(value, Format_SqlDate); } /// Format as SQL time: 'HH:mm:ss' public static String FormatSqlTime(this DateTime value) { return Format(value, Format_SqlTime); } /// Format as SQL datetime: 'yyyy-MM-ddTHH:mm:ss' public static String FormatSqlDateTime(this DateTime value) { return Format(value, Format_SqlDateTime); } //------------------------------------------------------------------- /// Format as XML date: 'yyyy-MM-dd' public static String FormatXmlDate(this DateTime value) { return Format(value, Format_XmlDate); } /// Format as XML time: 'HH:mm:ss' public static String FormatXmlTime(this DateTime value) { return Format(value, Format_XmlTime); } /// Format as XML datetime: 'yyyy-MM-ddTHH:mm:ss' public static String FormatXmlDateTime(this DateTime value) { return Format(value, Format_XmlDateTime); } //------------------------------------------------------------------- /// Format suitable for parsing as javascript date: 'yyyy-MM-dd' public static String FormatDateForJavascript(this DateTime? value) { return Format(value, Format_JavascriptDate); } //------------------------------------------------------------------- /// Format number of seconds. public static String FormatNumberOfSeconds(int numberOfSeconds) { int numberOfMinutes = numberOfSeconds / 60; int numberOfHours = numberOfMinutes / 60; int numberOfDays = numberOfHours / 24; String elapsed = ""; if (numberOfDays > 0) elapsed += numberOfDays + "d "; if (numberOfHours > 0) elapsed += (numberOfHours % 24) + "h "; if (numberOfMinutes > 0) elapsed += (numberOfMinutes % 60) + "m "; if (StringUtil.IsEmpty(elapsed) || numberOfSeconds % 60 > 0) elapsed += (numberOfSeconds % 60) + "s "; return elapsed.Trim(); } /// Format number of seconds, including millis where value <= 10s. public static String FormatNumberOfSeconds(double numberOfSeconds) { int numberOfMinutes = (int)(numberOfSeconds / 60); int numberOfHours = numberOfMinutes / 60; int numberOfDays = numberOfHours / 24; String elapsed = ""; if (numberOfDays > 0) elapsed += numberOfDays + "d "; if (numberOfHours > 0) elapsed += (numberOfHours % 24) + "h "; if (numberOfMinutes > 0) elapsed += (numberOfMinutes % 60) + "m "; if (StringUtil.IsEmpty(elapsed) || numberOfSeconds % 60 > 0) { int millis = (int)((numberOfSeconds - (int)numberOfSeconds) * 1000); if (numberOfSeconds > 10.0) elapsed += (int)(numberOfSeconds % 60) + "s "; else elapsed += (int)(numberOfSeconds % 60) + "." + millis.ToString("D3") + "s "; } return elapsed.Trim(); } //------------------------------------------------------------------- // L i s t S o r t i n g //------------------------------------------------------------------- public static void SortAscending(this List list) { list.Sort((a, b) => a.CompareTo(b)); } public static void SortDescending(this List list) { list.Sort((a, b) => b.CompareTo(a)); } //------------------------------------------------------------------- } //======================================================================= }