100 lines
3.6 KiB
C#
100 lines
3.6 KiB
C#
namespace TaxBaik.Web.Components.Admin.Shared;
|
|
|
|
public static class BusinessDayCalculator
|
|
{
|
|
private sealed record HolidayWindow(DateOnly Start, DateOnly End)
|
|
{
|
|
public IEnumerable<DateOnly> Dates()
|
|
{
|
|
for (var date = Start; date <= End; date = date.AddDays(1))
|
|
{
|
|
yield return date;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static readonly HolidayWindow[] HolidayWindows =
|
|
{
|
|
new(new DateOnly(2026, 1, 1), new DateOnly(2026, 1, 1)),
|
|
new(new DateOnly(2026, 2, 16), new DateOnly(2026, 2, 18)),
|
|
new(new DateOnly(2026, 3, 1), new DateOnly(2026, 3, 2)),
|
|
new(new DateOnly(2026, 5, 5), new DateOnly(2026, 5, 5)),
|
|
new(new DateOnly(2026, 6, 6), new DateOnly(2026, 6, 6)),
|
|
new(new DateOnly(2026, 8, 15), new DateOnly(2026, 8, 17)),
|
|
new(new DateOnly(2026, 9, 24), new DateOnly(2026, 9, 26)),
|
|
new(new DateOnly(2026, 10, 3), new DateOnly(2026, 10, 5)),
|
|
new(new DateOnly(2026, 10, 9), new DateOnly(2026, 10, 9)),
|
|
new(new DateOnly(2026, 12, 25), new DateOnly(2026, 12, 25)),
|
|
new(new DateOnly(2027, 1, 1), new DateOnly(2027, 1, 1)),
|
|
new(new DateOnly(2027, 2, 6), new DateOnly(2027, 2, 9)),
|
|
new(new DateOnly(2027, 3, 1), new DateOnly(2027, 3, 2)),
|
|
new(new DateOnly(2027, 5, 5), new DateOnly(2027, 5, 5)),
|
|
new(new DateOnly(2027, 5, 13), new DateOnly(2027, 5, 13)),
|
|
new(new DateOnly(2027, 6, 6), new DateOnly(2027, 6, 6)),
|
|
new(new DateOnly(2027, 8, 15), new DateOnly(2027, 8, 16)),
|
|
new(new DateOnly(2027, 9, 14), new DateOnly(2027, 9, 16)),
|
|
new(new DateOnly(2027, 10, 3), new DateOnly(2027, 10, 4)),
|
|
new(new DateOnly(2027, 10, 9), new DateOnly(2027, 10, 11)),
|
|
new(new DateOnly(2027, 12, 25), new DateOnly(2027, 12, 26))
|
|
};
|
|
|
|
private static readonly HashSet<DateOnly> HolidayDates = BuildHolidayDates();
|
|
|
|
public static DateOnly GetEffectiveDueDate(DateOnly dueDate)
|
|
{
|
|
var effectiveDate = dueDate;
|
|
while (!IsBusinessDay(effectiveDate))
|
|
{
|
|
effectiveDate = effectiveDate.AddDays(1);
|
|
}
|
|
|
|
return effectiveDate;
|
|
}
|
|
|
|
public static int GetDday(DateOnly dueDate, DateOnly? referenceDate = null)
|
|
{
|
|
var today = referenceDate ?? DateOnly.FromDateTime(DateTime.Today);
|
|
var effectiveDueDate = GetEffectiveDueDate(dueDate);
|
|
return effectiveDueDate.DayNumber - today.DayNumber;
|
|
}
|
|
|
|
public static bool IsBusinessDay(DateOnly date)
|
|
=> date.DayOfWeek is not DayOfWeek.Saturday and not DayOfWeek.Sunday
|
|
&& !HolidayDates.Contains(date);
|
|
|
|
private static HashSet<DateOnly> BuildHolidayDates()
|
|
{
|
|
var holidays = new HashSet<DateOnly>();
|
|
|
|
foreach (var window in HolidayWindows)
|
|
{
|
|
foreach (var date in window.Dates())
|
|
{
|
|
holidays.Add(date);
|
|
}
|
|
}
|
|
|
|
// 주말과 연속 공휴일 뒤에 붙는 대체휴일을 다음 영업일로 자동 확장한다.
|
|
foreach (var window in HolidayWindows)
|
|
{
|
|
foreach (var date in window.Dates())
|
|
{
|
|
if (date.DayOfWeek is not DayOfWeek.Saturday and not DayOfWeek.Sunday)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var substitute = date.AddDays(1);
|
|
while (substitute.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday || holidays.Contains(substitute))
|
|
{
|
|
substitute = substitute.AddDays(1);
|
|
}
|
|
|
|
holidays.Add(substitute);
|
|
}
|
|
}
|
|
|
|
return holidays;
|
|
}
|
|
}
|