using System; namespace MaksIT.Core.Extensions; public static class DateTimeExtensions { /// /// Adds workdays to a given date, skipping weekends and holidays defined in the holiday calendar. /// /// /// /// /// public static DateTime AddWorkdays(this DateTime date, int days, IHolidayCalendar holidayCalendar) { if (days == 0) return date; int direction = days > 0 ? 1 : -1; int absDays = Math.Abs(days); DateTime currentDate = date; while (absDays > 0) { // If the current date is a workday, decrement absDays if (currentDate.DayOfWeek != DayOfWeek.Saturday && currentDate.DayOfWeek != DayOfWeek.Sunday && !holidayCalendar.Contains(currentDate)) { absDays--; if (absDays == 0) break; } // Move to the next date currentDate = currentDate.AddDays(direction); } return currentDate; } /// /// Adds workdays to a given date, skipping weekends and holidays defined in the holiday calendar. /// /// /// /// /// public static DateTime AddWorkdays(this DateTime date, TimeSpan timeSpanWorkDays, IHolidayCalendar holidayCalendar) => date.AddWorkdays(timeSpanWorkDays.Days, holidayCalendar); /// /// Finds the next specified weekday from the given start date. /// /// /// /// public static DateTime NextWeekday(this DateTime start, DayOfWeek day) { int daysToAdd = ((int)day - (int)start.DayOfWeek + 7) % 7; daysToAdd = daysToAdd == 0 ? 7 : daysToAdd; // If today is the target day, move to next week return start.AddDays(daysToAdd); } /// /// Finds the TimeSpan to the next specified weekday from the given start date. /// /// /// /// public static TimeSpan ToNextWeekday(this DateTime start, DayOfWeek day) { int daysToAdd = ((int)day - (int)start.DayOfWeek + 7) % 7; daysToAdd = daysToAdd == 0 ? 7 : daysToAdd; return TimeSpan.FromDays(daysToAdd); } /// /// Finds the next end of month from the given date. /// /// /// public static DateTime NextEndOfMonth(this DateTime date) { var nextMonth = date.AddMonths(1); return new DateTime( nextMonth.Year, nextMonth.Month, DateTime.DaysInMonth(nextMonth.Year, nextMonth.Month), date.Hour, date.Minute, date.Second, date.Kind); } /// /// Finds the end of month for the given date. /// /// /// public static DateTime EndOfMonth(this DateTime date) => new DateTime( date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month), date.Hour, date.Minute, date.Second, date.Kind); /// /// Finds the begin of month for the given date. /// /// /// public static DateTime BeginOfMonth(this DateTime date) => new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind); /// /// Finds the start of year for the given date. /// /// /// public static DateTime StartOfYear(this DateTime date) => new DateTime(date.Year, 1, 1, date.Hour, date.Minute, date.Second, date.Kind); /// /// Finds the end of year for the given date. /// /// /// public static DateTime EndOfYear(this DateTime date) => new DateTime(date.Year, 12, 31, date.Hour, date.Minute, date.Second, date.Kind); /// /// Determines if the given date is the end of the month. /// /// /// public static bool IsEndOfMonth(this DateTime date) => date.Day == DateTime.DaysInMonth(date.Year, date.Month); /// /// Determines if the given date is the begin of the month. /// /// /// public static bool IsBeginOfMonth(this DateTime date) => date.Day == 1; /// /// Determines if the given date is the end of the year. /// /// /// public static bool IsEndOfYear(this DateTime date) => date.Day == 31 && date.Month == 12; /// /// Determines if the given date is the begin of the year. /// /// /// /// public static bool IsSameMonth(this DateTime date, DateTime targetDate) => date.Month == targetDate.Month && date.Year == targetDate.Year; /// /// Calculates the difference in complete years between two dates. /// Implementation follows the logic used in Excel's DATEDIF function /// "COMPLETE calendar years in between dates" /// /// /// /// public static int GetDifferenceInYears(this DateTime startDate, DateTime endDate) { int years = endDate.Year - startDate.Year; if (endDate.Month < startDate.Month || (endDate.Month == startDate.Month && endDate.Day < startDate.Day)) { years--; } return years; } } public interface IHolidayCalendar { bool Contains(DateTime date); }