using System; using System.Globalization; using BS.Shared.Core; using System.Collections.Generic; using System.Text.RegularExpressions; using DevExpress.XtraScheduler; namespace BS.Shared.Extensions { public enum TimeUnit { Day, Week, Fortnightly, Month, Quarter, Halfyear, Year, Hour } public class DateTimeUnit { public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public int UnitCount { get; set; } public int RestDays { get; set; } } public static class DateTimeExtensions { private static string _TimePattern = @"^(?:[01]\d|2[0-3]):[0-5]\d$"; public static DateTimeUnit GetDateTimeUnit(this DateTime Date1, DateTime Date2, TimeUnit unit) { List period = new List() { Date1, Date2 }; period.Sort(DateTime.Compare); DateTimeUnit ds = new DateTimeUnit(); ds.StartDate = period[0]; ds.EndDate = period[1].AddDays(1); int totaldays = (int)ds.EndDate.Subtract(ds.StartDate).TotalDays; DateTime startDate = ds.StartDate; switch (unit) { case TimeUnit.Hour: ds.UnitCount = totaldays * 24; ds.RestDays = 0; break; case TimeUnit.Day: ds.UnitCount = totaldays; ds.RestDays = 0; break; case TimeUnit.Week: ds.UnitCount = (int)(totaldays / 7m); ds.RestDays = totaldays - (ds.UnitCount * 7); break; case TimeUnit.Fortnightly: ds.UnitCount = (int)(totaldays / 14m); ds.RestDays = totaldays - (ds.UnitCount * 14); break; case TimeUnit.Month: int months = 0; while (startDate.AddMonths(1) <= ds.EndDate) { startDate = startDate.AddMonths(1); months++; } ds.UnitCount = months; ds.RestDays = (int)ds.EndDate.Subtract(startDate).TotalDays; break; case TimeUnit.Quarter: int quarter = 0; while (startDate.AddMonths(3) <= ds.EndDate) { startDate = startDate.AddMonths(3); quarter++; } ds.UnitCount = quarter; ds.RestDays = (int)ds.EndDate.Subtract(startDate).TotalDays; break; case TimeUnit.Halfyear: int halfyear = 0; while (startDate.AddMonths(6) <= ds.EndDate) { startDate = startDate.AddMonths(6); halfyear++; } ds.UnitCount = halfyear; ds.RestDays = (int)ds.EndDate.Subtract(startDate).TotalDays; break; case TimeUnit.Year: int year = 0; while (startDate.AddYears(1) <= ds.EndDate) { startDate = startDate.AddYears(1); year++; } ds.UnitCount = year; ds.RestDays = (int)ds.EndDate.Subtract(startDate).TotalDays; break; default: break; } return ds; } public static DateTime AddMonthsDayDependent(this DateTime pDateTime, int pMonthsToAdd, bool pFirstDay) { var lResult = new DateTime(pDateTime.Year, pDateTime.Month, 1); lResult = lResult.AddMonths(pMonthsToAdd); lResult = FromWeekdayOccurence(lResult.Year, lResult.Month, pDateTime.DayOfWeek, pDateTime.GetWeekdayOccurence(pFirstDay), pFirstDay); lResult = lResult.AddHours(pDateTime.Hour); lResult = lResult.AddMinutes(pDateTime.Minute); lResult = lResult.AddSeconds(pDateTime.Second); lResult = lResult.AddMilliseconds(pDateTime.Millisecond); return lResult; } public static T DoIfNotNull(this DateTime? pDateTime, Func pFunc) { if (pDateTime.HasValue) { return pFunc(pDateTime.Value); } else { return default(T); } } public static void DoIfNotNull(this DateTime? pDateTime, Action pFunc) { if (pDateTime.HasValue) { pFunc(pDateTime.Value); } } public static DateTime GetInSameCalendarWeek(this DateTime pDateTime, DayOfWeek pDay) { DateTime lStart = pDateTime.GetLast(DayOfWeek.Monday); while (lStart.DayOfWeek != pDay) { lStart = lStart.AddDays(1); } return lStart; } public static DateTime GetLast(this DateTime pDateTime, DayOfWeek pDay) { DateTime lResult = pDateTime; while (lResult.DayOfWeek != pDay) { lResult = lResult.Date.AddDays(-1); } return lResult; } public static DateTime GetLastFridayIfWeekend(this DateTime pDateTime) { DateTime lResult = pDateTime; while (lResult.DayOfWeek == DayOfWeek.Saturday || lResult.DayOfWeek == DayOfWeek.Sunday) { lResult = lResult.Date.AddMilliseconds(-1); } return lResult; } public static DateTime GetNext(this DateTime pDateTime, DayOfWeek pDay) { DateTime lResult = pDateTime; while (lResult.DayOfWeek != pDay) { lResult = lResult.Date.AddDays(1); } return lResult; } public static DateTime GetNextMondayIfWeekend(this DateTime pDateTime) { DateTime lResult = pDateTime; while (lResult.DayOfWeek == DayOfWeek.Saturday || lResult.DayOfWeek == DayOfWeek.Sunday) { lResult = lResult.Date.AddDays(1); } return lResult; } public static int GetWeekdayOccurence(this DateTime pDateTime, bool pForward) { if (pForward) { return (int)Math.Ceiling(pDateTime.Day / 7d); } return (int)Math.Ceiling((DateTime.DaysInMonth(pDateTime.Year, pDateTime.Month) - pDateTime.Day) / 7d); } public static bool InBetween(this DateTime pDateTime, DateTime pSpanStart, DateTime pSpanStop, bool ignoreTimeAndCompareWholeDays) { DateTime stop = pSpanStop; if (ignoreTimeAndCompareWholeDays) { if (stop < DateTime.MaxValue.Date) { try { stop = stop.Date.AddDays(1); } catch (Exception) { stop = pSpanStop; } } return pDateTime >= pSpanStart && pDateTime < stop; } return pDateTime >= pSpanStart && pDateTime <= stop; } public static bool InBetween(this DateTime pDateTime, DateTimeSpan pSpan, bool ignoreTimeAndCompareWholeDays) { if (pSpan == null) { return false; } return pDateTime.InBetween(pSpan.StartDateTime, pSpan.EndDateTime, ignoreTimeAndCompareWholeDays); } public static string ToMonthSlashShortYear(this DateTime pDate) { return pDate.Month + "/" + pDate.Year.ToString().Substring(2); } public static string ToVeryShortDateString(this DateTime ths) { return ths.ToShortDateString().Remove(ths.ToShortDateString().Length - 4, 2); } public static string ToMyDateTimeString(this DateTime pDate) { var lInfo = DateTimeFormatInfo.CurrentInfo; if (pDate.Year < 2000) { return lInfo.GetAbbreviatedDayName(pDate.DayOfWeek) + ", " + $"{pDate.Day:00}" + "." + $"{pDate.Month:00}" + String.Format(".{0:yyyy}", pDate); } return lInfo.GetAbbreviatedDayName(pDate.DayOfWeek) + ", " + $"{pDate.Day:00}" + "." + $"{pDate.Month:00}" + "." + pDate.Year.ToString().Substring(2); } private static DateTime FromWeekdayOccurence(int pYear, int pMonth, DayOfWeek pDay, int pWeekdayOccurence, bool pFromStart) { if (pFromStart) { DateTime lResult = new DateTime(pYear, pMonth, 1); while (lResult.DayOfWeek != pDay) { lResult = lResult.AddDays(1); } return lResult.AddDays(7 * (pWeekdayOccurence - 1)); } else { DateTime lResult = new DateTime(pYear, pMonth, DateTime.DaysInMonth(pYear, pMonth)); while (lResult.DayOfWeek != pDay) { lResult = lResult.AddDays(-1); } return lResult.AddDays(-7 * (pWeekdayOccurence - 1)); } } public static WeekOfMonth GetWeekOfMonth(this DateTime pDateTime, DayOfWeek WeekStartingOn) { var x = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(pDateTime, CalendarWeekRule.FirstFourDayWeek, WeekStartingOn); var y = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(pDateTime.AddDays(1 - pDateTime.Day), CalendarWeekRule.FirstFourDayWeek, WeekStartingOn); var r = x - y + 1; switch (r) { case 1: return WeekOfMonth.First; case 2: return WeekOfMonth.Second; case 3: return WeekOfMonth.Third; case 4: return WeekOfMonth.Fourth; default: return WeekOfMonth.None; } } public static DateTime GetXthLastOccurenceInMonth(this DateTime pDateTime, DayOfWeek pDayOfWeek, int x) { var initialDate = new DateTime(pDateTime.Year, pDateTime.Month, DateTime.DaysInMonth(pDateTime.Year, pDateTime.Month)); var lastOccurence = initialDate; for (var currentDate = initialDate; currentDate >= new DateTime(pDateTime.Year, pDateTime.Month, 1); currentDate = currentDate.AddDays(-1)) { if (!currentDate.DayOfWeek.Equals(pDayOfWeek)) continue; lastOccurence = currentDate; break; } return lastOccurence.AddDays(-1 * 7 * x); } public static int GetOccurenceNumber(this DateTime pDateTime) { var initialDate = new DateTime(pDateTime.Year, pDateTime.Month, DateTime.DaysInMonth(pDateTime.Year, pDateTime.Month)); var lastOccurence = initialDate; var dayOfWeek = pDateTime.DayOfWeek; for (var currentDate = initialDate; currentDate >= new DateTime(pDateTime.Year, pDateTime.Month, 1); currentDate = currentDate.AddDays(-1)) { if (!currentDate.DayOfWeek.Equals(dayOfWeek)) continue; lastOccurence = currentDate; break; } return Convert.ToInt32((lastOccurence - pDateTime).TotalDays / 7); } public static int GetIso8601WeekOfYear(this DateTime time) { var day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time); if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday) { time = time.AddDays(3); } return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); } public static DateTime FirstDateOfWeek(this DateTime date, int weekOfYear) { var jan1 = new DateTime(date.Year, 1, 1); var daysOffset = DayOfWeek.Thursday - jan1.DayOfWeek; var firstThursday = jan1.AddDays(daysOffset); var cal = CultureInfo.CurrentCulture.Calendar; var firstWeek = cal.GetWeekOfYear(firstThursday, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); var weekNum = weekOfYear; if (firstWeek <= 1) { weekNum -= 1; } var result = firstThursday.AddDays(weekNum * 7); return result.AddDays(-3); } public static bool CompareShortDates(this DateTime date, DateTime pDate) { return date.Year == pDate.Year && date.Month == pDate.Month && date.Day == pDate.Day; } public static double GetIntersectingMinutes(this DateTime start1, DateTime end1, DateTime start2, DateTime end2) { var e = end1 <= end2 ? end1 : end2; var s = start1 >= start2 ? start1 : start2; return s > e ? 0 : (e - s).TotalMinutes; } public static DateTime GetNextDayOfWeek(this DateTime start, DayOfWeek WeekDay) { var result = start; while (!result.DayOfWeek.Equals(WeekDay)) { result = result.AddDays(1); } return result; } public static DateTime GetShortDateTime(this DateTime date) { return new DateTime(date.Year, date.Month, date.Day); } public static int GetDayNumberBetweenTwoDates(this DateTime anteriorDate, DateTime posteriorDate) { var value = 0; var shortAnteriorDate = new DateTime(anteriorDate.Year, anteriorDate.Month, anteriorDate.Day); var shortPosteriorDate = new DateTime(posteriorDate.Year, posteriorDate.Month, posteriorDate.Day); var tmpDate = shortAnteriorDate; while (tmpDate < shortPosteriorDate) { tmpDate = tmpDate.AddDays(1); value++; } return value; } /// /// Erzeugt aus zwei Datumsobjekten ein neues mit den Datumsangaben des ersten und den Zeitangaben des zweiten Parameters. /// /// Datum dessen Datumswerte übernommen werden. /// Datum dessen Zeitwerte übernommen werden. /// Gibt ein neues Objekt der Klasse Datum mit dem Datum des ersten Parameters und der Zeit des zweiten Parameters zurück. public static DateTime MergeDatesByDate(this DateTime first, DateTime second) { return new DateTime(first.Year, first.Month, first.Day, second.Hour, second.Minute, second.Second); } /// /// Erzeugt aus zwei Datumsobjekten ein neues mit den Datumsangaben des zweiten und den Zeitangaben des ersten Parameters. /// /// Datum dessen Zeitwerte übernommen werden. /// Datum dessen Datumswerte übernommen werden. /// Gibt ein neues Objekt der Klasse Datum mit dem Datum des zweiten Parameters und der Zeit des ersten Parameters zurück. public static DateTime MergeDatesByDateReversed(this DateTime first, DateTime second) { return new DateTime(second.Year, second.Month, second.Day, first.Hour, first.Minute, first.Second); } public static DateTime MergeDateAndTimeSpan(this DateTime pDate, TimeSpan pTimeSpan) { return new DateTime(pDate.Year, pDate.Month, pDate.Day, pTimeSpan.Hours, pTimeSpan.Minutes, pTimeSpan.Seconds); } public static WeekDays GetSchedulerWeekDays(this DateTime pDateTime) { return (WeekDays)(int)Math.Pow(2, (double)pDateTime.DayOfWeek); } public static WeekOfMonth GetWeekOfMonthForRecurrenceInfo(this DateTime pDate) { pDate = pDate.Date; var firstMonthDay = new DateTime(pDate.Year, pDate.Month, 1); var firstMonthMonday = firstMonthDay.AddDays((DayOfWeek.Monday + 7 - firstMonthDay.DayOfWeek) % 7); if (firstMonthMonday > pDate) { firstMonthDay = firstMonthDay.AddMonths(-1); firstMonthMonday = firstMonthDay.AddDays((DayOfWeek.Monday + 7 - firstMonthDay.DayOfWeek) % 7); } var weekNumber = (pDate - firstMonthMonday).Days / 7 + 1; switch (weekNumber) { case 1: return WeekOfMonth.First; case 2: return WeekOfMonth.Second; case 3: return WeekOfMonth.Third; case 4: return WeekOfMonth.Fourth; default: return WeekOfMonth.None; } } /// /// Prüft, ob sich ein Interval aus start1 und end1 innerhalb vom zweiten Interval, das aus start2 und end2 besteht, befindet oder sich mit diesem deckt. /// /// Beginn des ersten Intervalls. /// Ende des ersten Intervalls. /// Beginn des zweiten Intervalls. /// Ende des zweiten Intervalls. /// Ob der das erste Intervall im zweiten liegt public static bool AreInBetweenDates(this DateTime start1, DateTime end1, DateTime start2, DateTime end2) { return start1 >= start2 && end1 <= end2; } public static bool IsIntervalOverlappingWithAbsenceTimeInterval(this DateTime start, DateTime end, DateTime absenceTimeStart, DateTime? absenceTimeEnd) { if (absenceTimeEnd == null) { return start >= absenceTimeStart && end >= absenceTimeStart; } var start2 = absenceTimeStart; var end2 = absenceTimeEnd.Value; return !(start >= start2 && start >= end2 || end <= start2 && end <= end2); } public static bool IsTimeZero(this DateTime dateTime) { return dateTime.Hour == 0 && dateTime.Minute == 0 && dateTime.Second == 0; } public static string GetIntervalDescription(this DateTime start, DateTime? end) { var result = string.Empty; var isAllDay = start.IsTimeZero() && (end is null || end.Value.IsTimeZero()); var endTimeHasValue = end.HasValue; if(isAllDay) { if(endTimeHasValue) { if (start.Date.Equals(end.Value.Date)) { result += $" {start.ToShortDateString()}"; } else { result += $" {start.ToShortDateString()} - {end.Value.ToShortDateString()}"; } } else { result += $" Ab dem {start.ToShortDateString()}"; } } else { if(endTimeHasValue) { if(start.Date.Equals(end.Value.Date)) { result += $" {start:dd.MM.yyyy HH:mm} - {end.Value:HH:mm}"; } else { result += $" {start:dd.MM.yyyy HH:mm} - {(end.Value.IsTimeZero() ? end.Value.ToString("dd.MM.yyyy") : end.Value.ToString("dd.MM.yyyy HH:mm"))}"; } } else { result += $" Ab dem {start:dd.MM.yyyy HH:mm}"; } } return result; } public static bool IsInInterval(this DateTime start, DateTime end, DateTime start2, DateTime end2) { var interval1 = new TimeInterval(start, end); var interval2 = new TimeInterval(start2, end2); return interval1.IntersectsWithExcludingBounds(interval2); } public static DateTime GetFirstOfMonth(this DateTime dateTime) { return new DateTime(dateTime.Year, dateTime.Month, 1); } public static DateTime GetServiceRecordNoDateTimeDate(this DateTime date) { return date.MergeDateAndTimeSpan(new TimeSpan(0, 0, 1)); } public static DateTime? GetMin(this DateTime? d1, DateTime? d2) { if (d1 != null && d2 == null) return d1; if (d1 == null && d2 != null) return d2; if (d1 <= d2) return d1; return d2; } public static DateTime? GetMax(this DateTime? d1, DateTime? d2) { if (d1 != null && d2 == null) return d1; if (d1 == null && d2 != null) return d2; if (d1 >= d2) return d1; return d2; } public static DateTime GetLastOfMonth(this DateTime dateTime) { return dateTime.GetFirstOfMonth().AddMonths(1).AddDays(-1); } public static string GetDateString(this DateTime start, DateTime end) { return start.Date != end.Date ? $"{start.ToShortDateString()} {start.ToShortTimeString()} - {end.ToShortDateString()} {end.ToShortTimeString()}" : $"{start.ToShortDateString()} {start.ToShortTimeString()} - {end.ToShortTimeString()}"; } public static DateTime MergeDateWithHoursMinutesSeconds(this DateTime date, int hours, int minutes = 0, int seconds = 0) { var h = hours >= 0 && hours < 24 ? hours : 0; var m = minutes >= 0 && minutes < 60 ? minutes : 0; var s = seconds >= 0 && seconds < 60 ? seconds : 0; return date.MergeDatesByDate(new DateTime(2000, 1, 1, h, m, s)); } public static Dictionary GetTotalDay2TotalMinutesInRange(this DateTime start, DateTime end) { var result = new Dictionary(); var timeSpan = end - start; var dayCount = timeSpan.Days; for(var i = 0; i <= dayCount; i++) { var currentDate = i == 0 ? start : start.Date.AddDays(i); var currentEnd = currentDate.Date.AddHours(23).AddMinutes(59).AddSeconds(59); if(currentEnd > end) { currentEnd = end; } result.Add(start.Date.AddDays(i), (currentEnd - currentDate).TotalSeconds); } return result; } public static int GetWeekOfYearCount(this DateTime startDate, DateTime endDate) { var weeksOfYear = new List(); var dateTime = startDate; while(dateTime < endDate) { var previousWeek = dateTime.GetIso8601WeekOfYear(); dateTime = dateTime.AddDays(1); weeksOfYear.AddIfNotIn(previousWeek); } return weeksOfYear.Count; } public static int GetMonthCount(this DateTime startDate, DateTime endDate) { var monthCount = new List(); var dateTime = startDate; while(dateTime < endDate) { var previousMonth = dateTime.Month; dateTime = dateTime.AddDays(1); monthCount.AddIfNotIn(previousMonth); } return monthCount.Count; } public static int GetDaysInMonth(this DateTime dateTime) { return (dateTime.GetLastOfMonth() - dateTime.GetFirstOfMonth()).Days + 1; } /// /// Liefert einen String, der das Interval darstellt. /// Ist endDate null und die Uhrzeit vom Startdatum 00:00 oder sind beide Uhrzeiten 00:00, wird die Uhrzeit nicht angezeigt. /// /// Startdatum des Intervalls /// Eventuelles Enddatum des Intervalls /// public static string ToIntervalString(this DateTime dateTime, DateTime? endDate) { if(endDate is null) { if(dateTime.Hour == 0 && dateTime.Minute == 0) { return $"{dateTime:dd.MM.yyyy}"; } return $"{dateTime:dd.MM.yyyy HH:mm}"; } if(dateTime.Hour == 0 && dateTime.Minute == 0 && endDate.Value.Hour == 0 && endDate.Value.Minute == 0) { return dateTime.Year == endDate.Value.Year ? $"{dateTime:dd.MM.} - {endDate.Value:dd.MM.yyyy}" : $"{dateTime:dd.MM.yyyy} - {endDate.Value:dd.MM.yyyy}"; } return dateTime.Year == endDate.Value.Year ? $"{dateTime:dd.MM. HH:mm} - {endDate.Value:dd.MM.yyyy HH:mm}" : $"{dateTime:dd.MM.yyyy HH:mm} - {endDate.Value:dd.MM.yyyy HH:mm}"; } public static bool CompareTime(this DateTime date, DateTime date2) { return date.Hour == date2.Hour && date.Minute == date2.Minute && date.Second == date2.Second; } /// /// Fügt einem Datumsobjekt die Uhrzeit aus einem Uhrzeitenstring im Format HH:mm hinzu. Ist der Uhrzeitenstring ungültig, wird 00:00:00 Uhr genommen. /// Ist aber useSecond1IfNoValidTimeString true, wird 00:00:01 Uhr für ServiceRecords genommen, die ohne Uhrzeit gespeichert werden. /// /// Datum, dem der Uhrzeitteil hinzugefügt werden soll /// Die Zeichenkette mit dem Uhrzeitteil im Format HH:mm /// Flag, das bestimmt, ob die Sekunde bei fehlendem/ungültigem Uhrzeitteil auf 1 gesetzt werden soll. /// Das Datumsobjekt mit Uhrzeitteil. public static DateTime MergeDateAndTimeString(this DateTime date, string timeString, bool useSecond1IfNoValidTimeString = false) { if(Regex.IsMatch(timeString, _TimePattern)) { var hour2Minute = timeString.Split(':'); var hour = int.Parse(hour2Minute[0]); var minute = int.Parse(hour2Minute[1]); date = date.MergeDateWithHoursMinutesSeconds(hour, minute, 0); } else if(useSecond1IfNoValidTimeString) { date = date.MergeDateWithHoursMinutesSeconds(0, 0, 1); } return date; } public static DateTime GetDayOfWeek(DayOfWeek dayOfWeek) { var today = DateTime.Today; var diff = (7 + (today.DayOfWeek - dayOfWeek)) % 7; return today.AddDays(-diff); } } }