UE4 C++ 農曆的算法

農曆是定歷,它具有天文年曆的特性,能很好地和各種天象對應,如它的節氣嚴格對應太陽高度,歷日較嚴格地對應月相,閏月的不發生頻率和發生頻率對應地球近日點和遠日點,其它天象如日出日沒, 晨昏蒙影,五星方位,日月食,潮汐等,就連歷月也大致對應太陽高度。所以農曆的計算更爲繁瑣一點,而且農曆歷月的天數只有29日和30日兩種。
代碼展示:

.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "MyPawn.generated.h"

UCLASS()
class TEST2_API AMyPawn : public APawn
{
	GENERATED_BODY()

public:
	// Sets default values for this pawn's properties
	AMyPawn();

public:	

	// 是否存在農曆的閏月,並返回閏月,沒有閏月返回零
	UFUNCTION(BlueprintCallable)
	int32 GetLeapMonth(int32 LunarYear);

	// 返回閏月的天數 : 2020年閏四月29天
	UFUNCTION(BlueprintCallable)
	int32 GetLeapMonthDays(int32 LunarYear);

	// 農曆當年月天數
	UFUNCTION(BlueprintCallable)
	int32 GetLunarCurentMonthDays(int32 LunarYear, int32  LunarMonth);

	// 公曆轉農曆函數
	UFUNCTION(BlueprintCallable)
	int32 SolarToLunar(int32 year, int32 month, int32 day);

	// 獲取公曆的當月天數
	UFUNCTION(BlueprintCallable)
	int32 SloarMonthDays(int32 year, int32 month);

	// 從公曆從1900.1.31(農曆1900.1.1)到今天的總天數
	UFUNCTION(BlueprintCallable)
	int32 GetSolarTotalDays(int32 year, int32 month, int32 day);

	// 獲取今年已經過去多少天了
	UFUNCTION(BlueprintCallable)
	int32 GetThisYearSoFarDays(const int32 Year, const int32 Month, const int32 Day);

	// 獲取農曆當年總天數
	UFUNCTION(BlueprintCallable)
	int32 GetLunarYearTotalDays(int32 Year);

	// 是否是閏年函數
	UFUNCTION(BlueprintCallable)
	bool GetLeapYear(int32 Year);

private:

	unsigned long int LunarInfo[151] =
	{
		0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,//1900---1909
		0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,//1910
		0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,//1920
		0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,//1930
		0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,//1940
		0x06ca0,0x0b550,0x15355,0x04da0,0x0a5b0,0x14573,0x052b0,0x0a9a8,0x0e950,0x06aa0,//1950
		0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,//1960
		0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b6a0,0x195a6,//1970
		0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,//1980
		0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,//1990
		0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,//2004
		0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,//2010
		0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,//2028
		0x05aa0,0x076a3,0x096d0,0x04bd7,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,//2030
		0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0,//2040--2049
		0x055b0,
	};

	int32 m_uilunaryear;
	int32 m_uilunarmonth;
	int32 m_uilunarday;
};
// Fill out your copyright notice in the Description page of Project Settings.

#include "MyPawn.h"


// Sets default values
AMyPawn::AMyPawn()
{
 	// Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	 m_uilunaryear = 0;
	 m_uilunarmonth = 0;
	 m_uilunarday = 0;

}

int32 AMyPawn::GetLeapMonth(int32 LunarYear)
{
	// 0x 是十六進制的前導符, f 表示 16進制的值 15 ,0xf 就是表示十六進制的f
	return LunarInfo[LunarYear - 1900] & 0xf;
}

int32 AMyPawn::GetLeapMonthDays(int32 LunarYear)
{
	if (GetLeapMonth(LunarYear))
		// 0x10000:0x是十六進制的前導符,所以這是一個十六進制數,數值爲10000。轉換成十進制數 :1 x 164 + 0 x 163+ 0 x 162 + 0 x 161 + 0 x 16 0 = 65536
		return(((LunarInfo[LunarYear - 1900]) & 0x10000) ? 30 : 29);
	else
		return (0);
}

int32 AMyPawn::GetLunarCurentMonthDays(int32 LunarYear, int32 LunarMonth)
{
	// ">>" 1. 用到輸出語句cout時會用到這個,表示輸出。2. 與變量在一起的時候表示左移操作,相當於乘以2。
	return((LunarInfo[LunarYear - 1900] & (0x10000 >> LunarMonth)) ? 30 : 29);
}

int32 AMyPawn::SolarToLunar(int32 year, int32 month, int32 day)
{
	int32 totalday = 0; // 記錄農曆1900.1.1日到今天相隔的天數
	int32 runyueflag = 0; //標記是否有閏月
	int32 LeapMonth = 0;
	int32 yearflag = 0;

	// 判斷農曆的時間在 1900 - 2049 年區間
	if (year < 1901 || year > 2049 || month > 12 || month == 0 || (year == 1900 && month == 1)) return 0;

	// SloarMonthDays 獲取這個月的天數(公曆)
	if (day > SloarMonthDays(year, month) || day == 0)
		return 0;
	//計算1900.1.1 到  輸入年月的天數
	totalday = GetSolarTotalDays(year, month, day);
	m_uilunaryear = 1900;

	while (totalday > 385) // 385 大於一年 剩餘一年用於條件計算
	{
		totalday -= GetLunarYearTotalDays(m_uilunaryear); 
		m_uilunaryear++;
	}
	if (totalday > GetLunarYearTotalDays(m_uilunaryear))  // 排除m_uilunaryear有閏月的情況
	{
		totalday -= GetLunarYearTotalDays(m_uilunaryear);
		m_uilunaryear++;
	}
	LeapMonth = GetLeapMonth(m_uilunaryear);  // 當前閏哪個月
	if (LeapMonth)
		runyueflag = 1; // 有閏月則一年爲13個月
	else
		runyueflag = 0;  // 沒閏月則一年爲12個月

	if (totalday == 0)   // 剛好一年
	{
		m_uilunarday = GetLunarCurentMonthDays(m_uilunaryear, 12);
		m_uilunarmonth = 12;
	}
	else
	{
		m_uilunarmonth = 1;
		while (m_uilunarmonth <= 12)
		{
			if (totalday > GetLunarCurentMonthDays(m_uilunaryear, m_uilunarmonth))
			{
				totalday = totalday - GetLunarCurentMonthDays(m_uilunaryear, m_uilunarmonth);  // 該年該月天數
				if (runyueflag == 1 && m_uilunarmonth == LeapMonth )  // 閏月處理
				{
					if (totalday > GetLeapMonthDays(m_uilunaryear))
					{
						totalday -= GetLeapMonthDays(m_uilunaryear);  // 該年閏月天數
					}
					else
					{
						m_uilunarday = totalday;
						break;
					}
					runyueflag = 0;
				}
				m_uilunarmonth++;
			}
			else
			{
				m_uilunarday = totalday;
				break;
			}
		}
	}
	FString RiQi = FString::FromInt(m_uilunaryear) + "-" + FString::FromInt(m_uilunarmonth) + "-" + FString::FromInt(m_uilunarday);
	GEngine->AddOnScreenDebugMessage(-1, 1000.f, FColor::Red, RiQi);
	return m_uilunarday;
}

int32 AMyPawn::SloarMonthDays(int32 year, int32 month)
{
	if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)
	{
		return 31;
	}
	if (month == 4 || month == 6 || month == 9 || month == 11)
	{
		return 30;
	}
	if (month == 2)
	{
		if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
		{
			return 29;
		}
		return 28;
	}
	return 0;
}

int32 AMyPawn::GetSolarTotalDays(int32 year, int32 month, int32 day)
{
	int32 NumberOfDays = 0;
	int32 YearBegin = 1900;
	int32 MonthBegin = 1;
	int32 DayBegin = 31;

	if (YearBegin == year)
	{
		return GetThisYearSoFarDays(year, month, day) - GetThisYearSoFarDays(YearBegin, MonthBegin, DayBegin - 1);
	}

	if (YearBegin < year)
	{
		for (int32 i = YearBegin; i < year; i++)
		{
			NumberOfDays = NumberOfDays + 365 + (i % 4 == 0 && i % 100 != 0 || i % 400 == 0);
		}

		NumberOfDays = NumberOfDays - GetThisYearSoFarDays(YearBegin, MonthBegin, DayBegin - 1) + GetThisYearSoFarDays(year, month, day);
	}

	return NumberOfDays;
}

int32 AMyPawn::GetThisYearSoFarDays(const int32 Year, const int32 Month, const int32 Day)
{
	int32 InDay = Day;

	for (int32 i = 1; i < Month; i++)
	{
		InDay +=  SloarMonthDays(Year, i);
	}

	return InDay;
}

int32 AMyPawn::GetLunarYearTotalDays(int32 Year)
{
	int Day = 0;
	for (int32 i = 1; i < 13; i++)
	{
		Day += GetLunarCurentMonthDays(Year, i);
	}
	if (GetLeapMonth(Year))
	{
		Day += GetLeapMonthDays(Year);
	}

	return Day;
}

bool AMyPawn::GetLeapYear(int32 Year)
{
	if ((Year % 4 == 0 && Year % 100 != 0) || Year % 400 == 0)
	{
		return true;
	}
	return false;
}

這裏用的是UE4 C++編寫,僅供參考!!!!

思路:

  1. 通過 LunarInfo 數組的值 “0x04bd8”(注1)可獲得
    (1)農曆的每月天數
    (2)當年是否有閏月
    (3)閏月當月的天數
  2. 公曆轉換農曆,公曆 1900.1.31 === 農曆 1900.1.1
    通過當前日期獲取距離1900.1.31已經過去多少天,用算出過去天數減去過去幾年的天數,留下最後一年的天數進行計算。
  3. 用最後一年的天數,從第一月開始計算,最後得出今年的天數

注:
1980年的數據是: 0x095b0
二進制:0000 1001 0101 1011 0000
1-4: 表示當年有無閏年知,有的話,爲閏月的月份,沒有的話,爲0。
5-16:爲除了閏月外的正常月份是大月還道是小月,1爲30天,0爲29天。
注意:從1月到12月對應版的是第16位到第5位。
17-20:表示閏月是大月還是小月,僅當存在閏月的情況下有意義。

參考:
https://zhidao.baidu.com/question/133523583.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章