Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Algorithm to find the Gregorian date of the Chinese New Year of a certain Gregorian year

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);
            }
        }  
like image 901
Liz Livingston Avatar asked Jun 08 '15 21:06

Liz Livingston


People also ask

How do you calculate the Chinese New Year?

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).

How do you calculate Chinese lunar month?

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.

Is Gregorian a Chinese calendar?

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.

Why does the date of Chinese New Year change each year?

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.


1 Answers

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 );
}

.NET 6 Support:

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
like image 139
Dai Avatar answered Sep 27 '22 02:09

Dai