```    // Version: Feb 2020
//
// The following C# program illustrates how easy it is to apply NexCalendar:
//
// Since software stores dates by its ordinal days from a special date,
// it is easy to convert any Gregorian date to NexCalendar date.
//
public static class NexCalendar
{
// Weekday = Day of the Week (1 = Monday ... 7 = Sunday, 8 = Sunday)
// Yearday = Ordinal Day of the Year (1 ... 365, 366 = Leap Sunday)
public const int MaxDaysInYear = 366;  // 366 is equal to the Leap Sunday
public const int DaysByWeek35 = 35 * 7 + 1;  // It is the annual long week with 8 days
= { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
= { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
private static bool InvalidInt(int x, int max) => (x < 1 || x > max);

public static int GetYeardayFromMonthDay(int month, int day)
{
if (InvalidInt(month, 12) || InvalidInt(day, DaysInMonths[month])) return 0;
return (DaysByMonths[month - 1] + day);
}
public static (int Month, int Day) GetMonthDay(int yearday)
{
if (InvalidInt(yearday, MaxDaysInYear)) return (0, 0);
int month = 2;
if (yearday > DaysByMonths) month = 6;
if (yearday > DaysByMonths) month = 10;
if (yearday > DaysByMonths[month]) month++; else month--;
if (yearday > DaysByMonths[month]) month++;
return (month, yearday - DaysByMonths[month - 1]);
}
public static int GetYeardayFromWeekDate(int week, int weekday)
{
if (week == 35 && weekday == 8) return DaysByWeek35;
if (week == 52 && weekday == 8) return MaxDaysInYear;
if (InvalidInt(week, 52) || InvalidInt(weekday, 7)) return 0;
int yearday = (week - 1) * 7 + weekday;
if (week > 35) yearday++;
return yearday;
}
public static (int Week, int Weekday) GetWeekDate(int yearday)
{
if (InvalidInt(yearday, MaxDaysInYear)) return (0, 0); // Unknown
if (yearday == MaxDaysInYear) return (52, 8); // The last week
if (yearday == DaysByWeek35) return (35, 8); // The last week
if (yearday >= DaysByWeek35) yearday--;
int week = (1 + (yearday - 1) / 7);
int weekday = yearday - ((week - 1) * 7);
return (week, weekday);
}
public static (int Week, int Weekday) GetWeekDateFromMonthDay(int month, int day)
=> GetWeekDate(GetYeardayFromMonthDay(month, day));
public static (int Month, int Day) GetMonthDayFromWeekDate(int week, int weekday)
=> GetMonthDay(GetYeardayFromWeekDate(week, weekday));
public static (string LongName, string ShortName) GetWeekdayName(int week, int weekday)
{
if ((week == 35 || week == 52) && weekday == 8) weekday = 7; // Special Sunday
if (InvalidInt(week, 52) || InvalidInt(weekday, 7)) return ("", "");
if (weekday == 7) weekday = 0; // Sunday is 0
var dateFormat = System.Globalization.CultureInfo.CurrentCulture.DateTimeFormat;
return (dateFormat.DayNames[weekday], dateFormat.AbbreviatedDayNames[weekday]);
}
public static DateTime NewDate(int year, int month = 1, int day = 1, int hour = 0,
int minute = 0, int second = 0, DateTimeKind kind = DateTimeKind.Unspecified)
{
if (!DateTime.IsLeapYear(year))
{
if (InvalidInt(month, 12)) throw new Exception("Invalid Month");
if (InvalidInt(day, DaysInMonths[month])) throw new Exception("Invalid Day");
if (month == 12 && day == 31) ;  // set the last day of the year
else if (month > 1 && day == DaysInMonths[month]) { month++; day = 1; }
else if (month > 2) day++;
}
return new DateTime(year, month, day, hour, minute, second, kind);
}
public static string ToString(DateTime date, string format)
{
var d = "@";
var diff = 0;
var day = date.Day;
var dayOfYear = date.DayOfYear;
var (week, weekday) = NexCalendar.GetWeekDate(dayOfYear);
var (longName, shortName) = NexCalendar.GetWeekdayName(week, weekday);
if (!DateTime.IsLeapYear(date.Year) && date.Month > 2)
{	// the previous day
diff = -1; day--;
if (day == 0) day = NexCalendar.DaysInMonths[(date.Month - 1)];
}
output = output.Replace(d + d + d + d, longName);
output = output.Replace(d + d + d, shortName);
output = output.Replace(d + d, day.ToString("D2"));
output = output.Replace(d, day.ToString());
return output;
}

//
// Three extended functions are provided as DateTime extensions.
// e.g.
// var (Year, Month, Day) = DateTime.Today.GetNexDate();
// Debug.WriteLine("Month/day: " + Month + "/" + Day);
// var (Yr, Week, Weekday) = DateTime.Today.GetNexWeekDate();
// Debug.WriteLine("Week Date: W" + Week + "-" + Weekday);
// var(LongName, ShortName) = DateTime.Today.GetNexWeekdayName();
// Debug.WriteLine("Today is " + LongName + " (" + ShortName + ")");
// Debug.WriteLine(DateTime.Today.ToString("d/M/yyyy dd ddd dddd"));
// Debug.WriteLine(NexCalendar.ToString(DateTime.Today, "d/M/yyyy dd ddd dddd"));
// var date = NexCalendar.NewDate(2021, 2, 29);
// Debug.WriteLine(NexCalendar.ToString(date, "d/M/yyyy dd ddd dddd"));

public static (int Year, int Month, int Day) GetNexDate(this DateTime date)
{
int year = date.Year; int month = date.Month; int day = date.Day;
if (DateTime.IsLeapYear(year) || month < 3) return (year, month, day);
if (day == 1) return (year, month - 1, DaysInMonths[month - 1]);
else return (year, month, day - 1);
}
public static (int Year, int Week, int Weekday) GetNexWeekDate(this DateTime date)
{
var (week, weekday) = NexCalendar.GetWeekDate(date.DayOfYear);
return (date.Year, week, weekday);
}
public static (string LongName, string ShortName) GetNexWeekdayName(this DateTime date)
{
var (week, weekday) = NexCalendar.GetWeekDate(date.DayOfYear);
return NexCalendar.GetWeekdayName(week, weekday);
}
}