I am making a driver to calculate various holidays in a given time span. So, I need to find the Gregorian dates of all the Chinese Holidays (Chinese New Year, QingMing Festival, Dragon Boat Festival, etc). I used the famous 'Easter algorithm' for Good Friday, Easter Monday, Ascension Day, and Whit Monday calculations; however, I don't understand it well enough to adapt it for the Chinese calendar.
I have found similar questions, but they often go from Gregorian to Chinese:
Moon / Lunar Phase Algorithm
Calculating lunar/lunisolar holidays in python
http://www.herongyang.com/year/program.html
http://www.hermetic.ch/cal_stud/ch_year.htm
The last link was exremely helpful but I'm still not sure how to implement that algorithm in a way that can help me. Any advice or code would be greatly appreciated!
Here is my Good Friday Algorithm:
private void GetGoodFridayOccurances(DateTime startDate, DateTime endDate, List<ObservedHoliday> observedHolidays, StandardHoliday holiday)
{
for (DateTime date = startDate; date <= endDate; date = date.AddYears(1))
{
#region Finding the Day of Easter Algorithm
int day, month;
int firstTwo = date.Year / 100;
int remainderMod = date.Year % 19;
int pfmDate = (firstTwo - 15) / 2 + 202 - 11 * remainderMod;
#region switches
switch (firstTwo)
{
case 21:
case 24:
case 25:
case 27:
case 28:
case 29:
case 30:
case 31:
case 32:
case 34:
case 35:
case 38:
pfmDate = pfmDate - 1;
break;
case 33:
case 36:
case 37:
case 39:
case 40:
pfmDate = pfmDate - 2;
break;
}
#endregion
pfmDate = pfmDate % 30;
int tA = pfmDate + 21;
if (pfmDate == 29)
tA = tA - 1;
if (pfmDate == 29 && remainderMod > 10)
tA = tA - 1;
//Find next sunday
int tB = (tA - 19) % 7;
int tC = (40 - firstTwo) % 4;
if (tC == 3 || tC > 1)
tC = tC + 1;
pfmDate = date.Year % 100;
int tD = (pfmDate + pfmDate / 4) % 7;
int tE = ((20 - tB - tC - tD) % 7) + 1;
day = tA + tE;
if (day > 31)
{
day = day - 31;
month = 4;
}
else
{
month = 3;
}
#endregion
DateTime observed = new DateTime(date.Year, month, day).AddDays(-2);
ObservedHoliday obsdate = new ObservedHoliday(holiday);
if (startDate == endDate && startDate.Day == observed.Day)
{
obsdate.DateObserved = observed;
observedHolidays.Add(obsdate);
}
else if (startDate != endDate && observed >= startDate)
{
obsdate.DateObserved = observed;
observedHolidays.Add(obsdate);
}
}
The Chinese calendar defines the lunar month containing the winter solstice as the eleventh month, meaning that Chinese New Year usually falls on the second new moon after the winter solstice (rarely the third if an intercalary month intervenes).
When the moon moves into a line with the earth and the sun, it comes the first day of the lunar month. When the moon is full, it comes the middle of the month. So there are 29 or 30 days in a month. There are 12 or 13 months in a lunar year.
Although modern-day China uses the Gregorian calendar, the traditional Chinese calendar governs holidays, such as the Chinese New Year and Lantern Festival, in both China and overseas Chinese communities.
Because it depends on the Moon, the date of Lunar New Year actually changes each year, but it will always fall some time between 21 January and 20 February. The festival starts on 1 February and lasts for up to 16 days. This year marks the change from the year of the Ox to the year of the Tiger.
For Chinese New Year, I think this would work:
using System;
using System.Globalization;
public static ( Int32 year, Int32 month, Int32 day ) GetDateOfChineseNewYear()
{
ChineseLunisolarCalendar chinese = new ChineseLunisolarCalendar();
GregorianCalendar gregorian = new GregorianCalendar();
DateTime utcNow = DateTime.UtcNow;
// Get Chinese New Year of current UTC date/time
DateTime chineseNewYear = chinese.ToDateTime( utcNow.Year, 1, 1, 0, 0, 0, 0 );
// Convert back to Gregorian (you could just query properties of `chineseNewYear` directly, but I prefer to use `GregorianCalendar` for consistency:
Int32 year = gregorian.GetYear( chineseNewYear );
Int32 month = gregorian.GetMonth( chineseNewYear );
Int32 day = gregorian.GetDayOfMonth( chineseNewYear );
return ( year, month, day );
}
Now that .NET 6 (finally) has the DateOnly
type (after we've been asking for it for 20 years now...), this works:
(Unfortunately .NET 6's Calendar
class hasn't yet been updated to support DateOnly
, but it's straightforward to handle manually):
private static readonly ChineseLunisolarCalendar _chineseCal = new ChineseLunisolarCalendar();
private static readonly GregorianCalendar _gregorianCal = new GregorianCalendar();
public static DateOnly GetGregorianDateOfChineseNewYear()
{
return GetGregorianDateOfChineseNewYear( DateTime.UtcNow.Year );
}
public static DateOnly GetGregorianDateOfChineseNewYear( Int32 gregorianYear )
{
// Get Chinese New Year of current UTC date/time
DateTime chineseNewYear = _chineseCal.ToDateTime( year: gregorianYear, month: 1, day: 1, /*hms:*/ 0, 0, 0, 0 );
// Convert back to Gregorian (you could just query properties of `chineseNewYear` directly, but I prefer to use `GregorianCalendar` for consistency:
Int32 year = _gregorianCal.GetYear( chineseNewYear );
Int32 month = _gregorianCal.GetMonth( chineseNewYear );
Int32 day = _gregorianCal.GetDayOfMonth( chineseNewYear );
return new DateOnly( year, month, day, _gregorianCal );
}
So running this...
Console.WriteLine( "Gregorian year: {0}, Chinese New Year: {1:ddd} {1}", 2021, GetGregorianDateOfChineseNewYear( 2021 ) );
Console.WriteLine();
Console.WriteLine( "Next 10 years:" );
for( Int32 i = 2022; i < 2030; i++ )
{
Console.WriteLine( "Gregorian year: {0}, Chinese New Year: {1:ddd} {1}", i, GetGregorianDateOfChineseNewYear( i ) );
}
...gives me this output:
Gregorian year: 2021, Chinese New Year: Sat 2021-02-12 Next 10 years: Gregorian year: 2022, Chinese New Year: Tue 2022-02-01 Gregorian year: 2023, Chinese New Year: Sun 2023-01-22 Gregorian year: 2024, Chinese New Year: Sat 2024-02-10 Gregorian year: 2025, Chinese New Year: Wed 2025-01-29 Gregorian year: 2026, Chinese New Year: Tue 2026-02-17 Gregorian year: 2027, Chinese New Year: Sat 2027-02-06 Gregorian year: 2028, Chinese New Year: Wed 2028-01-26 Gregorian year: 2029, Chinese New Year: Tue 2029-02-13 Gregorian year: 2030, Chinese New Year: Sun 2030-02-03 Gregorian year: 2031, Chinese New Year: Thu 2031-01-23
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With