编程示例:公农历转换的算法

编程示例:公农历转换的算法

我国农历的编制与表示,在2017年,由中国科学院的南京的紫金山国家天文台发布了国家标准。
其中提到了编制规则。
第一以东八区的北京时间为准。
第二 朔日为农历月的第一个农历日
第三包含节气冬至在内的农历月为农历十一月。
第四若人某个农历十一月开始到下一个农历十一月(不含)之间有13个农历月,
则需要置闰月。置闰规则为取其中最先出现的一个不包含中气的农历月为农历闰月。
第五农历十一月之后第2个(不计闰月)农历月为农历年的起始月。

规则是既简明又准确的,就是不太好懂,我先解释一下,第一条定义了时间标准。
第二条定义了每个月的初一。第三条,俗称冬至规则,就是说,冬至那一天必须是在冬月,即农历十一月。
这一条至关重要,它是用公式推导其它农历月的基础。第四条包括了两个规则,(1)是是否置闰月的规则。
(2)是如何置闰月,这被叫做中气规则。这也是极为重要的一条规则。
第五条是定义了正月。

难点有两个,一是是否置闰月,它规定的年度期间为前一年的冬至到本年的冬至。然后看这个期间有多少个
朔日,也就是初一的数量,如果有十一个,没有闰月,如果是十二个,就是要闰月了。最佳的例子是2033年,
在公历的期间内也就是元旦到12月31日,它是有闰月的,闰11月。如果按照前一年的冬至到这一年的冬至期间
来看,它是没有闰月的。
二是置闰月的中气规则。还是举一个例子。以1987年为例,1987年8月24日为处暑,是农历七月初一,闰六月
如果是8月23日为处暑,则8月23日为农历七月的最后一天,8月24日是农历闰七月初一。
因为23日为处暑,它后面一个月之内缺中气秋分,所以闰七月。
因为24日为处暑,它前面一个月之内缺中气处暑,所以闰六月。

解释一下,什么是中气,24个节气中,出现在一个月的下旬的节气,称为中气,出现在一个月的上旬的节气,
称为节令。所以有12个节令,12个中气,节令和中气交替出现。它们合称24个节气。

下面要实现的功能是给出一个公历的年月日,返回一个农历的月和日。
javascript版本的api函数如下的调用方式
//输入阳历 2020 11 28 输出阴历 10月14日
 sun_to_moon(yyyy,mm,dd)

程序实现的源代码如下
//以1900年0月31日为起点,计算节气的积日
function accumute_day_for_year_term(year,xterm)
{
    var y=year-1900;
   return Math.floor(365.242*y+6.2+15.22*xterm-1.9*Math.sin(0.262*xterm));
}

以1900年0月31日为起点,计算某年元旦时的积日
function accumute_day_for_year_begin_date(year)
{ var d1=0;
    var y=year-1900;
    var ym4=y%4;
    var yd4=(y-ym4)/4;
    if(ym4==0)
    {d1=1461*yd4-1;}
    else
    {d1=1461*yd4+365*ym4;}
    return d1;
}

//求该积日的之前的最近的朔日的积日
function all_dark_day_for_coldest(d)
{
    var m=Math.floor(d/29.5306);
    var M_day=Math.floor(1.6+29.5306*m+0.4*Math.sin(1-0.45058*m));
    return M_day;
}

//判断是否是公历闰年,闰年为1,平年为0
function is_leap_year(years)
{
     if((years&3)!=0)
        {return 0;}
     else
         {if (years%100!=0)
            {return 1;}
           else
            {
                  if (year%400!=0)
                         {return 0;}
                  else
                        {return 1;}
            }              
        }
}

//判断是否是农历的有闰月年 ,有闰月为1,否则为0
function is_have_leap_month_of_year(year)
{
    var d0=accumute_day_for_year_term(year-1,23);
    var m0=Math.floor(d0/29.5306);
    var d=accumute_day_for_year_term(year,12*2-1);
    var m=m0+12+1;
   var str=all_dark_day_for_coldest(d);
   var   M_day=Math.floor(1.6+29.5306*m+0.4*Math.sin(1-0.45058*m));
   if(M_day>d)
   {
        var d1=accumute_day_for_year_term(year,1*2-1);
        var str1=all_dark_day_for_coldest(d1);
        if(str1>d1)
          {return 1;}
        else
          {return 0;}
   }
   else {return 1;}
}

//判断一个阴历月份范围内有没有中气,中气是冬至,大寒等位于下半月的节气。
// 结果为没有0,有中气返回1,中气在朔日结果是2。闰月之后的情况为3
function is_have_xterm_of_month(year,i)
{
   var d0=accumute_day_for_year_term(year-1,23);
   var m0=Math.floor(d0/29.5306);
   var d=accumute_day_for_year_term(year,i*2-1);
   var  m=m0+i+1;
   var  str=all_dark_day_for_coldest(d);
   var  M_day=Math.floor(1.6+29.5306*m+0.4*Math.sin(1-0.45058*m));
    if(str<d&&d<M_day) {return 1;}
    else if(str==d&&d<=M_day){return 2;}
    else if(str>d&&M_day>d){return 0;}
    else if(str<d&&str==M_day) {return 3;}
}

//在元旦作为起点 日期在年度中的天数  month 范围是1~12  日子范围是1——31。
function days_of_year_for_january(year,month,day)
{   var result=0;
    var monthadd=[0,31,59,90,120,151,181,212,243,273,304,334];
    var temp=is_leap_year(year);
    if(month==4||month==6||month==9||month==11)
    {if(day>30||day<1)
        {result="错误:日期超过范围";}
    }
    else if(month==2)
    {if(day>(28+temp)||day<1)
        {result="错误:日期超过范围";}
    }
    else
    {
        if(day>31||day<1)
        {result="错误:日期超过范围";}
    }    

    if((month==1||month==2)&&result==0)
        {
            result=monthadd[month-1]+day;
        }
    else if((month>2&&month<13)&&result==0)
        {
            result=monthadd[month-1]+day+temp;
        }
    else
    {
        if(month<1||month>12)
        {result+="错误:月份超过范围";}
    }    
    return  result;
}
//以3月1日作为起点 日期在年度中的天数
function days_of_year_for_march(month,day)
{
 var result=0;
    var monthadd=[306,337,0,31,61,92,122,153,184,214,245,275];
    
    if(month==4||month==6||month==9||month==11)
    {if(day>30||day<1)
        {result="错误:日期超过范围";}
    }
    else if(month==2)
    {if(day>29||day<1)
        {result="错误:日期超过范围";}
    }
    else
    {
        if(day>31||day<1)
        {result="错误:日期超过范围";}
    }    

   if((month>0&&month<13)&&result==0)
        {
            result=monthadd[month-1]+day;
        }
    else
    {
        if(month<1||month>12)
        {result+="错误:月份超过范围";}
    }    
    return  result;     
}

以1700年2月28日为起点,计算某年3月1日时的积日
function accumute_day_for_march(year)
{ var d1=0;
    var y=year-1700;
    var ym4=y%4;
    var yd4=(y-ym4)/4;
    if(ym4==0)
    {d1=1461*yd4;}
    else
    {d1=1461*yd4+365*ym4;}
    return d1;
}

//0为星期天,1-6为星期一到星期六
function day_of_week(y,m,d)
{
    var yy=y;
    if(m==1||m==2)
    {yy=y-1;}
  var acc= accumute_day_for_march(yy)+days_of_year_for_march(m,d);
  return (acc+5)%7;
}

//输入阳历 2020 11 28 输出阴历 10月14日
function sun_to_moon(yyyy,mm,dd)
{
var year=yyyy;
var month=mm;
var day=dd;

 var d0=accumute_day_for_year_term(year-1,23);
 var m0=Math.floor(d0/29.5306);
 var result=[];
 var result_xterm=[];
 var result_month=0;
 var result_day=0;
 
 var d=accumute_day_for_year_term(year,1*2-1);
 var m=m0+1+1;
  var str=all_dark_day_for_coldest(d);
 var   M_day=Math.floor(1.6+29.5306*m+0.4*Math.sin(1-0.45058*m));
 var xterm=is_have_xterm_of_month(year,1);
 result_xterm.push(xterm);
 //result.push(str+" "+d+" "+M_day+" 腊月 "+xterm+String.fromCharCode(13)+String.fromCharCode(10));
  var current=accumute_day_for_year_begin_date(year)+days_of_year_for_january(year,month,day);
 if(str<=current&&current<=M_day)
  {result_month=12;
    result_day=current-str+1;
  }
 
 for(var i=2;i<13;i++)
 {var message='';
  var y_month=i-2;
  d=accumute_day_for_year_term(year,i*2-1);
  m=m0+i+1;
   str=all_dark_day_for_coldest(d);
    M_day=Math.floor(1.6+29.5306*m+0.4*Math.sin(1-0.45058*m));
    var xterm=is_have_xterm_of_month(year,i);
    
    if(result_xterm[result_xterm.length-1]==1&&xterm==2)
    {message="闰"+y_month+"月来了"
      m=m0+i;
      M_day=Math.floor(1.6+29.5306*m+0.4*Math.sin(1-0.45058*m));
      if(M_day<=current&&current<str)
      {result_month="闰"+y_month;
       result_day=current-M_day+1;
      }
      
       var ii=i-1;
        m=m0+i+2;
        M_day=Math.floor(1.6+29.5306*m+0.4*Math.sin(1-0.45058*m));
      if(str<=current&&current<M_day)
      {result_month=ii;
       result_day=current-str+1;
      }
    }
    else if(xterm==3)
    {  var ii=i-1;
        m=m0+i+2;
        M_day=Math.floor(1.6+29.5306*m+0.4*Math.sin(1-0.45058*m));
      if(str<=current&&current<=M_day)
      {result_month=ii;
       result_day=current-str+1;
      }
    }
    else if(xterm==1)
    {var ii=i-1;
      if(str<=current&&current<=M_day)
      {result_month=ii;
       result_day=current-str+1;
      }
    }
    
 //result.push(str+" "+d+" "+M_day+" "+ii+"月 "+xterm+" "+message+String.fromCharCode(13)+String.fromCharCode(10));    
 }
 
  //document.getElementById("txt2").innerText=
  return result_month+"月"+result_day+"日";
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王伟1982

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值