// 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 public static readonly int[] DaysInMonths = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; public static readonly int[] DaysByMonths = { 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[4]) month = 6; if (yearday > DaysByMonths[8]) 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)]; } var output = date.AddDays(diff).ToString(format.Replace("d", d)); 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); } } // Copyright © 2020 Dr. Donny C.F. Lai & Justin J.S. Lai. All Rights Reserved.